A client - a B2B SaaS company, details anonymized with their permission - engaged us last year with a paradox. They had adopted microservices specifically to ship faster. Three years in, they had 43 services, 7 backend engineers, and the slowest delivery cadence in their history. A feature that touched three services took a sprint of coordination before a line of code was written.
Nothing was broken. Every service was reasonable in isolation. The cost was structural, and it was hiding in places their metrics did not measure.
Where the cost actually lives
The coordination tax. With 43 services and 7 engineers, every engineer owned six services. Every non-trivial feature crossed service boundaries, which meant API design discussions, versioned interfaces between components owned by the same person, and deployment ordering. The boundaries designed to decouple teams were decoupling an engineer from herself.
The infrastructure floor. Each service carried a baseline: a deploy pipeline, dashboards, alerts, dependency updates, security patches. Call it a few hours per service per month - modest individually, but 43 services made it a full-time engineer's worth of pure maintenance, before any product work.
The debugging surface. A single user-facing request traversed up to 9 services. Distributed tracing existed, but tracing tells you where the time went, not why the logic misbehaved. Incidents that would have been a stack trace in a monolith became multi-service correlation exercises. Their median incident diagnosis time was over an hour; most of it was spent locating the problem, not fixing it.
The consistency tax. Data that would have been one transaction was spread across service boundaries with eventual consistency and compensation logic. Some of the subtlest bugs we found were reconciliation jobs quietly papering over race conditions that a single database transaction would have made impossible.
What we actually did
The fashionable answer would have been a rewrite. We recommended consolidation instead: merge services along data-ownership lines until each remaining service has a single reason to exist and, ideally, a single team-sized owner.
Over ten months, 43 services became 9. The mergers were mechanical more often than expected - many boundaries existed for historical rather than domain reasons, and services sharing a database or deploying in lockstep were merge candidates by definition. Two boundaries survived on merit: a compliance-isolated payment service and a compute-heavy document pipeline with a genuinely different scaling profile.
The results, one quarter after completion: deploy frequency up 2.4x, median incident diagnosis down from 70 to 25 minutes, and the infrastructure maintenance load down by roughly two-thirds. No customer noticed anything, which was the point.
The heuristic we now give every client
Microservices convert organizational scale problems into technical ones. That trade is worth it exactly when you have the organizational problem - multiple teams stepping on each other in one codebase. Our rule of thumb:
- Fewer services than teams: probably underinvested in boundaries
- Services roughly equal to teams: healthy
- Services several times your engineer count: you are paying the distributed-systems tax without the parallelism dividend
The client's mistake was not incompetence; it was adopting the architecture of the companies they read about, at a headcount those companies had outgrown years earlier. Architecture is a function of team size, and team size changes. The boundaries should too - in both directions.