The Temptation of Microservices: Lessons from a Mature Monolith

Written in

by

A few years ago, while working for one of the largest Indian consultancies, I was assigned to a system that surprised me in ways I didn’t expect. It was a monolith, yes, that word that often makes developers frown, but it was one of the most well structured pieces of software I had ever seen.

The codebase was clear. The boundaries between modules, though not enforced by a compiler, were respected by discipline. Each domain had its own space, its own logic, its own reason to exist. The architecture wasn’t fashionable, but it was effective. It served millions of users, scaled reliably, and was easy to understand. There was no rush to break it apart, no obsession with services, containers, or orchestration. Just solid software.

Working on that monolith taught me something important: not all monoliths are bad, and not all microservices bring clarity. In fact, the temptation to modularize, or worse, to split a system into microservices, can become a trap when it is driven by trends instead of needs.

This reflection comes in light of a recent thread I came across on Reddit. A team maintaining a Java based SaaS platform is debating whether to restructure their monolith into something more modular, perhaps with the long term vision of migrating to microservices. The thread is full of valid concerns: long PR build times, tightly coupled cronjobs, blurry module boundaries and a general desire to move faster and reduce risk.

But the conversation also reveals something deeper. A reminder that restructuring a monolith is not just about architecture, it’s about intention. And like all intentional changes, it requires reflection, not just excitement.


Key Considerations Before Breaking Down a Monolith

A shared data access module is a trap in disguise

Many teams begin their modularization journey by creating a central Data Access module. The idea is to share common queries and data models across different modules. It sounds logical, maybe even efficient, but it leads to a tightly coupled system in disguise.

This kind of setup turns a monolith into what some call a distributed monolith. Every change in data logic needs coordination. Every consumer becomes sensitive to internal changes. You may have modules, but they all lean on the same crutch.

I’ve seen this go wrong before. Instead of empowering teams, it creates bottlenecks. And more dangerously, it creates the illusion of modularity without any of its benefits.

Microservices are not a silver bullet for deployment pain

One of the most common arguments for moving away from a monolith is to improve fault tolerance or reduce blast radius. The assumption is that splitting things up automatically brings resilience.

But I’ve seen services go down due to unexpected network behaviors, message size limits or misconfigured queues. The failure modes become more subtle, more fragmented, and much harder to track.

Sometimes, just scaling the monolith horizontally solves the problem better. You gain availability without trading off simplicity.

Rewriting without a clear goal creates more complexity, not less

One of the most honest questions in the Reddit thread was: “What problem are you solving?”

And that’s where most re-architecture efforts fail. It’s not enough to say, “We want faster builds” or “We want more deployments.” You have to know why that matters. How it maps to business value. Otherwise, you risk months of work that change everything, only to end up exactly where you started.

Vertical modularization is more effective than horizontal layers

A better way to modularize is to slice the system by domain or business capability. This means each module owns everything it needs: its logic, its database access, its external integrations. This kind of boundary is much easier to extract later into a service, if needed.

I’ve worked with systems that followed this pattern. They aged better. Teams could work more independently. And testing, debugging, and onboarding became simpler.


What I’ve Learned from Monoliths and Their Transformations

Not everything needs to be a microservice. The most important thing is to start with clean boundaries, well defined modules and build on top of that gradually. Monoliths can evolve, improve, and even thrive when they’re nurtured properly. Trying to chase microservices for the sake of it often leads to pain, not progress.

Modularity, at its core, is about responsibility more than it is about structure. If your codebase doesn’t have clear ownership boundaries, introducing Maven modules won’t magically solve that. Structure without responsibility is just a facade. And it’s not just a Maven problem. Creating folders in a TypeScript project, defining packages in Python, or organizing Go code into separate files won’t magically bring clarity either. Without discipline and intent, modularity is only skin deep.

It’s also important to remember that technology is not the goal: clarity is. Clear responsibilities, clean interfaces, and a deep understanding of why something changes are more valuable than adopting whatever architecture is trending. All else is noise.

And finally, changing your tech stack or architecture is always expensive. Those changes should be rooted in real, observable pain. Otherwise, it’s just motion without meaning.

So maybe the bravest thing to do isn’t to split everything up. Maybe it’s to stay where you are a little longer, look carefully, and ask yourself if change really is necessary, or if you just need to understand your system better.

Sometimes, the most courageous thing you can do is to not change the architecture. At least not yet.

Leave a comment

The Stack Overflow of My Mind

Debugging life, one post at a time