Strangler Fig Pattern: Safely Modernize Legacy Systems
Learn the strangler fig pattern for legacy modernization. A step-by-step guide to incrementally replacing old systems without risky big-bang rewrites.
📑 Table of Contents
What Is It?
The strangler fig pattern is named after tropical fig trees that wrap around host trees, eventually replacing them entirely while the host continues to live.
In software, it means building a new system around your old one, gradually migrating traffic until the old system can be safely removed.
It's the opposite of the "big bang rewrite"—where you build a completely new system and switch over all at once. Big bang rewrites fail constantly. The strangler pattern almost always succeeds.
| Big bang rewrite | Strangler pattern |
|---|---|
| All or nothing | Incremental progress |
| Value delivered at the end | Value delivered throughout |
| One chance to get it right | Many chances to course-correct |
| Team burns out | Team builds momentum |
When to Use It
The strangler pattern works best when:
- Your legacy system still runs and serves traffic
- You can put a routing layer in front of it
- You have time (months, not days)
- Business continuity is critical
It doesn't work well when:
- The system is so broken it can't run reliably
- You can't intercept requests
- The codebase is too small to justify the overhead
- Everything needs to change at once
For most legacy modernization projects, the strangler pattern is the right choice. We've used it successfully on systems ranging from 10-year-old PHP monoliths to enterprise Java applications.
How It Works
Step 1: Add a routing layer
Put something in front of your legacy system that can direct traffic. This could be an API gateway, a load balancer, or even just nginx.
At first, everything goes to the old system. The routing layer is just passing through.
Step 2: Identify the first slice
Pick a piece of functionality to modernize. Good first candidates:
- High business value but low complexity
- Well-defined boundaries
- Not tightly coupled to everything else
Authentication is often a good starting point. So is reporting/analytics.
Bad first candidates:
- The "core" of the system
- Anything touching payments until you're confident
- Components with unclear boundaries
Step 3: Build and shadow
Build the new version of that component. Don't route real traffic to it yet—instead, run it in parallel and compare results.
Send requests to both systems. Log any differences. This catches bugs before users see them.
Step 4: Gradually shift traffic
Start with 1% of traffic. Then 5%. Then 25%. Watch your metrics at each stage.
If something breaks, shift back to 0%. Fix it. Try again.
Step 5: Decommission the old component
Once 100% of traffic is on the new system and you're confident, remove the old code.
Step 6: Repeat
Pick the next slice. Do it again. Each iteration gets easier as your team learns and the remaining legacy surface shrinks.
A Real Example
A client came to us with a 15-year-old PHP system handling €2M/month in transactions. They'd tried two rewrites before—both failed.
Month 1: Routing layer + authentication
We added an API gateway and rebuilt user authentication as a separate service. Low risk, high visibility—everyone logs in, so quick wins show up immediately.
Month 2: Product search
The old search was painfully slow. We built a new service using Elasticsearch. Shadow-tested for a week, caught three edge cases, then rolled out.
Users noticed. Support tickets about "slow search" dropped to zero.
Month 3-4: Order processing
The scary part. We built the new order service but shadow-tested for almost three weeks before shifting any real traffic.
For two weeks, both systems processed orders and we compared every single transaction. When we found discrepancies (we found four), we fixed them before anyone was affected.
Month 5-6: Everything else
By this point, the remaining legacy code was mostly admin tools and reporting. Less critical, easier to migrate.
Final result:
The whole process took 6 months. At no point did the business stop operating. At no point did we have to tell customers "sorry, we're migrating."
Compare that to the two previous rewrite attempts—18 months each, neither shipped.
Common Mistakes
Moving too fast. The whole point is incremental progress. If you're trying to strangle half the system in the first month, you've just turned it into a big bang rewrite with extra steps.
Ignoring data migration. Two systems mean data in two places. You need a clear strategy for keeping data in sync during the transition.
Underinvesting in the routing layer. If your routing logic is fragile, everything is fragile. Make it robust, make it observable.
Skipping shadow testing. Yes, it takes extra time. Yes, it's worth it. The bugs you catch in shadow mode would have been production incidents.
Declaring victory too early. Don't remove old code until you're certain the new system handles all the edge cases. "It works in testing" isn't good enough.
Is it right for your project?
If you're struggling with a legacy system and a full rewrite feels too risky, the strangler pattern is probably your answer.
We've used it on dozens of projects. Happy to walk you through how it might apply to your specific situation.
📬 Get Engineering Insights
Practical articles on MVP development, legacy modernization, and building products that scale. Delivered to your inbox.
No spam. Unsubscribe anytime. We respect your privacy.
The Ordinary Company
Product-minded engineers helping startups build and scale. 50+ projects delivered.
Ready to Build Your Project?
Let's discuss how we can help bring your ideas to life. Free consultation, no strings attached.