How do you migrate out of a monolith incrementally?
The rewrite that runs alongside the existing system almost never lands. The old system cannot be frozen because the business still needs changes, so the new one is permanently chasing a target that moves at the same speed it does, and the cutover keeps slipping until someone cancels it. Incremental extraction is slower on paper and it is the only version that reliably finishes, partly because it is allowed to stop early.
Why does the parallel rewrite fail?
Because the monolith keeps changing while the replacement is built. Every feature shipped to the old system is a feature the new one must also implement before cutover, so the finish line moves at roughly the speed of the team building towards it. Feature freezes are proposed to solve this and are almost never granted, because the business did not commission a rewrite in order to stop competing.
The second reason is that a big cutover concentrates all the risk into one night. Everything that was wrong about the new system is discovered simultaneously, under time pressure, with a rollback that is complicated by data written during the attempt.
Incremental extraction inverts both properties. The old system keeps receiving changes, each extraction is small enough to reverse, and value arrives during the migration rather than at the end of it. That last point is what keeps a multi-year programme funded.
How does the strangler pattern work in practice?
You put something in front of the monolith that can route requests, then move one capability at a time behind it. New code goes into new services, the router sends the relevant traffic there, and the monolith shrinks over time without ever being switched off in one step.
The router is usually an existing component: an ingress controller, an API gateway, or a reverse proxy already terminating traffic. What matters is that routing decisions can be made per path or per header without a code change in the monolith, so extraction and rollback are configuration operations rather than releases.
The precondition people miss is that the seam has to be somewhere requests actually arrive. If a capability is only reachable through internal function calls deep inside the monolith, there is nothing at the edge to route, and the first piece of work is introducing an internal interface at that point rather than extracting anything. That intermediate step is worth doing anyway, because it makes the eventual extraction a change of transport rather than a redesign.
Which patterns should you use for each move?
Four, chosen by how tangled the code is and how much the data is shared. Most migrations use several in different places, and using the wrong one is what turns a two-week extraction into a quarter.
| Pattern | What it does | Use when | Risk |
|---|---|---|---|
| Strangler facade | Route selected requests to a new service at the edge | The capability has its own entry points | None if routing is reversible by configuration |
| Branch by abstraction | Introduce an interface, add a second implementation, switch | The capability is reachable only from inside | The abstraction outlives its purpose and sticks |
| New feature outside | Build the next capability as a service from the start | Something genuinely new is being commissioned | The new service still needs the monolith's data |
| Replicate then cut over | Copy data to the new owner, read there, then write there | The data is shared by many parts of the monolith | Divergence between copies during the overlap |
Which service should you extract first?
Something with few inbound dependencies, clearly owned data, and a specific reason to be separate. The first extraction is a rehearsal for the platform work, the deployment pipeline, the tracing and the on-call arrangements, so its value is mostly in what it teaches. Choosing the most business-critical component for that rehearsal is how migrations acquire a reputation for causing outages.
Resist the opposite temptation as well. A trivial component with no reason to be separate teaches you the pipeline and then sits there as a service that adds a network hop for nothing. The best first candidates usually have an honest justification already: a workload with a different scaling profile, a component a second team wants to own, or something needing a runtime the monolith cannot host.
A frequently better answer is not to extract anything first. Build the next new capability outside the monolith instead. It has no legacy data to untangle, it exercises the entire platform end to end, and if the approach turns out to be wrong you have learnt it on something small rather than on a component the business depends on.
How do you move the data?
In stages, with reads moving before writes. Stand up the new service's database and replicate the relevant data into it continuously, usually with change data capture from the monolith. Point the new service's reads at its own copy while the monolith remains the writer, and compare results for a while: run the same query against both and log the differences. That comparison period is where you find the assumptions nobody documented.
Then move the writes, once, with the monolith reading from the new service afterwards rather than from its own tables. The intermediate state where both systems write to their own copies should be avoided if at all possible, because reconciling two writers is genuinely difficult and the bugs it produces are data corruption rather than errors.
Where dual writing cannot be avoided, make one side authoritative and treat the other as a projection to be rebuilt rather than repaired. Anything else requires conflict resolution rules, and writing those correctly is harder than the extraction that motivated them.
How do you know when to stop?
When the remaining monolith is no longer causing the problem you set out to solve. A migration does not have to finish, and the end state of a well-run one is frequently a smaller monolith surrounded by a handful of services that had specific reasons to exist. That is a legitimate architecture, not an abandoned project, and it costs far less to operate than a fully decomposed estate.
The signal to stop is that extractions stop having a named benefit. As long as each move is justified by an autonomy, scaling or runtime need, keep going. When the next candidate is being extracted because it is the next one on the diagram, the migration has become an end in itself and the remaining budget is better spent on the product.
A useful check at any point: list the extractions completed so far and, for each, the specific improvement it delivered. If several have no answer, the criteria being used are not working, and continuing will produce more of the same.
Common questions
- What is the strangler pattern?
- An incremental migration approach where a router in front of an existing system sends selected requests to new services, one capability at a time, so the old system shrinks without ever being switched off in a single step. The routing usually lives in an ingress controller, gateway or reverse proxy, and extraction becomes a configuration change that can be reversed rather than a release that cannot.
- Why do big-bang rewrites of monoliths fail?
- Because the old system keeps changing while the replacement is built, so the finish line moves at roughly the speed of the team approaching it, and feature freezes are rarely granted. A single cutover also concentrates every undiscovered problem into one night, with a rollback complicated by data written during the attempt. Incremental extraction delivers value throughout, which is also what keeps it funded.
- Which part of a monolith should you extract first?
- Something with few inbound dependencies, clearly owned data, and a real reason to be separate, such as a different scaling profile or a second team wanting ownership. The first extraction mainly teaches you the pipeline, tracing and on-call arrangements, so the most business-critical component is the wrong rehearsal. Often the better move is building the next new capability outside the monolith instead.
- How do you split a database during a migration?
- Move reads before writes. Replicate the relevant data into the new service's database continuously, point the new service's reads at its own copy while the monolith still writes, and compare the two for a period to surface undocumented assumptions. Then move the writes once, with the monolith reading from the new service afterwards. Avoid a state where both systems write their own copies.
- Do you have to fully decompose a monolith?
- No. A smaller monolith surrounded by a handful of services that each had a specific reason to exist is a legitimate end state and costs less to operate than a fully decomposed estate. Stop when extractions no longer have a named benefit. If the next candidate is being extracted because it is next on the diagram, the migration has become an end in itself.