Articles

Monolith to Microservices Migration: The Strangler Fig Playbook for Teams Who Can't Afford to Stop

By Claus Villumsen
18 August, 2026
Share this article
Monolith to microservices migration is not a technology problem. It is a sequencing problem. Most teams that fail do not fail because they chose the wrong framework or the wrong cloud. They fail because they tried to do too much at once, drew the wrong boundaries, and then spent 18 months untangling a distributed monolith that was harder to operate than the original. There is a better way. It is slower. It is less dramatic. And it actually works.
The pressure to migrate is real. Your monolith was probably fine when it was built. It handled the load, the team understood it, and shipping features was straightforward. Then the team grew. Then the load grew. Then the codebase grew until no single person could hold it in their head anymore, and every deployment became a negotiation between twelve engineers about what was safe to release together. That is when the word "microservices" starts appearing in every conversation.
The instinct is to start over. To clear the board. To plan the big-bang rewrite that will finally produce the clean, modern, independently deployable system you should have had all along. That instinct is almost always wrong, and Martin Fowler's foundational writing on the strangler fig application has been saying so for over a decade. The pattern is named for a plant that grows around a host tree, slowly taking over its structure until the original tree is gone and only the new growth remains. Uncomfortable metaphor. Extremely effective engineering strategy.
Think about the last time your organisation tried to rewrite a major system from scratch. How long did it take compared to the original estimate, and what happened to the features that were "temporarily" deprioritised while the rewrite was underway?
What makes monolith to microservices migration fail before it even starts?
Most migrations fail because teams treat service boundaries as a technical decision rather than a business one. They split along code layers - frontend, backend, database - instead of along business capabilities. The result is services that are technically separate but operationally glued together, which is harder to manage than the original monolith.
The first question most teams ask is: "How do we split the monolith?" That is the wrong question. The right question is: "What business capability can we extract that has clear ownership, a well-defined interface, and a reason to change independently of everything else?" These are very different questions, and they produce very different service boundaries.
The failure pattern that kills more migrations than anything else is the distributed monolith. It looks like microservices from the outside - separate repositories, separate deployments, separate teams. But every service makes synchronous calls to six others for every user action, and they all share the same database. You have distributed the complexity without distributing the autonomy. You have made everything harder to debug, trace, and recover, without gaining any of the deployment independence that was the point.
InfoQ has documented this failure mode extensively, and the root cause is consistently the same: teams start with the code and work backward to the business, instead of starting with the business and working forward to the code. Domain-driven design, specifically the concept of bounded contexts, exists precisely to solve this problem. A bounded context is a boundary around a set of business concepts that belong together, have consistent language inside the boundary, and communicate with the outside world through explicit contracts.
Before you draw a single service boundary, you should be able to answer: who owns this capability, what does it need to know about, what does it deliberately not need to know about, and how often does it change relative to adjacent capabilities? If those answers are fuzzy, the boundary will be fuzzy, and a fuzzy boundary in a microservices architecture is a support ticket waiting to happen.
How does the strangler fig pattern work in practice?
You build a proxy or routing layer in front of the monolith. New services handle specific routes or capabilities. Traffic is progressively redirected from the monolith to the new services. The monolith shrinks over time as functionality moves out, until it handles nothing and can be decommissioned. Nothing goes dark. Users notice nothing.
The strangler fig pattern sounds elegant in diagrams. Here is what it looks like in a real organisation over the first six months.
Month one: you identify the single business capability that is the best candidate for extraction. Best candidate means well-understood, moderately sized, has a clear owner, does not share data with seventeen other modules in complicated ways, and is causing genuine pain in its current form. User authentication is a classic starting point. So is notification delivery, or PDF generation, or pricing calculation. Something real, something bounded, something your team can get their head around without six months of archaeology.
Month two: you build the new service alongside the monolith, not as a replacement for it. Both exist. You introduce a routing layer - often called an API gateway or facade - that sits in front of both. For now, everything still goes to the monolith. The new service is being built and tested in parallel.
Month three: you begin routing a percentage of traffic to the new service. Not all of it. Five percent, then twenty, then fifty. You watch the error rates. You watch the latency. You compare behaviour between the two paths. This is the phase where you learn what you got wrong in your assumptions, and you want to learn that at five percent of traffic, not one hundred.
By month six, the new service is handling all traffic for that capability. The monolith still contains the old code, but nothing calls it anymore. You can safely delete it. The monolith is smaller. The team is more confident. You pick the next capability and repeat.
This is slow. It is supposed to be slow. The alternative is betting the business on a rewrite that, statistically, runs 200 percent over budget more often than not.
If you extracted the single highest-pain capability from your monolith in the next ninety days and nothing else, what would that capability be, and what would it cost you in engineering time to do it incrementally versus what you imagine a full rewrite would cost?
How do you handle the shared database, the hardest part of any migration?
You never try to split the database and the service at the same time. Separate the schemas first within the same database instance. Then migrate to separate database instances. Use event-based communication and dual-write patterns to keep data consistent during the transition. Treat database decoupling as its own workstream with its own timeline.
Splitting a monolith is relatively straightforward when the code is the only thing tangled together. In most real systems, the database is more tangled than the code. Tables reference each other through foreign keys across what should be separate domain boundaries. Services that should be independent share the same User table, the same Order table, the same God Object that grew over fifteen years of feature additions.
You cannot move the service without moving the data. But you cannot move all the data at once without a big-bang migration. So you decouple incrementally.
The practical sequence is: logical separation first, physical separation second, communication pattern last. Logical separation means you identify which tables belong to which future service and stop letting code outside that future service query those tables directly. You enforce this through the application layer before you touch the database. Then you split schemas within the same database, which has low operational risk. Then, when the service is stable and the data ownership is clear, you migrate to a separate database instance.
The communication problem - how do services share data they genuinely both need - is where most teams reach for synchronous API calls, and that is almost always a mistake. If Service A needs to know the customer's email address to send a notification, and Service B owns the customer record, the temptation is to have Service A call Service B on every notification. Now you have a dependency at runtime. If Service B is slow, Service A is slow. If Service B is down, Service A cannot operate.
The better pattern is events. Service B publishes an event when a customer record changes. Service A subscribes to that event and maintains its own local copy of the fields it needs. Service A is now operationally independent. It can send notifications even if Service B is offline. This is eventual consistency, and it requires a different mental model, but it is what gives microservices their operational resilience.
Where does AI genuinely help, and where does it make things worse?
AI tools are excellent at mapping hidden dependencies, suggesting initial domain boundaries, generating boilerplate service scaffolding, and scanning for coupling patterns that humans miss in large codebases. They cannot tell you which boundaries matter to your business, and they cannot replace the architectural judgment call on data ownership. Use them to see more clearly, not to decide for you.
There is a growing category of tools - including platforms built around architectural observability like the approach taken by vFunction in the AWS ISV Workload Migration Program - that use AI to visualise the actual dependency graph of a running monolith. Not what the architecture diagram from 2018 says the dependencies are. What they actually are, based on runtime behaviour, call patterns, and data flow.
This is genuinely valuable. Most teams discover, when they run these tools, that their monolith is ten to fifteen percent less coupled than they feared, and thirty percent more coupled in places they had not noticed. That changes where you start the migration. It changes which capabilities are safe to extract early and which ones need more untangling first.
AI-assisted tools can also accelerate the boilerplate work: generating service skeletons, writing adapter layers, producing the scaffolding for API contracts, and flagging places in the code where cross-domain data access is happening without permission. This removes weeks of manual archaeology from the early phases of a migration.
Where AI falls short is the judgment layer. A model can tell you that these forty database tables are heavily accessed together. It cannot tell you whether those forty tables represent one business capability or three, because that is a question about how your business works, not how your code works. It cannot tell you that the pricing calculation service needs to be owned by the finance team rather than the engineering team, because that is an organisational decision with political dimensions no tool can navigate.
The Thoughtworks Technology Radar has tracked AI-assisted modernization tools for several cycles now, and the consistent observation is that they accelerate the discovery and scaffolding phases substantially while leaving the strategic decisions squarely with human architects. That is exactly the right division of labour. Use AI to see the problem clearly. Use judgment to decide what to do about it.
What does a realistic migration timeline look like for an enterprise system?
A mid-size enterprise monolith with five to fifteen years of history typically takes 18 to 36 months to migrate incrementally. The first three months are discovery and the first service extraction. Months four through twelve prove the pattern. Months twelve through thirty-six industrialise it across the remaining capabilities. Rushing any phase multiplies the cost of the next one.
There is a version of this conversation where someone says "eighteen to thirty-six months is too long, we need this done in six." I understand the pressure behind that statement. I also know what happens when you try to compress it.
You skip the discovery phase and guess at boundaries. You share the database longer than planned because there is no time to decouple it properly. You build services that are technically separate but operationally entangled, and you go live with a system that is simultaneously harder to operate and less reliable than the monolith it replaced. Then you spend the next two years fixing it, and the total time is four years instead of three, at significantly higher cost in both engineering hours and production incidents.
The single biggest thing that separates successful migrations from failed ones is treating the first service extraction as a proof of concept, not a deliverable. The goal of the first three months is not to ship a microservice. It is to learn your team's actual capacity for this kind of work, validate your tooling choices, discover what your dependency map actually looks like, and produce a realistic revised estimate for the rest of the programme.
If month three produces a clean, independently deployable service with its own data store, clear ownership, and an automated deployment pipeline, you have the foundation. Everything after that is repetition and refinement. If month three reveals that your database is twenty percent more entangled than you thought, you now know that before you have committed the entire engineering organisation to a plan built on a wrong assumption.
Go slow at the start. The rest of the journey will be faster for it.
When your team talks about this migration, are they talking about what it will look like when it is finished, or are they talking about what success looks like at the end of the first ninety days? Which conversation would be more useful to have right now?
Frequently Asked Questions
What is the strangler fig pattern in monolith to microservices migration?
The strangler fig pattern is an incremental migration approach where new microservices are built alongside the existing monolith, gradually taking over functionality until the monolith can be retired. It avoids a big-bang rewrite by letting the old and new systems coexist and share traffic during the transition period.
How long does a monolith to microservices migration typically take?
Most enterprise migrations take 18 to 36 months when done incrementally. Rushing it into a shorter window usually means cutting corners on service boundaries, data decoupling, and observability, which creates a distributed monolith instead of genuine microservices. Scope, team size, and codebase complexity all affect the timeline significantly.
What is the biggest risk in migrating from a monolith to microservices?
The biggest risk is drawing service boundaries in the wrong place. If you split services along technical layers rather than business capabilities, you end up with tightly coupled microservices that are harder to operate than the original monolith. Domain-driven design helps anchor boundaries to real business logic rather than code structure.
Can AI tools help with monolith to microservices migration?
Yes, but within specific limits. AI tools are genuinely useful for mapping dependencies, surfacing hidden coupling, identifying domain boundaries, and generating boilerplate service scaffolding. They cannot make the judgment call on which boundaries matter to your business, and they cannot replace the architectural review needed before you move shared databases.
What is a distributed monolith and how do you avoid it?
A distributed monolith looks like microservices but behaves like a monolith. Services are deployed independently but share a database, make synchronous calls to each other for every operation, or cannot be released without coordinating across teams. You avoid it by enforcing data ownership per service, using asynchronous communication where possible, and treating deployment independence as a hard requirement from day one.
How do you handle the shared database problem during migration?
You decompose the database incrementally alongside the services. The typical approach is to start by separating schemas within the same database instance, then move to separate database instances per service over time. Dual-write patterns and event sourcing can bridge the gap while both systems are live. Never try to split the database and the service in a single step.
How do you measure whether your migration is succeeding?
Track deployment frequency per service, mean time to recovery, the percentage of traffic handled by new services versus the monolith, and the number of cross-service synchronous dependencies. If deployment frequency is not increasing and recovery time is not falling, the migration is producing overhead without benefit.
Kodebaze maps your monolith's real dependency graph, identifies the safest extraction sequence, and guides your team through an incremental migration that keeps production stable at every step.
See how Kodebaze approaches it →Related articles

Legacy Modernization
AI
Most legacy system modernization risk frameworks flag the wrong things. Here's what actually determines whether your project succeeds or fails.

Legacy Modernization
AI
Application modernization services vary wildly in what they deliver. Here is how enterprise leaders evaluate and select the right one before committing.

Legacy Modernization
AI
Static vs dynamic code analysis together reveal what no human reviewer can. Here's what each method finds and why both matter before you modernize.
AI + Human
AI + Human software Solution
© 2026 Kodebaze. All Rights Reserved.
© 2026 Kodebaze. All Rights Reserved.