In 2021, we made a company-wide decree: all new projects start as microservices. By 2024, we were quietly walking that back — not because microservices are bad, but because we'd been using them as a default rather than a decision.
The allure of microservices
It's easy to see why microservices became the default. The benefits are real — independent deployability, language flexibility, fault isolation, and the ability to scale individual services. After years of maintaining a monolith that had become a distributed monolith in disguise, switching felt liberating.
For the first 18 months, it worked beautifully. Smaller services meant cleaner boundaries. Teams moved faster. Deploys went from weekly events to daily occurrences.
"Microservices are not an architecture. They're an organisational pattern. Confuse the two and you'll build the wrong thing for the wrong reasons."
Sam Newman, Building Microservices
Where it started breaking down
The cracks appeared gradually. A "simple" feature — say, updating a user's subscription tier — now required coordinating changes across 6 services. Integration tests became so slow they were routinely skipped. Our service mesh configuration grew more complex than the business logic it was serving.
We ran an internal audit in Q3 2024. The results were humbling:
- Average time to ship a cross-service feature: 3.4× longer than equivalent monolith work
- 42% of our on-call incidents were caused by inter-service communication failures
- Local development setup took new engineers an average of 2.5 days
- We were running 34 services for a product used by ~12,000 businesses
What we switched to: a modular monolith
We didn't swing back to a spaghetti monolith. Instead, we adopted the modular monolith pattern — a single deployable unit with strict internal module boundaries enforced by linting rules and architectural fitness functions.
src/
modules/
billing/ → owns its own DB schema slice
auth/ → zero imports from other modules
analytics/ → event-driven, async only
notifications/ → adapter pattern, swappable
shared/
events/ → typed domain events only
utils/ → pure functions, no side-effects
Modules communicate via an internal event bus — no direct imports across boundaries. The discipline required is the same as microservices. The operational complexity is a fraction.
Results after 6 months
The numbers spoke for themselves. Cross-service feature delivery time dropped by 60%. New engineer onboarding dropped from 2.5 days to 4 hours. On-call incidents from inter-service issues: effectively zero.
When should you still use microservices?
We haven't abandoned microservices entirely. They're still the right call when:
- A component has dramatically different scaling characteristics (e.g. a video processing pipeline vs. a CRUD API)
- Security isolation is mandated — PCI scope, for example
- Independent release cadences are genuinely needed across large autonomous teams
- You're at a scale where the operational overhead is justified (think: 100+ engineers, millions of RPS)
If none of those apply to you today, start with a well-structured monolith. You can always extract a service when the evidence demands it. Going the other way — collapsing microservices into a modular structure — is significantly harder.
The meta-lesson
The real problem wasn't microservices. It was adopting architecture by trend rather than by fit. The best architectural decisions are boring: they're driven by concrete constraints, revisited regularly, and changed when the evidence changes.
We got too enamoured with the pattern and forgot to keep asking: does this still serve us?
This resonates deeply. We went through exactly this at my company last year. The 2.5 days for local dev setup was the breaking point for us too — we had engineers spending their first week just getting things running.
Yes, the onboarding time was the most shocking metric for us. You stop noticing when you're in it — then you measure it and it's like a cold shower.
Great write-up. One thing I'd add: the organisational pressure to adopt microservices is often just as strong as the technical pressure. It can feel like admitting defeat to step back, even when the evidence is clear.
Leave a Comment
Your email address will not be published. Required fields are marked *