We had twenty-three microservices.
I know the exact number because I counted them during a deployment.
Twenty-three services, each with its own repo, its own pipeline, its own database, and its own on-call rotation.
We were proud of it.
We had a wiki page titled “Our Microservices Architecture” with a beautiful diagram that looked like a subway map.
Investors loved it. New hires were impressed.
We were, by every metric, a modern engineering org.
Then we tried to change a button color.
The button lived in the frontend, but the color came from a feature flag service, which called a user preferences service, which queried a profile service, which read from a database that was updated by an event bus that was fed by an admin tool that required a schema change in three different services.
The button color change took six weeks and four teams.
And when we finally shipped it, the checkout flow broke because one of the services had a hardcoded assumption about the color hex code.
That’s the microservices mirage: you think you’re building a decoupled system, but you’re actually building a distributed monolith.
Same coupling. Same coordination overhead.
Just with more network hops and a much more expensive failure mode.
Here’s the uncomfortable truth nobody puts in the conference talk:
Microservices don’t decouple organizations. They just move the coupling from code to infrastructure.
Instead of one big codebase where you can see all the dependencies, you have twenty-three small codebases where the dependencies are invisible until they break at 2 a.m.
You haven’t eliminated the monolith.
You’ve hidden it behind HTTP calls.
I’ve seen teams spend months extracting a service from a monolith, only to have it call back to the monolith for every single request.
That’s not a microservice. That’s a monolith with extra latency.
I’ve seen “decoupled” services that share a database table.
I’ve seen “independent” deployments that require coordinated releases because service A’s new API contract breaks service B’s parser.
I’ve seen a team add a message queue to “decouple” two services, then add a synchronous HTTP call on top of the queue because they needed the response immediately.
That’s not decoupling. That’s a distributed monolith with extra steps.
The real problem is that we confused deployment independence with organizational independence.
Microservices give you the former.
They do absolutely nothing for the latter.
If your teams still need to coordinate every release, if your services still share a database, if your “API contracts” are just informal Slack messages, you don’t have microservices.
You have a monolith that’s harder to debug.
And the worst part?
The distributed monolith is strictly worse than the regular monolith.
With a monolith, you can trace a request in a debugger.
With a distributed monolith, you trace it across five dashboards, three log aggregators, and a distributed tracing tool that’s missing half the spans.
With a monolith, a schema change is one migration.
With a distributed monolith, it’s a cross-team negotiation.
We eventually consolidated six services back into two.
The subway map got smaller.
The deployment pipeline got simpler.
The button color change took two days instead of six weeks.
Nobody missed the extra services.
Nobody even noticed they were gone.
The lesson isn’t “microservices bad.”
The lesson is that decoupling is an organizational problem, not an architectural one.
You can’t fix a communication problem with a network boundary.
You can only make the communication problem harder to see.
Top comments (0)