From monolith to microservices: when it makes sense and how to get it right
ArchitectureScaling

From monolith to microservices: when it makes sense and how to get it right

Moving to microservices isn't always the right answer. A practical roadmap for deciding when to extract, what to isolate and how to keep delivery moving along the way.

QMates· Software Advisory3 April 202611 min read

Every scale-up reaches a point where the monolith starts holding it back. Features pile up, releases become risky, teams tread on each other's toes. The temptation is strong: "let's move to microservices and that will sort everything out".

The honest answer is that microservices solve problems of organisational scale, not technical scale. If your team can't release independently on the monolith, it won't be able to with microservices either; it will simply have more services to coordinate. Migration makes sense when the domain boundaries are clear, teams can work in parallel without getting in each other's way, and the monolith is the real bottleneck. Not before.

This article is a practical roadmap for anyone weighing up a migration: when it makes sense, when it doesn't, and how to go about it without bringing delivery to a standstill.

The monolith is not the enemy

A well-structured monolith, with clear internal boundaries, independent modules and an efficient release pipeline, can support an organisation for years. Companies with dozens of developers work effectively on monoliths: what matters is not the architectural topology but the quality of the boundaries.

The trouble starts when the monolith becomes a coupled monolith: every change touches unrelated components, every release needs several teams to coordinate, every test needs the whole system. At that point it isn't the monolith itself holding you back; it's the coupling.

Before deciding to migrate, the right question is not "monolith or microservices?" but "are the boundaries in our system in the right place?"

When microservices solve the problem

Microservices are the right answer when the problem is structural rather than a matter of code quality. Three signs that a migration makes sense:

  • Teams held up by dependencies: teams can't release on their own because every change cuts across areas of responsibility. Coordination between teams becomes the main bottleneck (more on dependencies between teams)
  • Parts of the system that change at different speeds: the payments module changes every week, the reporting module every quarter. Releasing them together wastes time and adds risk
  • A need for selective scalability: one part of the system needs 10 times the computing resources of the rest. Scaling the entire monolith for a single feature is expensive and inefficient

If none of these signs is present, the problem can probably be solved with strategic refactoring inside the monolith: less risky, less expensive and quicker.

Decision matrix for extracting a microservice: the horizontal axis shows how much team autonomy is needed, the vertical axis how often the code changes
Decision matrix for extracting a microservice: the horizontal axis shows how much team autonomy is needed, the vertical axis how often the code changes

When microservices make things worse

Microservices don't come for free. Every service you extract adds operational complexity: monitoring, deployment, networking, handling errors across a distributed system, data consistency. If the organisation isn't ready to manage that complexity, the result is a distributed monolith: all the drawbacks of a monolith plus all the drawbacks of distributed systems.

Signs that a migration is premature:

  • The team has no experience with distributed systems
  • There is no automated, reliable CI/CD pipeline
  • Monitoring in production is missing or rudimentary
  • The domain boundaries are unclear: nobody knows where one module ends and the next begins
  • The problem is code quality, not architecture

In these cases the priority is to shore up the foundations: tests, CI/CD, monitoring, module ownership. Only once those basics are solid does migration become a realistic option.

The roadmap: how to migrate incrementally

Migrating to microservices is not a big-bang project. It is an incremental journey measured in months, sometimes years, and it delivers value at every step.

Step 1: map the domain boundaries. Before extracting anything, you need to understand where the natural boundaries of the business lie. Techniques such as Event Storming or Domain Storytelling help surface the bounded contexts: the areas of the system that ought to be able to evolve independently. This map guides every decision that follows.

Step 2: pick the right candidate. Don't extract the largest or the most important module: extract the one with the best balance between the friction it causes today and the risk of extracting it. Typically that is a module that changes often, has reasonably clear boundaries and doesn't sit at the centre of too many dependencies.

Step 3: isolate the boundary. Before extracting the module, define the interface between it and the rest of the system. This boundary, the so-called anti-corruption layer, is the contract that lets old and new coexist. The monolith calls the new service through this interface; if the new service fails, the old code can still respond.

Step 4: migrate gradually. This is the Strangler Fig pattern: the new service handles a growing share of the traffic while the old code is progressively retired. No big switchover, just a controlled and reversible transition.

Step 5: validate and repeat. After each extraction, measure the impact. Is the team releasing faster? Have dependencies gone down? Is the system more stable? If so, move on to the next candidate. If not, work out what went wrong before carrying on.

The most common mistake: the wrong boundaries

The biggest risk in a migration is not technical; it's conceptual. If the boundaries between services don't reflect the boundaries of the business domain, you end up with a distributed system in which every feature needs calls to five different services, transactions become distributed and debugging turns into a nightmare.

Architectural boundaries have to follow organisational boundaries. Each service should be owned by a single team, and each team should be able to release its own service without coordinating with other teams. If that isn't possible, the boundaries are in the wrong place.

This is Conway's law in action: the structure of the software mirrors the structure of the organisation. Redesigning the architecture without redesigning the organisation produces results that don't last; within a few months the system drifts back to mirroring the old structure. We go into this in detail in our article on evolutionary architecture.

What to do in practice

If you are weighing up a migration, these are the three things to do before writing a single line of code.

The mistake we see most often in scale-ups that come to us about a migration is this: they want to move to microservices because "the monolith has become hard to manage", but when we analyse the system we find that the monolith isn't the problem; the problem is that there are no clear boundaries between the modules. A modular monolith, with clear ownership by domain, solves 70% of the coordination problems that get blamed on monolithic architecture. Moving to microservices comes later, if and when it is genuinely needed.

  1. Map the real dependencies: not the ones you think you have, but the ones that show up in the code and in how teams communicate. Who talks to whom? Which module changes every time another one does? The Consistency Impact Calculator can help you put a figure on the friction
  2. Check the boundaries against the business: technical boundaries must reflect the boundaries of the domain. If the payments team and the orders team work on the same module, the boundary is in the wrong place
  3. Start with the lowest-risk module, not the most critical one. The first service you extract is where you learn the process; the second is where you start creating value

If the picture is complicated, with tangled dependencies and unclear boundaries, this is exactly the kind of situation where a Targeted Intervention engagement makes all the difference. Finding the right place to start is often the most important decision of the whole journey.

Tell us where you're stuck

A fragile prototype, a burdensome legacy codebase or unpredictable delivery: that's where we start

We use your data to respond to your request. Read our Privacy Policy.