Introduction
“Should we use microservices?” is one of the most common architecture questions I get asked. The answer is almost always “it depends,” but that’s not helpful. This article provides a concrete framework for making this decision.
The Honest Truth
Most teams that adopt microservices too early regret it. The complexity overhead is significant, and the benefits only materialize at certain scales.
I’ve seen:
- A 5-person startup with 20 microservices struggling to ship features
- A 200-person company with a well-structured monolith moving faster than competitors
The architecture should match your context, not your aspirations.
When Monoliths Win
Small to Medium Teams (< 50 engineers)
With a smaller team:
- Communication overhead is low
- Shared code ownership works
- Deployment coordination is manageable
- You can’t afford the operational overhead of distributed systems
Early-Stage Products
When you’re still figuring out:
- What features users want
- Where the domain boundaries are
- What the performance requirements are
A monolith lets you iterate faster and refactor easily.
Simple Domains
If your business logic is straightforward and doesn’t have natural boundaries, forcing microservices creates artificial complexity.
When Microservices Win
Large Teams (50+ engineers)
At scale:
- Teams need autonomy to move fast
- Deployment coordination becomes a bottleneck
- Different parts of the system have different scaling needs
Clear Domain Boundaries
When you have:
- Distinct business capabilities (payments, inventory, shipping)
- Different data ownership requirements
- Teams aligned to business domains
Different Technical Requirements
When parts of your system need:
- Different languages or frameworks
- Different scaling characteristics
- Different deployment frequencies
- Different reliability requirements
The Decision Framework
Score your situation on these factors:
Team Size and Structure
| Situation | Score |
|---|---|
| < 10 engineers, single team | Monolith (+3) |
| 10-30 engineers, 2-4 teams | Either (0) |
| 30-50 engineers, multiple teams | Lean microservices (+1) |
| 50+ engineers, many teams | Microservices (+3) |
Domain Complexity
| Situation | Score |
|---|---|
| Simple, unified domain | Monolith (+2) |
| Some distinct subdomains | Either (0) |
| Clear bounded contexts | Microservices (+2) |
Deployment Needs
| Situation | Score |
|---|---|
| Weekly releases are fine | Monolith (+2) |
| Daily releases needed | Either (0) |
| Multiple daily releases, independent | Microservices (+2) |
Scaling Requirements
| Situation | Score |
|---|---|
| Uniform load across features | Monolith (+1) |
| Some features need more scale | Either (0) |
| Vastly different scaling needs | Microservices (+2) |
Interpretation:
- Score > 3: Strong monolith candidate
- Score -2 to 3: Either works, consider team preference
- Score < -2: Strong microservices candidate
The Modular Monolith: Best of Both Worlds
Before jumping to microservices, consider a modular monolith:
src/
├── modules/
│ ├── users/
│ │ ├── api/
│ │ ├── domain/
│ │ └── infrastructure/
│ ├── orders/
│ │ ├── api/
│ │ ├── domain/
│ │ └── infrastructure/
│ └── payments/
│ ├── api/
│ ├── domain/
│ └── infrastructure/
└── shared/
└── kernel/
Benefits:
- Clear boundaries without network overhead
- Easy to extract to services later
- Single deployment, simpler operations
- Enforced module boundaries through code structure
Migration Path
If you start with a monolith and need to migrate:
1. Identify Extraction Candidates
Look for modules that:
- Have clear boundaries
- Need independent scaling
- Have different deployment needs
- Are owned by a specific team
2. Strangler Fig Pattern
Don’t rewrite—gradually extract:
- Put a facade in front of the monolith
- Extract one capability to a service
- Route traffic to the new service
- Repeat
3. Start with the Edges
Extract services that:
- Have fewer dependencies
- Are less critical (lower risk)
- Have clear interfaces
Common Mistakes
Premature Decomposition
Splitting before you understand the domain leads to wrong boundaries. It’s much harder to merge services than to split a monolith.
Distributed Monolith
If your services:
- Must be deployed together
- Share a database
- Have synchronous dependencies everywhere
You have a distributed monolith—all the complexity, none of the benefits.
Ignoring Operational Costs
Microservices require:
- Service discovery
- Distributed tracing
- Log aggregation
- Container orchestration
- More on-call complexity
Make sure you can afford this overhead.
Real-World Example
A company I advised had 30 engineers and 15 microservices. They were struggling with:
- Slow feature development (changes touched multiple services)
- Frequent integration issues
- Complex deployment coordination
- High operational overhead
We consolidated to 4 services aligned with team boundaries:
- Core platform (shared)
- Customer-facing product
- Internal tools
- Data pipeline
Result:
- 40% faster feature delivery
- 60% fewer production incidents
- Happier engineers
Conclusion
The microservices vs monolith debate isn’t about which is “better”—it’s about which fits your context.
Start with a well-structured monolith. Add clear module boundaries. Extract to services only when you have a concrete reason: team autonomy, independent scaling, or different technical requirements.
The best architecture is the one that lets your team ship value to users quickly and reliably. Sometimes that’s microservices. Often, it’s not.