Most legacy migration projects fail the same way. A team spends eighteen months building “the new system” in parallel. The old system keeps evolving because the business cannot pause. By the time the new system is ready for launch, the requirements have shifted, edge cases have multiplied, and nobody is entirely sure the new system handles everything the old one does. The big-bang cutover becomes a white-knuckle weekend, and half the time you end up rolling back.

The strangler fig pattern offers a fundamentally different approach. Instead of replacing a system all at once, you replace it one piece at a time, routing traffic to new components as they become ready. The old system shrinks as the new one grows, until eventually the legacy code is simply switched off.

This is not a theoretical exercise. It is the most reliable way to modernise a production system without putting your business at risk.

Where the Name Comes From

Martin Fowler coined the term in 2004, inspired by strangler fig trees he observed in Australia. These trees germinate in the canopy of a host tree, sending roots down to the ground. Over time the fig’s roots thicken, wrapping around the host trunk. Eventually the host tree dies and decomposes, leaving the strangler fig standing independently in the shape of its former host.

The metaphor is precise. Your new system grows around the old one, gradually taking over its responsibilities. The old system is never violently torn out. It simply becomes unnecessary.

Fowler’s insight was that software systems could be migrated the same way: incrementally, safely, and without the existential risk of a full rewrite.

Why Big-Bang Migrations Fail

Before diving into implementation, it is worth understanding exactly why the alternative — the big-bang rewrite — fails so consistently. This is not speculation. These failure modes show up in project after project across every industry.

Requirements drift. A rewrite takes months or years. During that time, the business keeps evolving. New features get added to the old system. Regulations change. Customer expectations shift. The specification you wrote at the start of the rewrite is stale by the time you finish building to it.

Hidden complexity. Legacy systems accumulate behaviour that nobody fully understands. Business rules get encoded in unexpected places. Edge cases get handled by code that looks pointless until you remove it and discover it was covering a scenario that only occurs on the last day of February in a leap year. A rewrite must reproduce all of this behaviour, and the team building the new system rarely has complete knowledge of what the old system actually does.

Sunk cost pressure. Once a rewrite is six months in, there is enormous organisational pressure to keep going, even when warning signs emerge. Nobody wants to admit that the eighteen-month timeline is actually going to be thirty months. So the team pushes forward, cutting corners, skipping tests, and building up exactly the kind of technical debt they were trying to escape.

All-or-nothing risk. A big-bang migration has a single point of failure: the cutover. Everything must work perfectly on day one, or you roll back to the old system and the project is seen as a failure. There is no middle ground.

The strangler fig pattern eliminates all four of these failure modes by breaking the migration into small, reversible, independently valuable steps.

How the Strangler Fig Works

The pattern has three phases that repeat for each component of the system you are migrating.

Phase 1: Intercept

Place a routing layer between your users and the legacy system. This could be an API gateway, a reverse proxy, a load balancer, or even application-level routing middleware. The critical requirement is that all requests pass through this layer before reaching any backend.

Initially, this layer does nothing except forward all traffic to the existing system. It is a pass-through. But it gives you the ability to selectively redirect specific requests to new implementations without changing anything about how users interact with the system.

Phase 2: Replace

Build the new implementation for a specific slice of functionality. This new component handles the same inputs and produces the same outputs as the corresponding part of the legacy system, but uses your target architecture, language, or platform.

Once the new component is ready and tested, update the routing layer to send relevant requests to the new implementation instead of the old one. You can do this gradually — starting with 1% of traffic, then 10%, then 50%, then 100% — validating behaviour at each step.

Phase 3: Retire

Once a piece of legacy functionality has been fully replaced and the new component is handling 100% of traffic with no issues, remove the old code. Delete it. Do not leave it in place “just in case.” Dead code is a liability, not a safety net.

These three phases repeat for each module, feature, or boundary you migrate. Over time, the legacy system handles less and less traffic until it serves no requests at all and can be decommissioned entirely.

Step-by-Step Implementation

Here is how to execute this pattern in practice.

Step 1: Map the Existing System’s Boundaries

Before you can strangle anything, you need to understand what you are working with. Map out the legacy system’s major boundaries: its APIs, its data stores, its integrations with other systems, and its user-facing surfaces.

You are looking for natural seams — places where the system can be divided into relatively independent pieces. Good candidates for seams include:

  • API endpoints that serve distinct business functions
  • Bounded contexts in the domain model (orders, inventory, users, billing)
  • Separate user interfaces or pages that operate independently
  • Integration points with external systems

Document the inputs and outputs at each seam. This becomes your contract — the specification your new implementation must satisfy.

Do not try to understand every line of code in the legacy system. That way lies paralysis. You need to understand the boundaries, not the internals. The whole point of the strangler fig is that you can treat the legacy system as a black box and replace it piece by piece.

Step 2: Identify the First Candidate Module

Choose your first migration target carefully. The ideal first candidate is:

  • Low risk. If something goes wrong, the impact is limited. Avoid starting with your payment processing pipeline.
  • Well-bounded. It has clear inputs, clear outputs, and minimal coupling to other parts of the system.
  • High value. Migrating it delivers visible business benefit — better performance, new capabilities, or reduced operational burden.
  • Representative. The lessons you learn migrating this module will apply to future modules.

A common good first choice is a read-only API endpoint or a reporting module. These tend to be well-bounded, low risk, and relatively easy to validate because you can compare outputs directly.

