Microservices: when splitting the system makes it worse
Service boundaries, data ownership, network failure, deployment complexity, and when a modular monolith is the better engineering decision.
Microservices solved real problems for organisations with scale, team topology, and release pressure that genuinely required independent deployables. The pattern escaped that context. Now teams split systems because slides look modern, because hiring posts mention Kubernetes, or because a monolith became painful — without asking whether the pain was boundary discipline or boundary multiplication.
Splitting can make a system worse: slower to change, harder to debug, more fragile under partial failure, and more expensive to operate — while the original coupling reappears as synchronous chains, shared databases, and distributed transactions dressed as sagas nobody trusts.
Constrange is not anti-microservices. It is anti-splitting without a constraint match. When the bottleneck is unclear ownership inside one codebase, more repositories do not help. When the bottleneck is network and operations tax, a modular monolith may be the adult decision.
Boundaries that look clean and behave coupled
Service boundaries drawn around org charts or nouns often split entities that change together. Order and fulfilment. Account and billing. User and permission. Teams deploy independently on paper. In production they coordinate constantly because the business transaction spans both. The split added latency and failure modes without removing coupling.
| Split may help | Split may hurt |
|---|---|
| Different scale profiles | Same change cadence always |
| Regulatory isolation required | Shared transactional invariants |
| Team ownership aligns with capability | Team smaller than service count |
| Technology mismatch is real | Coupling is logical, not deployable |
| Clear async domain events | Synchronous request chains everywhere |
Data ownership without shared truth
Microservices demand each service owns its data. Programmes declare ownership on slides then share a database 'temporarily'. Temporary lasts years. Two services writing one table destroys the boundary more thoroughly than any monolith ever could — because now failures are non-deterministic and migrations require coordinated outages.
Splitting without data ownership is theatre. Either enforce separate stores and accept eventual consistency, or keep the transaction inside one deployable until you can. The worst outcome is distributed ownership of one schema.
- Shared database behind 'microservices'
- Cross-service queries through views nobody owns
- Foreign keys replaced by hope and nightly jobs
- Duplicate entity IDs reconciled manually
- Migration blocked because three services deploy separately
Network failure on a synchronous path
Monoliths fail as a unit. Microservices fail as chains. A user action that was one call becomes five. Each hop adds timeout risk, serialization cost, and version skew. Partial failure produces states the business never defined: paid but not fulfilled, updated in CRM but not in billing, authorised but not recorded.
Teams respond by adding orchestration, retries, compensations, and idempotency keys — the full distributed systems tax. Sometimes that tax is worth paying. Often it buys nothing because the business process was always atomic and the split was cosmetic.
- User-facing latency dominated by internal service hops
- Incidents requiring three teams for one customer ticket
- Sagas longer than the business process documentation
- Choreography nobody can draw from logs alone
- Rollbacks requiring coordinated multi-service releases anyway
Deployment complexity without release independence
The myth of autonomous deploys
Microservices promise independent deployment. Reality includes contract tests, shared libraries, feature flags across services, and release trains because breaking API changes still happen. If you always ship three services together, you have a distributed monolith with worse observability.
| Question | If no… |
|---|---|
| Can one service deploy without others? | Coupling remains |
| Are API contracts versioned and enforced? | Surprises at deploy |
| Can you roll back one service alone? | Blast radius is joint |
| Do teams own end-to-end outcomes? | Incidents ping-pong |
| Is there a game day for partial deploy? | Confidence is theoretical |
When a modular monolith is the better bet
A modular monolith keeps one deployment unit with strict internal module boundaries: clear interfaces, enforced dependency rules, separate schemas inside one database if needed. Teams gain refactor safety and transactional integrity. They defer network tax until a constraint — scale, regulation, team size — genuinely requires split.
This is not nostalgia. It is matching architecture to constraint. Small platform teams supporting many features cannot afford ten on-call rotations for a product that still fits one machine. Startups chasing split early often rebuild toward consolidation at series B — expensively.
“Microservices do not fix a messy monolith. They distribute the mess — and charge interest in network calls.”Constrange
Symptoms splitting made it worse
Velocity down after migration. Incident count up. Developers need three repos to fix one bug. Integration tests skipped because 'too slow'. Product waits longer for features that touch two services. None of this proves microservices are wrong globally. It proves they were wrong for this system at this stage.
- More engineers, slower delivery
- Platform work duplicated per team
- Local optimisations breaking global rules
- Documentation fragmented; onboarding measured in months
- Architecture review celebrates split; ops celebrates nothing
Recovering without shame
Consolidate, clarify, or constrain
Merge services that always change together — internally first, deployable second if evidence supports. Strengthen module boundaries inside a monolith before another split. Replace synchronous chains with batch or events only where the business tolerates delay. Name the constraint that would justify the next split — scale number, regulatory line, team size — and revisit when met.
Reversing split is politically hard and technically healthy. Continuing a failed split because the slide deck committed is how organisations fund permanent toil.
A decision framework before the next split
- Write the user journey latency budget — can services fit inside it?
- Map data that must stay consistent in one transaction.
- Count on-call rotations and platform headcount post-split.
- Prototype failure: kill one service; measure customer blast radius.
- Ask whether module boundaries in one repo solve the org problem instead.
If the framework returns 'not yet', that is a valid architecture outcome. Delaying split is engineering judgement, not failure to innovate.
Organisational pressure to split
Sometimes the push for microservices is not technical. It is hiring narrative, vendor partnership, or the belief that modern equals distributed. Teams inherit a target architecture before they inherit a problem that requires it. The result is services without owners, platforms without capacity, and a monolith's coupling expressed as latency.
Other times the monolith is genuinely painful — but because boundaries inside the repo were never enforced, not because deployment was unified. Fixing module discipline and test strategy may unlock more velocity than a three-year migration. Splitting a undisciplined monolith produces undisciplined services.
What good modular monolith practice looks like
Enforced dependency rules between packages. Schema modules aligned to aggregates. One deploy with feature flags for risky change. Clear ownership per module even if on-call is shared. Extraction becomes a later option with known seams — not an emergency rewrite.
- Lint rules blocking cross-module imports
- Integration tests at module boundaries
- Shared kernel kept uncomfortably small
- Extract-to-service checklist with data migration proof
- Architecture decision record for each future split candidate
Questions for your next architecture decision
- What constraint does splitting remove — specifically?
- What coupling reappears as network calls?
- Who owns data — really, not on the slide?
- Will we deploy independently or is that a story?
- Would a modular monolith meet the same goals with less tax?
Microservices are a tool for specific constraints. Used as default, they often make systems worse — slower, brittle, and harder to understand. The better engineering decision is sometimes fewer boxes, stricter modules, and honesty about when the split earns its keep.
Delaying a split is not technical debt if the constraint is not yet real. Shipping a modular monolith with clear seams is often how mature organisations avoid paying the distributed tax twice — once on the way out, once on the way back.
The question for leadership is not 'monolith or microservices'. It is which constraint you are buying relief from — and whether the invoice matches. Honest engineering answers with evidence, not pattern names.
The return journey
Organisations that split too early sometimes spend years re-merging services — not because microservices failed as an idea, but because the boundaries were wrong and the tax was paid without relief. The modular monolith they left behind would have been easier to evolve than the distributed graph they inherited. That return journey is expensive, politically awkward, and entirely predictable in retrospect.
Frequently asked questions
Are microservices always wrong for small teams?
Not always — but small teams rarely benefit from many deployables. Modular monolith first is often correct.
Can we merge services after splitting?
Yes. Consolidation is valid engineering when coupling proves joint lifecycle.
What is a modular monolith?
One deployable with enforced internal modules and clear interfaces — boundaries in code, not network.
How do we know we have a distributed monolith?
Joint releases, shared database, synchronous chains for most features — split without independence.