Key takeaways
- A modular monolith is the right default for most new products.
- Microservices pay off with multiple teams and clearly separate domains.
- Distributed systems bring real costs: consistency, tracing, operations.
- Migrate gradually, one capability at a time, never with a big-bang rewrite.
Microservices are not an upgrade from a monolith — they are a trade-off. How to decide which architecture fits your product today, and how to move between them safely.
It's a trade-off, not a maturity level
A monolith is one deployable application. Microservices split an application into independently deployable services, each owning a business capability and its data. Neither is inherently better; each makes different things easy and different things hard.
Where a monolith wins
A well-structured modular monolith — clear internal modules with defined interfaces — keeps these advantages while preparing for a later split.
- One codebase to understand, test and deploy.
- Database transactions across the whole domain.
- Simple debugging — one process, one log.
- Low operational overhead for a small team.
Where microservices win
In a delivery platform we built on Spring Boot and Kafka, isolating ordering and payments from menus and dispatch meant checkout kept working during peak load — a benefit worth the added complexity for that business.
- Teams deploy independently without coordinating releases.
- Busy components scale on their own.
- A failure in one service can be contained.
- Each service can use the storage that suits it.
A simple decision framework
Consider microservices when most of these are true; otherwise start with a modular monolith.
- Several teams need to release independently.
- The domain has clearly separate business capabilities.
- Parts of the system have very different load profiles.
- You already run CI/CD, monitoring and tracing confidently.
Moving from monolith to services
When the time comes, extract one capability at a time. Put an interface in front of it, build the new service behind that interface, run old and new in parallel, and switch traffic gradually. Each step should be reversible and proven in production before the next.
Enjoyed this? Get the next one by email.

