In 2022 I watched a team I mentor tear down a dozen services and ask why we spent so much time fighting our own infrastructure instead of shipping code.
The impulse to carve out a new service became the default answer whenever a feature grew a bit larger, even when the code could have lived happily inside the same process.
What we didn’t anticipate was the hidden tax: every service demanded its own CI pipeline, its own Prometheus scrape, its own on‑call rotation, and every call turned into a network hop that added milliseconds to latency.
For example, we had a service that handled authentication, which was called by almost every other service. It had its own pipeline, monitoring, and on-call rotation. But when we looked at the traffic, we saw that it was only handling a few hundred requests per second. We realized that we could easily move it back into the monolith and eliminate the extra overhead.
After a few months the operational burden outweighed the theoretical benefits, and I started looking at the old monolith we’d once called a legacy monster.
A well‑designed monolith with clear module boundaries and strict dependency rules can give you most of the isolation microservices promise, minus the extra network and ops layers. DHH called it the Majestic Monolith, Spring later packaged the idea as Modulith, and the .NET world has similar patterns.
In our case, we used a combination of Java modules and clear interface definitions to keep the internal architecture clean and maintainable. We also made sure to follow strict dependency rules to avoid coupling between modules.
Those patterns let you deploy the whole thing as a single unit, run unit tests across module boundaries, and still keep a clean internal architecture that developers can reason about.
Microservices still make sense when you have dozens of autonomous squads that need to ship on completely separate cadences, when a subset of traffic must scale to millions of requests per second, or when compliance forces physical isolation.
In most of the companies I’ve seen, those conditions are rare; the majority of teams end up paying for pipelines and monitoring they never use.
To move back, I first map which services talk to each other constantly, then ask if they ever needed independent scaling. I draw a consolidation boundary and apply the strangler‑fig pattern in reverse, pulling code into the monolith piece by piece until the old services can be retired.