Blog
Monolith or microservices: when splitting actually pays off.

In short. Microservices are justified when teams need to deploy independently, when parts of the system have very different scaling or availability needs, and when domain boundaries are already stable. If those conditions are not met, a well-modularized monolith is usually faster to build, easier to run, and simpler to split later, once it is really needed.
Signs that splitting makes sense
- Several teams work on the same codebase and block each other to deploy.
- One part of the system gets far more load than the rest and has to scale on its own.
- A component has availability or security requirements that differ from the rest.
- Some parts change every week and others go untouched for months.
- Domain boundaries are clear and have not moved in a while.
Signs that it is not time yet
- The whole team fits around one table.
- The product is still figuring out what it is, and boundaries between modules shift every month.
- Almost every important operation touches data from several modules at once.
- There is no continuous integration, monitoring, or tracing: without them, a distributed system is impossible to debug.
- The main motivation is “that is how big companies do it”.
The complexity you take on
Splitting a system does not remove complexity: it moves it from the code to the network. Every call that used to be a function can now fail, time out, or arrive twice. Problems appear that did not exist in a monolith:
- Consistency: an operation that spans several services no longer fits in one transaction.
- Contracts: every API is versioned, and any change means coordinating with its consumers.
- Operations: more deployments, more configuration, more infrastructure to watch.
- Debugging: following an error requires distributed tracing and correlated logs.
- Testing: validating an end-to-end flow means running several pieces at once.
The middle ground: a modular monolith
Between a messy monolith and a network of microservices there is an option we recommend often: a single deployable system, divided internally into modules with strict boundaries. Each module owns its data and exposes a clear interface to the others, even though everything runs in the same process.
You get most of the order microservices promise without paying their operational cost. And if one day a module does need to be split out, the work is half done: its boundaries exist and its dependencies are visible.
If you decide to split, how to do it
Splitting all at once rarely goes well. The path we follow is gradual:
- Put observability in place first: metrics, logs, and traces before moving anything.
- Pick the first service at the least coupled edge, not the most important part.
- Define the API contract before writing the service, and test it.
- Give the service its own data; a shared database cancels out much of the benefit.
- Shift traffic gradually and keep the previous version as a fallback in the meantime.
- Repeat only if the first service left operations better off than before.
Bottom line
The useful question is not “monolith or microservices?” but “what specific problem would splitting this solve, and is it bigger than the cost of running it?”. If the answer is not clear, start with a modular monolith. If you already have a system and are unsure, an architecture review will show you where the natural boundaries are before you invest in moving them.