How to Apply the Strangler Fig Pattern to a Live Legacy System
A practical how-to guide on using the strangler fig pattern to migrate a legacy system incrementally without downtime or a risky big-bang cutover.
Your legacy system is slowing you down, but you cannot afford to stop the business while you rebuild it. A big-bang rewrite sounds appealing until you see the failure rate — most multi-year rewrites either get cancelled, go massively over budget, or ship something worse than what they replaced. The strangler fig pattern exists precisely because the industry learned this lesson the hard way.
This guide walks you through how to apply the strangler fig pattern to a real production system, step by step.
What the Strangler Fig Pattern Actually Solves
The strangler fig pattern is named after a tropical plant that grows around an existing tree, eventually replacing it. In software terms: you build a new system alongside the old one, gradually route traffic to the new system piece by piece, and retire the legacy component only after the new one has proven itself in production.
The key property is that the old system stays live and serves real users throughout the entire migration. There is no freeze on new work. There is no moment where everything has to cut over simultaneously.
Phase 1: Map the Seams in Your Existing System
Before writing a single line of new code, you need to understand where the legacy system can be split.
Start with a capability audit:
- List every distinct business capability the system provides (authentication, billing, notifications, reporting, etc.)
- Identify which capabilities are called by external consumers versus internal
- Find the natural integration points — HTTP endpoints, database tables, shared queues, file drops
The capabilities with clearly defined inputs and outputs and low coupling to the rest of the system are your starting targets. These are the seams where you will insert your facade layer.
Avoid starting with the most business-critical flows. Prove the pattern on something peripheral first.
Phase 2: Insert a Facade Layer
The facade (often a reverse proxy or API gateway) is what makes the pattern work. All traffic enters through the facade. The facade decides whether to route a request to the old system or the new one.
Common facade choices:
- NGINX or HAProxy for HTTP traffic routing by path or header
- AWS API Gateway or Kong for more sophisticated routing logic
- A thin routing service you own, if you need custom logic
Set the facade up so that initially 100% of traffic goes to the legacy system. The facade is invisible to users at this point. This is your zero-risk starting position.
Phase 3: Extract and Replace One Capability at a Time
Pick your first migration target — the low-risk peripheral capability you identified in Phase 1.
- Build the new service independently, backed by its own datastore
- Write a data backfill to populate the new service with the legacy data it needs
- Run both services in parallel and compare outputs (dark launch or shadow mode)
- Once outputs match and confidence is high, shift a small percentage of traffic to the new service via the facade
- Monitor error rates, latency, and data correctness
- Increase traffic incrementally until the new service handles 100%
- Remove the legacy code path for that capability
Repeat for the next capability.
Managing Data During the Transition
The hardest part of the strangler pattern is data, not code. During any period where both systems are live, you may need dual writes — operations write to both the legacy database and the new service's database simultaneously. Event-driven synchronization via a message queue is often cleaner for this.
Write your retirement criteria before you start, not after. Define what "done" means for each capability: what metrics have to hold, for how long, before you can decommission the legacy path.
Phase 4: Define and Track Retirement Criteria
Without explicit retirement criteria, the old system survives indefinitely. Teams end up maintaining two systems that do the same thing, and the migration never actually completes.
For each extracted capability, define:
- Minimum traffic percentage handled by the new service before decommission
- Error rate threshold that must be met for at least N days
- A rollback window after which the legacy code path is removed
Put these criteria in your project tracker as acceptance conditions, not aspirational notes.
What to Watch Out For
Scope creep on the new service. The strangler migration is not an invitation to redesign everything at once. Build the new service to match the existing behavior first. Improve it after the migration is complete.
Shared database coupling. If the legacy system and new services share the same database tables, you have not actually decoupled anything. Data ownership has to move with the service.
Routing complexity growth. The facade routing rules can become a maintenance burden over time. Keep them declarative and version-controlled.
When the Strangler Pattern is the Wrong Choice
The pattern works best when the legacy system has identifiable boundaries and some modularity, even if that modularity is informal. If the entire system is a single mass of tightly coupled code with no distinguishable capabilities, you may need to do some internal decomposition work before the pattern can take hold.
It is also a poor fit for very small systems where a clean rewrite would take less effort than instrumenting the migration apparatus.
If your team is facing a legacy migration and needs a realistic plan — not a whiteboard exercise — talk to Clixo. We have executed incremental migrations on production systems and can help you scope the work honestly.