The strangler fig pattern is a strategy for replacing a legacy system gradually, by building new functionality alongside the old system and routing traffic to the new code piece by piece, until the old system does nothing at all and can be safely turned off for good. Instead of a full rewrite done in one large, high-stakes project, the old and new systems run side by side for an extended period, often months or even years, with a routing layer deciding request by request which system handles what. The name comes from the strangler fig tree, which grows around a host tree, gradually replacing its structure over years until the original tree is gone entirely and the fig stands fully on its own, a slow but reliable form of replacement.
The reason the strangler fig pattern exists is that full rewrites of large legacy systems fail far more often than teams expect going in, and when they fail, they tend to fail badly and expensively. A big-bang rewrite means the business bets months or years of engineering effort on a single release that has to work correctly the first time out, replacing something that's often poorly documented and full of undocumented business rules nobody currently at the company remembers the original reason for. Meanwhile the old system usually still needs bug fixes and new features during the rewrite period, which the rewrite team either duplicates painfully or simply falls behind on while everyone waits. The strangler fig pattern solves this by never asking for that all-or-nothing bet: each piece that gets replaced is small enough to verify carefully, ship confidently, and roll back safely if something goes wrong, and the old system keeps running and getting maintained until its replaced parts are fully proven out in production. This is also, not incidentally, a much easier pitch to make to a business stakeholder who has lived through a failed rewrite before and is understandably wary of funding another one on faith alone.
What distinguishes the strangler fig approach from other migration strategies is the routing or facade layer sitting in front of both systems, directing each individual request to either the legacy system or the new implementation based on exactly what has been migrated so far. Early on in the migration, nearly all traffic goes to the legacy system, with only a small, carefully chosen slice routed to the new code as a first test. Over time, as more functionality gets built and proven reliable in the new system, the routing layer shifts progressively more traffic toward it, until eventually the legacy system is receiving nothing at all and can be decommissioned cleanly. The routing layer is the specific mechanism that makes the migration reversible at every single step along the way, since any newly migrated piece that starts misbehaving can be routed back to the legacy system almost instantly, without a dramatic incident.
By 2026, the strangler fig pattern is the default recommended approach for modernizing large legacy systems, whether that's breaking apart a monolith into services, moving off an unsupported framework nobody wants to touch anymore, or migrating from an on-premises system to the cloud gradually. It has become common enough that most cloud providers and modernization consultancies describe it as a starting assumption rather than a novel technique worth explaining from scratch, and teams increasingly reach for it by name rather than reinventing the same underlying idea independently each time. The pattern's popularity reflects a broader, hard-won lesson across the industry: legacy systems rarely get replaced cleanly in one shot, no matter how carefully the rewrite was planned or how talented the team executing it is. Enough public postmortems of failed rewrites have circulated over the years that the caution behind the strangler fig approach is no longer treated as excessive risk aversion; it's treated as basic professional diligence.
This page covers how the pattern's routing layer actually works in practice, how it compares to a big-bang rewrite and a parallel-run migration, where it fits well and where its inherently slow pace becomes a liability of its own, and how to run a strangler fig migration without letting it drag on indefinitely with no real end in sight. The durable idea underneath it is that large-scale change is genuinely safer taken in small, reversible steps than attempted in one irreversible leap, and once a team internalizes that lesson properly, they can apply the same incremental instinct to migrations well beyond the specific technical pattern described here by name.
The migration starts by identifying one small, well-bounded piece of functionality to move first, usually something with clear inputs and outputs and relatively low risk attached if something goes wrong along the way. This might be a single API endpoint, one page of a web application, or one contained business process like password reset. The goal at this early stage isn't to prove the new system can handle everything the old one does; it's to prove the routing mechanism itself actually works and that the team can safely ship and roll back a real piece of production functionality without drama.
Once that first piece is built and verified thoroughly, the routing layer is configured to send requests for that specific piece to the new system, while everything else continues going to the legacy system exactly as it always has. Users and other systems calling in shouldn't notice any difference at all except, ideally, that the migrated piece now works correctly or performs better. This is deliberately unglamorous work: the whole point is that nothing dramatic happens at any single step in the process, which is a strange thing to optimize for in an industry that often celebrates big, dramatic launches, but it's exactly the right instinct for a migration that has to hold up over months of real production traffic.
The team repeats this cycle, piece by piece, choosing the next chunk of functionality based on some mix of business priority, technical risk, and dependency ordering (pieces that other unmigrated pieces still depend on often get moved later, or get built with careful backward compatibility in mind from the start). Each piece migrated shrinks the legacy system's remaining responsibility and grows the new system's footprint in turn, and because each individual step is small, a mistake in one newly migrated piece doesn't threaten the pieces that were already proven stable earlier in the process.
Eventually, the legacy system's remaining responsibility narrows down to nothing at all, or to a small enough footprint that finishing it off in one final push is a reasonable, genuinely low-risk step rather than the original all-or-nothing gamble the team avoided at the start. At that point the legacy system can be decommissioned cleanly, and what's left standing is the new system on its own, exactly the way the fig tree eventually replaces the host tree it grew around over the years.
The routing layer, sometimes called a facade or proxy, is what makes the whole pattern possible in the first place, because it's the single point deciding where each incoming request actually gets sent. Without it, callers, whether that's end users, other internal systems, or external partners, would need to know themselves which system to talk to for which specific piece of functionality, which entirely defeats the purpose of a gradual, invisible migration nobody outside the team should notice.
This layer can be implemented several different ways depending on exactly what's being migrated: an API gateway with routing rules for a set of services, a reverse proxy directing web requests based on URL path, a feature flag system controlling which code path executes within a single monolithic application, or a message router directing events to old or new handlers depending on the event type. The specific mechanism used matters less than the underlying principle: one place decides where traffic goes, and that decision can be changed instantly and safely whenever needed, without a code deploy, a database migration, or coordination across multiple teams to make that change happen.
The routing layer also provides the rollback mechanism that makes the whole pattern genuinely low-risk in practice, not just in theory. If a newly migrated piece starts misbehaving in production, the fix isn't a rushed hotfix deployed under pressure or a rollback of an entire release, it's simply flipping the routing rule back to send that traffic to the legacy system while the new implementation gets properly fixed on its own timeline. This turns what would be a stressful, all-hands incident in a big-bang rewrite into a routine, low-drama adjustment that barely registers as an incident at all, and it's often handled by a single engineer within minutes rather than an entire team scrambling through an emergency call.
Building the routing layer well takes real engineering discipline of its own, and teams sometimes underestimate this part of the work entirely. It needs good observability so the team can see exactly how much traffic is going where at any given moment, good testing so routing changes themselves don't accidentally introduce new bugs, and clear ownership since the routing layer becomes a critical piece of shared infrastructure that both the legacy and new systems now depend on completely for their traffic.
A big-bang rewrite builds the entire replacement system separately from scratch, then switches everything over at once in a single cutover event scheduled well in advance. It has an appeal of simplicity in the planning stage, since there's no routing layer to build and no gradual traffic-splitting logic to manage over time, and the migration has a clear, if distant, finish line everyone can point to. The risk is concentrated entirely at the cutover moment: if the new system has a problem the old one never had, everyone finds out about it at once, often in production, often at the worst possible moment for the business.
A parallel run sends the same requests to both the old and new systems simultaneously, comparing their outputs carefully without the new system actually serving real responses to users yet, until confidence is high enough to cut over safely. This is a strong technique for validating correctness before making any real commitment, particularly for financial or data-critical systems where a wrong answer is genuinely expensive, but it requires running two full systems at once and doesn't by itself solve the all-at-once cutover risk; it usually gets combined with a strangler fig style gradual cutover once the validation work is done and confidence is established.
The strangler fig pattern spreads the risk across many small steps instead of concentrating it all at one single cutover, and it lets the legacy system keep serving traffic and receiving fixes throughout the entire process, which neither of the other two approaches does as naturally on its own. The cost is time: a strangler fig migration usually takes considerably longer in calendar time than a big-bang rewrite would, assuming that big-bang rewrite actually succeeded on its first attempt, which is exactly the assumption the pattern exists specifically to avoid relying on in the first place.
In practice, many serious modernization efforts combine elements of all three approaches: parallel-run validation for the riskiest pieces of functionality, a strangler fig style gradual rollout for the bulk of the migration work, and something closer to a decisive final cutover for the small remainder once the legacy system's footprint has shrunk enough that the remaining risk is genuinely low and well understood.
The strangler fig pattern fits best for large, business-critical systems where downtime or major bugs introduced during migration are simply unacceptable, and where the system is complex enough that a full rewrite would take so long the business requirements would have changed before it even finished anyway. Core transaction systems, large e-commerce platforms, and systems carrying years of accumulated business logic are classic candidates for this approach, precisely because the risk of getting a full rewrite wrong is severe enough to justify the slower, safer path through many small steps.
It also fits well when the legacy system needs to keep receiving bug fixes and even new features throughout the migration period, since the business rarely agrees to freeze a critical system's feature development for the year or more a full rewrite might realistically take. The strangler fig approach lets both tracks continue simultaneously: legacy fixes on the old system for not-yet-migrated pieces, new development on the new system for the pieces already migrated and proven stable.
It struggles for small or simple systems, where building an entire routing layer and running two systems in parallel is far more overhead than the actual risk being managed justifies; a small internal tool with a handful of users is usually served better by a straightforward, honest rewrite instead, since the coordination overhead of a routed migration would dwarf the actual engineering effort of just replacing the thing outright. It also struggles when an organization can't sustain the discipline a long migration genuinely requires: without strong project management, strangler fig migrations have a well-known failure mode where the easy 80% gets migrated quickly and enthusiastically, and the hard, gnarly 20%, the parts carrying the messiest business logic, lingers for years, leaving the organization running two full systems indefinitely instead of the one they set out to have.
The pattern also assumes the legacy system can actually be broken into pieces and fronted by a routing layer at all, which isn't true for every architecture out there. A system with tightly coupled internals and no clean seams for a facade to sit in front of may need real preparatory work, sometimes called "seam finding," before a strangler fig migration is even possible to start in any meaningful way. That preparatory work is easy to underestimate in a project plan, and skipping it tends to produce a routing layer that technically exists but sits in front of a system too entangled to actually route around cleanly.
Set a real deadline and a real budget for the migration right from the start, and revisit both regularly as the work progresses, because the single biggest failure mode of this pattern is a migration that never quite finishes because there's always some lower-priority piece left to defer just one more quarter. Treat the last 20% with exactly the same seriousness as the first 80%, since that's usually where the messiest, highest-risk logic actually lives, exactly the parts most tempting to keep postponing indefinitely. Putting a named owner and a specific quarter against that final piece of scope, rather than leaving it as an open-ended item on a backlog, is often the single difference between a migration that finishes and one that quietly becomes permanent.
Choose the first few pieces to migrate based on what will build confidence and prove the routing mechanism works reliably, not necessarily what's most valuable to the business on paper. Early wins should be low-risk and clearly successful, both to validate the technical approach chosen and to build real organizational trust in the migration before tackling the genuinely harder pieces later on. A well-chosen early win also gives the team a concrete story to point to when leadership asks, months into a long migration, whether the investment is actually paying off.
Invest seriously in observability across both systems and the routing layer itself, since the whole safety argument for this pattern depends entirely on being able to see, quickly and clearly, whether a newly migrated piece is behaving correctly in production. Without strong monitoring in place, the "safe rollback" advantage the pattern offers is only theoretical rather than real, because nobody will notice a problem in time to actually roll it back before real damage is done. A dashboard that shows the exact traffic split between old and new systems, updated in near real time, is one of the simplest and most valuable things a migration team can build before touching a single line of production traffic.
Keep the legacy system's remaining scope visible and tracked explicitly, ideally as a shrinking list the whole team can see and reference at any time, rather than letting "what's left in legacy" become an increasingly vague, undocumented question as the migration drags on. A migration that can't clearly state what fraction of functionality has moved is a migration that's quietly losing control of its own finish line. Even a simple spreadsheet listing each piece of functionality, its migration status, and its owner tends to outperform more elaborate tracking tools, mainly because everyone on the team actually looks at it and keeps it current.
The strangler fig pattern is a way to replace a legacy system gradually, building new functionality piece by piece and routing traffic to it incrementally through a routing layer, until the old system has nothing left to do and can be retired safely without a risky all-at-once cutover.
It's named after the strangler fig tree, which grows around a host tree and gradually replaces its structure over time until the original tree is gone entirely and the fig stands independently, a fitting image for a legacy system being replaced piece by piece rather than all at once.
A full rewrite builds an entire replacement system separately and switches everything over at once, concentrating all the risk in a single cutover event, while the strangler fig pattern spreads that same risk across many small, reversible steps taken over a longer period of time.
It's the component, often an API gateway, reverse proxy, or feature flag system, that decides which requests go to the legacy system versus the new implementation, and it's the specific mechanism that makes each migration step reversible and genuinely low-risk to execute.
Yes, that's one of the pattern's core advantages over other approaches; the legacy system keeps running and can still receive bug fixes for the parts not yet migrated, rather than being frozen entirely while a separate rewrite happens off to the side in isolation.
It varies widely with system size and complexity, but it's generally slower in calendar time than a big-bang rewrite would be if that rewrite actually succeeded on the first try, since the pattern deliberately trades speed for lower risk at every step along the way.
The migration stalling out indefinitely, where the easier pieces get migrated quickly and the hardest, messiest 20% of legacy logic keeps getting deferred, leaving an organization running two systems side by side far longer than anyone originally planned for.
Yes, the same incremental, routed-traffic approach applies equally well to moving from on-premises infrastructure to the cloud, migrating off an old unsupported framework, or any large-scale system replacement in general, not just breaking a monolith into microservices specifically.
Usually not; the overhead of building a routing layer and running two systems side by side is generally more effort than warranted for a small, low-risk system, where a straightforward, honest rewrite is often the simpler and considerably faster path to take.