You Split the Monolith. Now You Own the Chaos.
Photo by Photo by Franck V. on Unsplash on Unsplash
There's a specific kind of engineering regret that hits you around month eight of a microservices migration. You've got 40 services. Twelve of them have one endpoint each. Nobody on the team can draw the full dependency graph from memory anymore. And the on-call rotation has quietly become a full-time job for whoever lost the coin flip that week.
You did everything right — or at least, everything the blog posts told you to do. You read the case studies. You followed the domain-driven design diagrams. You built the service mesh. And now the mesh is the problem.
The Promise vs. The Reality
The pitch for microservices is genuinely compelling. Independent deployability. Team autonomy. The ability to scale only the parts of your system that actually need it. For large orgs running at Uber or Netflix scale, these benefits are real and they matter.
But most teams adopting microservices aren't operating at that scale. They're 8-person startups or 30-engineer product orgs that read the same architecture blogs and drew the wrong conclusions. The result is a distributed system with all the complexity of large-scale infrastructure and none of the traffic to justify it.
What you end up with isn't a clean collection of independent services. It's a monolith that learned to make HTTP calls to itself.
When the Mesh Becomes the Monster
Service meshes — tools like Istio, Linkerd, or Consul Connect — exist to solve real problems: mutual TLS between services, traffic routing, circuit breaking, observability. In the right context, they're genuinely powerful. But they also add a layer of operational complexity that compounds fast.
You're now managing sidecar proxies alongside every service. You've got new failure modes: misconfigured mTLS policies, certificate rotation issues, proxy version drift. Your latency profile changes because every request hops through an additional network layer. And when something breaks, the debugging path isn't "check the logs" — it's "open four tools, correlate trace IDs, and hope the sampling rate caught it."
The teams that feel this hardest are usually the ones who adopted the mesh before they had the observability foundation to support it. Distributed tracing, structured logging, and service-level metrics aren't optional extras in this architecture. They're the floor. Without them, you're flying blind through a system that fails in ways a monolith never would.
The Hidden Tax Nobody Budgets For
Here's the math that rarely makes it into the architecture proposal: every service boundary is an operational surface area you now own forever. You need health checks, deployment pipelines, runbooks, alerting, capacity planning, and on-call coverage — for each one. A 40-service system doesn't have 40 times the complexity of a single service. It has something closer to 40-squared, because the interactions between services multiply the failure space exponentially.
Small teams feel this immediately. A startup with eight engineers shouldn't be running 30 microservices. The cognitive overhead alone will kill your velocity. You'll spend more time managing inter-service contracts and debugging network timeouts than building features. Your service mesh becomes a full-time job for engineers who should be working on the product.
Larger orgs aren't immune either. They just distribute the pain across more people until it becomes invisible in the headcount line item.
The Warning Signs You've Crossed the Line
How do you know you've gone too far? A few signals worth watching:
Your services can't deploy independently. If deploying Service A requires coordinating with Service B and Service C because of shared data schemas or synchronous dependencies, you haven't achieved microservices — you've achieved a distributed monolith with more moving parts.
You have services with single-digit daily request counts. A service that handles 200 requests a day doesn't need its own Kubernetes pod, its own CI/CD pipeline, and its own on-call rotation. That's overhead for the sake of architectural purity.
Nobody can explain the full request path for a core user flow. If tracing a checkout or a login through your system requires three engineers and a whiteboard, your service boundaries aren't clean — they're obscure.
Your incident response time has increased. More services means more places to look when something breaks. If your mean time to resolution has crept up since the migration, the architecture is eating your operational capacity.
When Splitting Actually Makes Sense
None of this means microservices are wrong. They're just frequently misapplied. The split makes sense when:
- You have genuinely independent scaling needs. Your image processing pipeline needs 10x the compute of your user auth service. Separate them.
- You have separate teams with separate release cadences. Conway's Law is real. If two teams own different parts of the system and need to ship independently, a service boundary enforces that cleanly.
- You've identified a bounded context with a stable, well-defined interface. Not "this feels like it should be separate" — an actual, proven boundary that won't require constant cross-service coordination.
- You're already running at a scale where the monolith is genuinely a bottleneck. Not "it might be someday" — actually, measurably, right now.
If none of those apply, you probably want a modular monolith. Good internal module boundaries, clear interfaces, separate packages or libraries — all the organizational benefits of microservices without the distributed systems tax.
Getting Back to Solid Ground
If you're already deep in the mess, the answer isn't a full rollback — that's rarely practical. But there are moves worth making.
Consolidate services that have high coupling and low independent value. That cluster of three services that always deploys together and shares a database? Merge them. Identify and eliminate zombie services — the ones with minimal traffic that exist only because someone once thought they might scale independently.
Invest in your observability layer before you touch anything else. You can't make good architectural decisions about a system you can't see clearly. Get distributed tracing working, get your service-level metrics in place, and actually look at the data before deciding what to split or merge.
And slow down on new service creation. Establish a real bar for when a new service is justified. "It feels like a separate concern" isn't a bar. "It has independent scaling needs, a stable interface, and a dedicated team" is a bar.
Build Intentionally, Not Ideologically
The microservices graveyard is full of good intentions. Teams that wanted to move faster, scale smarter, and build something they'd be proud of. The architecture wasn't wrong — the context was.
At b8c.io, we keep coming back to the same principle: the best architecture is the one that matches your actual constraints, not the one that matches your aspirational scale. Build for where you are. Design for where you're going. And don't let an architecture pattern turn into an operational trap that slows you down for years.
Your users don't care how many services you're running. They care if the product works.