When I talk to developers about microservices, one thing always stands out: everyone seems to define microservices slightly differently. From how small a service should be to how tightly they interact, the implementation varies widely. But one topic comes up frequently and often sparks debate — shared libraries. Should microservices share code, or does that undermine their independence?
Based on my experience, I believe that having a shared library can be a good idea — if you do it right. In this post, I’ll explore why shared libraries can be useful, where the risks are, and how to approach them responsibly.
What Do We Mean by a Shared Library?
A shared library is reusable code that multiple services can include as a dependency, typically managed using tools like Gradle or Maven in the Java ecosystem. These libraries usually provide utilities, technical abstractions, or common infrastructure integrations.
Yes, microservices promise language and framework freedom. But in small to medium-sized companies, that flexibility often becomes a burden. It's more efficient to standardize around a single tech stack — and in that environment, sharing well-abstracted code can offer real productivity benefits.
Think of frameworks like Spring Boot — we don’t write everything from scratch. In the same spirit, a shared library is your internal toolbox, tuned to your company’s needs.
Why Would You Want a Shared Library?
Here are some practical reasons shared libraries can be valuable:
- Boost Productivity: Avoid re-implementing the same boilerplate in every service.
- Standardize Best Practices: Enforce consistent logging, error handling, metrics, and more.
- Encapsulate Technical Complexity: Implement things like multi-tenancy, authentication, or service-to-service communication once and reuse them.
Some examples from real-world projects:
- Multi-tenant HTTP middleware
- Shared Spring Security configuration
- Multi-tenant MongoDB setup
- Kafka message header propagators
These aren’t business rules — they’re technical concerns best solved once and reused.
Where’s the Catch?
Of course, shared libraries are not a silver bullet. They come with trade-offs that you should handle with care:
1. Avoid Business Logic
Never include business rules or data models in shared libraries. That creates tight coupling between services and violates microservice independence.
Bad examples:
- Shared domain entities (JPA/Hibernate)
- Shared data access layers
- Shared decision logic
Services should own their own business logic and evolve independently.
2. Versioning Matters
Shared libraries should be versioned like any external dependency. Use semantic versioning, and assume services may not upgrade right away. This forces you to maintain backward compatibility and minimize breaking changes.
3. Ownership and Maintenance
Someone needs to own the library. Without clear responsibility, it can become outdated, bloated, or inconsistent. Ideally, a platform or infra team maintains it — or at least one team with a clear mandate and review process.
Combine Shared Library and the BOM
When using a shared library in your project alongside other external dependencies, managing dependency versions can become tricky. Inconsistent versions across microservices can lead to compatibility issues, especially as your codebase grows. Fortunately, frameworks like Spring Boot address this problem by providing a BOM (Bill of Materials), which predefines the version of each used dependency. This ensures that all dependencies are aligned and eliminates the risk of version mismatches.
It’s a good idea to apply the same concept to your shared library. By using a BOM for your internal dependencies, you can maintain consistent versions across all services that rely on it. When using Spring Boot, you can even inherit from its BOM and simply add your own dependencies as needed.
Simplified upgrades
One of the biggest advantages of using a BOM is the simplicity of upgrading project dependencies. In a system with multiple microservices, maintaining dependency versions independently in each service can be a significant burden. With a BOM, you can centralize version management, making upgrades much more straightforward. Instead of updating dependencies in each individual service, you only need to upgrade the version of your shared library in the BOM.
This means that when you need to update your shared library or any of its dependencies, you can apply that change across all services in a single step, rather than updating each service manually. This approach significantly reduces maintenance overhead and ensures that all services stay in sync with the latest versions of the shared libraries and dependencies.
Recommendations
✅ Use a BOM for version management
✅ Include technical utilities only (auth, logging, retries, etc.)
✅ Use semantic versioning and release notes
✅ Assign clear ownership and establish a contribution process
❌ Don’t share business logic or domain models
❌ Don’t force synchronous coupling through shared code
Treat Shared Libraries Like Third-Party Libraries
This is the best mindset. A shared library should feel like an external dependency. You use it because it solves a general technical problem well — not because someone dropped a bunch of code into a common repo.
Summary
Microservices are about independent, scalable, and loosely coupled components. But independence doesn’t mean isolation. Technical reuse — if done carefully — can boost productivity and consistency without violating the microservice philosophy.
Just remember: share the tools, not the rules.