Step 3: Build the New Implementation Behind a Facade

Build the replacement component using your target architecture. It should conform to the same contract (inputs and outputs) as the legacy component it replaces.

Critically, deploy the new component alongside the old one. Both run simultaneously. The routing layer determines which one handles each request.

During this phase, you can use several techniques to build confidence:

  • Shadow traffic. Send copies of real requests to both the old and new implementations. Compare responses. Flag discrepancies for investigation.
  • Feature flags. Use feature flags to control which users or requests hit the new implementation. Start with internal users or a subset of customers.
  • Contract tests. Write automated tests that verify the new implementation produces identical outputs to the old one for a comprehensive set of inputs.

Step 4: Route Traffic Incrementally

Once you are confident in the new implementation, begin shifting traffic. A sensible progression:

  1. Internal traffic only — your team uses the new implementation
  2. 1-5% of external traffic — monitor error rates, latency, and business metrics
  3. 25% of traffic — watch for load-related issues
  4. 50% of traffic — the new system is handling real scale
  5. 100% of traffic — the legacy component is no longer serving requests

At each stage, compare the behaviour of the new implementation against the old one. If anything looks wrong, roll back by updating the routing layer. This is the beauty of the pattern: rollback is instant and painless.

Step 5: Retire the Old Module

Once the new implementation has been handling 100% of traffic for a sufficient period — typically two to four weeks — remove the old code. Update your routing layer to remove the conditional logic. Clean up any infrastructure that was supporting the old component.

Then pick the next module and repeat.

Real-World Considerations

The pattern is conceptually simple. The implementation has nuances that can trip you up if you are not prepared for them.

Data Migration

This is almost always the hardest part. If the old and new systems share a database, you have coupling that makes independent deployment difficult. If they use separate databases, you need a strategy for keeping data in sync during the migration period.

Common approaches include:

  • Change data capture (CDC) to replicate changes from the legacy database to the new one in near-real-time.
  • Event-driven synchronisation where writes to either system publish events that the other system consumes.
  • Read from new, write to both during the transition period, with a reconciliation process that detects and resolves conflicts.

There is no one-size-fits-all answer. The right approach depends on your consistency requirements, data volume, and tolerance for complexity. But plan for data migration from the start. It will take longer than you think.

Session and State Handling

If your system maintains user sessions or other server-side state, you need both the old and new implementations to share that state during the transition. Users should not notice when their requests are routed from one implementation to the other.

Solutions include externalising session state to a shared store (Redis is the common choice), using stateless authentication tokens (JWTs), or ensuring your routing layer uses sticky sessions so a given user consistently hits the same implementation.

Rollback Strategy

One of the pattern’s greatest strengths is that rollback is trivial — just change the routing. But this only works if you maintain rollback capability throughout the migration. That means:

  • Keeping the old implementation running and deployable until the migration is complete
  • Ensuring database changes are backwards-compatible (no dropping columns that the old system needs)
  • Testing rollback regularly, not just assuming it works

Monitoring and Observability

You need excellent observability during a strangler fig migration. At minimum:

  • Request-level tracing that shows which implementation handled each request
  • Error rate comparison between old and new implementations
  • Latency comparison at each traffic percentage
  • Business metric monitoring to catch subtle behavioural differences that do not manifest as errors

Invest in dashboards that let you see old versus new side by side. When something goes wrong during a migration, you need to know immediately whether the issue is in the new implementation or coincidental.

When NOT to Use the Strangler Fig

The strangler fig pattern is not universally applicable. There are situations where it adds complexity without sufficient benefit.

The system is small enough to rewrite quickly. If the entire legacy system can be rewritten and validated in a few weeks, the overhead of setting up routing, running parallel implementations, and managing incremental migration is not justified. Just rewrite it.

The system has no clear boundaries. Some legacy systems are so tightly coupled that there is no way to extract a piece without rewriting most of the system. If every component depends on every other component, the strangler fig pattern gives you all the complexity of an incremental migration with none of the benefits. In these cases, a rewrite may be the only viable path — but invest heavily in understanding the existing behaviour before you start.

The underlying data model is the problem. If the core issue is that the database schema is fundamentally wrong, migrating individual application components around it does not solve the real problem. You may need to tackle the data model first, which often requires a different approach.

You are changing the product, not the platform. The strangler fig is a technique for replacing how something works without changing what it does. If the business is fundamentally reimagining the product — different features, different user experience, different value proposition — then the discipline of matching legacy behaviour is counterproductive. Build the new thing as a new thing.

For more on making the rebuild-versus-migrate decision, see our guide on how to decide whether to rebuild or migrate.

Getting Started

If you are considering a legacy migration, the first step is understanding your current system well enough to identify the seams. Map the boundaries, assess the coupling, and find your first candidate module. The strangler fig pattern works because it lets you start small and prove the approach before committing to migrating the entire system.

The second step is investing in the routing layer. This is the infrastructure that makes incremental migration possible. Whether it is an API gateway, a reverse proxy, or application-level middleware, getting this right is essential.

The third step is building confidence. Shadow traffic, contract tests, and gradual traffic shifting all serve the same purpose: reducing risk to the point where migration becomes routine rather than terrifying.

We have helped multiple organisations execute strangler fig migrations across platforms ranging from monolithic .NET applications to legacy PHP systems. If you are looking for a technical partner to help plan and execute a migration, get in touch or learn more about our legacy modernisation services.