Yashveer Singh
Connect
<- All posts
Tech Debt and Refactoring12 min read

The Strangler Fig Pattern: Replacing Legacy in Stages

The strangler fig pattern replaces a legacy system incrementally by routing traffic surface by surface to a new system that grows around the old one. The old system continues running until enough surfaces have been migrated that it can be retired. I use this pattern on every large migration where a big bang cutover would be too risky and a direct rewrite would take longer than the business can sustain.

Written by Yashveer Singh, founder of Yashveer Labs.

What you actually need to know

  • The strangler fig is the right default for any meaningful legacy migration. Big bang is the exception.
  • Traffic routing is the mechanism. The new system intercepts requests for migrated surfaces. The old system handles the rest.
  • Start with the safest surfaces, not the most important ones. Build confidence before moving high-stakes traffic.
  • The seventy percent wall is real. The last thirty percent is harder and slower than the first seventy.
  • Set a retirement date for the old system at the start. Without a date, the old system runs forever.
Migration approachRisk levelRollback easeWhen to use
Big bang cutoverHighHardSmall, well-tested systems only
Strangler fig, surface by surfaceLowEasyMost production migrations
Branch by abstractionLowEasyMigrations within a single codebase
Parallel run with reconciliationVery lowEasyCritical systems with zero tolerance for divergence

The core argument

The strangler fig pattern exists because software systems almost always contain undocumented behaviors. The legacy system the team wants to replace has been in production for years. It handles edge cases that nobody wrote down. It has integrations that were added in a hurry and never documented. It serves data in formats that downstream consumers depend on and nobody mapped.

A big bang migration reveals all of this at once, at 2 am, after the cutover. The team scrambles to figure out which behavior changed, which integration broke, which edge case the new system does not handle. The rollback, if there is one, is painful.

The strangler fig reveals these problems one at a time. Each surface migration is a small, contained change. The old system is still running and handling everything the new system has not yet taken over. When a problem surfaces on a migrated path, the fix is small and the blast radius is limited. The team learns the real behavior of the old system while building the new one.

The cost of this approach is time and complexity. The team operates two systems for months. The routing layer, the proxy or gateway that decides which system handles each request, adds a new piece of infrastructure to maintain. The data story, especially if the two systems have different models, requires careful dual-write management. These costs are real. They are worth paying against the alternative of a high-stakes cutover.

Running the migration

Step one: build the routing layer

Before any traffic shifts, the routing mechanism needs to be in place. An API gateway, a feature flag system, or a proxy layer that can direct traffic for a given path or surface to either the old system or the new one. The routing layer is not glamorous. It is the piece that makes the whole pattern work, and it needs to be solid before migration begins.

Step two: pick the first surface

The first surface should be boring. Low traffic, well understood, ideally with test coverage. The goal of the first migration is to prove that the pattern works in your environment, not to move the needle on coverage. Pick something where being wrong is recoverable.

Step three: migrate, validate, move on

Each surface follows the same cycle. Build the new implementation. Write tests that verify it matches the old behavior. Route a small percentage of traffic to the new implementation. Monitor for divergence. Increase the traffic percentage. When the new implementation is handling one hundred percent of the traffic for that surface, mark the surface done and move to the next.

The monitoring step is not optional. The strangler fig works because divergence is caught early and contained. A migration that moves traffic without watching the metrics is not a gradual migration. It is a slow big bang.

Step four: manage the seventy percent wall

Plan for the point where seventy percent of surfaces are migrated. At that point, the old system still handles the most complex and least documented behaviors. The team has been on the migration for months. The motivation is lower. The work is harder. Address this explicitly in the migration plan. Reserve dedicated time and energy for the final phase. Set a date for retirement that creates urgency.

How long does it take

System sizeMigration durationRecommended pace
Small service, 5-10 surfaces4-8 weeksFull team focus
Medium SaaS, 20-40 surfaces3-6 months20-30% of team capacity
Large application, 50+ surfaces6-18 monthsDedicated migration track
Monolith to services12-36 monthsLong-running program

