When Microservices Become a Liability
Decide whether service boundaries create independent ownership and failure containment or merely add network calls, coordination, and operational load.
The Microservices Promise
Microservices can support independent deployments, team ownership, selective scaling and fault isolation. Those outcomes do not come from process count alone. They require boundaries that match business responsibility, explicit contracts, independent release paths, and teams that can operate what they own.
The decision is not “monolith or microservices” in the abstract. Ask which parts of the system need an independent change, scaling, security, compliance or recovery boundary. Keep the rest together until evidence makes separation worthwhile.
The Reality Check
Every remote boundary introduces partial failure, version compatibility, network latency, authorization, observability, deployment coordination and data-consistency decisions. A useful boundary earns that cost by containing change or failure. An unhelpful one preserves the coupling while making it harder to see.
"type": "svg-comparison", "title": "Microservices: Promise vs Reality", "headers": ["Promise", "Reality (Wrong Context)"], "rows": [ ] }} />
Signs You Adopted Microservices Too Early
1. Ownership Is Spread Too Thin
Each independently deployed service needs maintenance, monitoring, dependency upgrades, incident response and recovery knowledge. The problem is not a universal engineers-per-service ratio. It is an ownership map where critical services have no team able to change and operate them safely.
2. You're Deploying Services Together
If services A, B and C routinely require coordinated releases, inspect why. Shared database changes, lockstep client versions, synchronous call chains and cross-service transactions are evidence that the intended independence has not been achieved. A coordinated release can occasionally be appropriate, but it should not be the only safe path.
3. Most Time Goes to Plumbing
"type": "svg-metrics", "title": "Where Engineering Time Goes", "metrics": [{ "label": "Delivery wait", "value": "Measure", "description": "Time blocked on other services" }, ] }} />
The Path Forward
Option 1: The Modular Monolith
A modular monolith keeps one deployment boundary while enforcing ownership and dependency rules inside the codebase. Modules can own schemas or tables, expose explicit internal interfaces, and prohibit arbitrary cross-module imports. This keeps ordinary transactions and local calls simple while the product and team boundaries are still evolving.
It is not automatically easy. It still needs architecture tests, code ownership, migration discipline and production observability. Its advantage is that the organization can learn where stable boundaries exist before paying the full distributed-systems cost.
Option 2: Maxi-Services
Consolidate several tightly coupled services behind a smaller number of domain-aligned deployment units. The exact count is not a target. The aim is to let one accountable team change and restore a meaningful capability without coordinating with several other teams.
What Actually Matters
The goal was never microservices. The goal was: fast, safe deployments; team autonomy; production stability.
Measure those outcomes directly. Useful signals include the number of teams required for a change, services touched per feature, lead time from approved change to production, rollback scope, cross-service incident participation, dependency wait time, and the percentage of services with an accountable operating owner. Do not use service count as a maturity metric.
Measure the Coupling
Inspect the last ten meaningful changes. Count how many services, repositories, teams and deployment windows each change required. Review traces for synchronous call depth and failure amplification. Check whether a service can change its schema or interface without coordinating a fleet release.
A service boundary is useful when it contains a business capability, owns its data and can be changed and operated by one accountable team. A boundary created around a technical layer often moves ordinary function calls onto the network without creating real independence.
Read the signals together
No single measurement decides the architecture. A high number of services per feature may be reasonable for a rare cross-domain change. A short synchronous call chain may still be dangerous if every component must be available for the customer journey to succeed. Combine change history, runtime traces, incident participation and ownership evidence.
Use a four-week or ten-change sample and record:
| Signal | Question it answers | Warning pattern | |---|---|---| | Services touched per change | Does the boundary contain change? | Routine work spans the same cluster of services | | Teams required per release | Does the boundary create autonomy? | One feature waits on several planning queues | | Synchronous dependency depth | Is failure contained? | One user request depends on a long live chain | | Cross-service data changes | Is data ownership real? | Shared tables or coordinated schema releases are normal | | Restore and rollback scope | Can one team recover safely? | Recovery requires several teams and undocumented ordering | | On-call ownership | Is the service operable? | Alerts route to a shared queue with no accountable owner |
The purpose is to find the most expensive coupling, not to produce a target service count.
Simplify without Another Rewrite
Start with the most expensive boundary. Stop creating new dependencies on it, document the contract and move tightly coupled code or data toward one owner. Some services can merge behind the same deployment while keeping internal module boundaries. Others should remain separate but replace synchronous chains with a durable workflow.
Do not launch a “return to the monolith” rewrite. Use the same incremental discipline expected in a decomposition: compatibility, observability, staged traffic and rollback. The target may be a modular monolith, a smaller set of domain services or a mixed architecture.
For a merge, decide which service becomes the write authority. Add contract tests around existing consumers, move one behavior at a time, and reconcile data before retiring the old path. Keep routing reversible during the observation window. Preserve operational evidence so a lower service count does not hide a larger failure domain.
Know the consolidation tradeoffs
Combining services reduces network and coordination costs, but it can increase deployment and failure scope if internal boundaries are weak. Protect module ownership, dependency direction, data invariants and observability inside the consolidated unit. A modular monolith without enforced boundaries can become a tightly coupled codebase that is hard to change for different reasons.
Keeping services separate also has valid costs. It preserves independent scaling, security or release boundaries only when the organization can operate them. Do not merge a service that genuinely requires a distinct regulatory control, failure domain or workload profile merely to make the repository count smaller.
Reconsider Separation When Evidence Changes
Separate services become justified when teams need independent release ownership, workloads have materially different scaling or security requirements, or failure containment cannot be achieved inside the current boundary. Record that evidence. Architecture should follow the constraints the organization has now while preserving options for constraints it can reasonably expect.
Use a boundary decision record
For each proposed split or merge, record:
| Question | Evidence | |---|---| | What capability and data does the boundary own? | domain model, write authority, invariants | | Which change becomes independent? | recent change history and consumer contracts | | Which failure becomes contained? | failure-mode and recovery test | | What new failure is introduced? | network, queue, dependency, and consistency risks | | Who owns it in production? | team, on-call path, runbook, and service objective | | How will the move be reversed? | routing, data reconciliation, and rollback window |
Approve the boundary only when the independence or containment value is concrete enough to justify its operational cost. Revisit the record after representative changes and incidents. A boundary that was reasonable at one stage may become the next bottleneck as the product and organization change.
The next action
Take the last ten meaningful production changes and group the services that were repeatedly changed, released or restored together. Select the cluster with the highest coordination cost. Create one boundary decision record for it, then choose the smallest reversible experiment: consolidate one workflow, remove one synchronous dependency, assign one operating owner or prove that the current separation is still justified. Measure the next set of changes before expanding the programme.
Primary references: Martin Fowler on microservice prerequisites, AWS Prescriptive Guidance on decomposing monoliths, and Google SRE on handling overload. Apply the patterns to the actual domain, team topology, workload and recovery requirements rather than treating any reference architecture as a target state.