The pace matters as much as the duration. Migrations that absorb more than thirty percent of engineering capacity stall the product and exhaust the team. Migrations that absorb less than fifteen percent rarely finish.

What a good strangler fig migration requires

  • A routing layer that can direct traffic per surface with no downtime changes.
  • Monitoring that detects divergence between the old and new systems in real time.
  • Test coverage for each surface before its traffic shifts, not after.
  • A documented inventory of every surface in the old system at the start. Surprises in the final phase are what kill migrations.
  • A retirement date for the old system set at the beginning, not the end.
  • A communication plan for internal teams and, where relevant, for external integrators.
  • A named owner for the migration who is accountable for the retirement date.

Expert opinion

The strangler fig is the only migration pattern I have seen succeed consistently on systems above a certain size. The teams that try to big bang a complex legacy migration do not succeed more often than the teams that use the strangler fig. They just fail faster. The pattern requires patience. The patience is the thing that makes it work.

>

Yashveer Singh, founder of Yashveer Labs

How this played out on a real project

A five-year-old monolith needed to be decomposed into services. The team had tried a big bang approach eighteen months earlier and abandoned it after four months when they realized the scope was impossible to manage. They came to me with a codebase that was now two years further along and more entangled.

We started with the strangler fig and the inventory. Before writing a line of new code, we ran a two-day tech debt audit to map the forty-seven surfaces and rank them by risk. We built a simple routing layer using the existing API gateway. The first migration was the public status page, which was low-traffic and essentially static. It took three days. It proved the routing layer worked.

Over seven months, we migrated thirty-one of the forty-seven surfaces to independent services. The remaining sixteen were the complex, poorly documented core. Those took another four months. The old monolith was retired eleven months after the migration started, on the date we had set at the beginning.

Three issues surfaced during the migration that would have been catastrophic in a big bang. All three were contained because the old system was still running when they appeared.

For teams deciding between this approach and a full rewrite, when to refactor and when to rewrite covers the decision framework. For the broader migration strategy question, big bang vs gradual migration: a decision map is the companion read.

Common mistakes teams make

  1. Building the routing layer after starting the first surface migration. The routing layer comes first.
  2. Starting with the most important surface to demonstrate business value. Start with the safest surface to build confidence.
  3. Not monitoring for divergence actively. Silent divergence is the most dangerous failure mode.
  4. No inventory of surfaces at the start. Discovery surprises in the final phase stall the migration.
  5. Letting the seventy percent wall derail the timeline. Plan for it explicitly.
  6. No retirement date for the old system. Without a date, the old system runs indefinitely after the migration is nominally complete.
  7. Running the migration with a separate team. The team that owns the old system has to own the new one.
  8. Skipping test coverage before shifting traffic. Problems in untested surfaces are invisible until production.

A migration kickoff plan

  1. Week one. Build the routing layer. Test it in staging. Do not touch any production traffic yet.
  2. Week two. Complete the surface inventory. Rank surfaces from safest to riskiest. Set the retirement date for the old system.
  3. Week three. Migrate the first surface. Low traffic, well understood. Validate for one week before moving on.
  4. Weeks four and beyond. Work through the ranked list. One to three surfaces per week depending on complexity. Monitor divergence at each step.
  5. Final phase. Reserve dedicated time for the last thirty percent. Do not run this phase alongside a busy product sprint.
  6. Retirement. Remove the routing layer. Delete the old code. Document the lessons.

For teams working on the test coverage that should accompany each surface migration, adding tests to a legacy codebase without going mad covers the practical approach in detail.

FAQ

Frequently asked

Author

Why you should hire Yashveer Singh for this

The kind of work this article describes is the kind of work I do every week. Production deployments, scaling decisions, the architecture choices that compound over years. I am Yashveer Singh, founder of Yashveer Labs. If you need this done, I do not need to be sold on the brief. Send me what you have and I will tell you what it actually takes.

Related reading