The Strangler Fig Pattern: Escape Legacy Hell Without the Big-Bang Rewrite

Photo via Unsplash

Every engineering team has a version of the same horror story. The codebase was fine five years ago. Then features piled up, shortcuts were taken under deadline pressure, and the original developers left. Now every change is a negotiation with a system nobody fully understands.

The instinct is always the same: let’s just rewrite it.

And that instinct has killed more projects than the legacy code ever could.

What Is the Strangler Fig Pattern?

The Strangler Fig is named after a tropical plant that grows around a host tree, gradually replacing it while the original tree is still alive and functional. Martin Fowler introduced the concept in 2004, and it’s since become the gold standard for legacy migration.

The core idea: never shut down the old system to build the new one. Instead, you route specific requests or features to the new implementation while the legacy system handles everything else. Over time, the new system handles more and more traffic until the legacy code can be safely deleted.

Legacy code modernization — moving from old systems to new architecture
The goal: new architecture runs alongside the old until it's safe to decommission
73%
Rewrite failure rate
Standish CHAOS Report
3–5×
Longer than estimated
Typical rewrite timeline
0
Downtime required
With Strangler Fig
30d
To first win
Our typical engagement

Why Full Rewrites Fail (Again and Again)

Before diving into implementation, it’s worth understanding the failure modes. Rewrites fail for predictable reasons:

You underestimate what the old system actually does. Legacy systems accumulate decades of business logic — much of it undocumented, some of it in comments, some of it known only to employees who left years ago. When you start fresh, you discover this knowledge gap the hard way: in production, when something breaks that nobody thought to replicate.

The target keeps moving. While your team spends 18 months building the new system, the business keeps running on the old one. New features get added to the legacy system because they have to be. Now you have two moving targets.

You can’t test parity. Even with comprehensive test suites, proving that the new system behaves identically to the old one under all conditions is functionally impossible. There are always edge cases.

The Four-Layer Strangler Fig Architecture

A production-grade Strangler Fig migration typically involves four layers working in concert:

01
Facade / API Gateway Layer
A routing layer that intercepts all traffic and decides whether to send it to the legacy system or the new one. This is the key enabler — without it, you can't do incremental migration. AWS API Gateway, Kong, or a simple Nginx config all work here.
02
Feature-by-Feature Migration
Identify the clearest boundary in the system — often CRUD operations for a single domain (users, orders, products). Build the new implementation for that domain in isolation. Add tests. Route traffic to it. Validate.
03
Data Synchronization
During parallel operation, both systems may need access to the same data. Design an event-driven sync mechanism so changes in either system propagate. Event sourcing or dual-writes with change-data capture (CDC) are common approaches.
04
Decommissioning
Once a module has been running successfully in the new system for a defined period (we typically use 30 days of production traffic), you remove the routing for it from the legacy system and delete the old code. This is genuinely satisfying.

Legacy vs. Strangler Fig vs. Rewrite: The Real Comparison

Factor Keep Legacy Strangler Fig Big-Bang Rewrite
Business continuity ✅ No disruption ✅ No disruption ❌ Long freeze
Risk level 🟡 Grows over time 🟢 Low, incremental 🔴 Very high
Time to first value 🟡 Immediate but diminishing 🟢 Weeks 🔴 12–24 months
Knowledge preservation ✅ Implicit ✅ Explicit via tests ❌ Often lost
Team morale 🔴 Degrades 🟢 Improves steadily 🟡 Drops during crunch
Cost predictability 🟡 Reactive 🟢 Phased and known 🔴 Almost always over budget

Based on patterns observed across legacy modernization engagements

The Migration Sequence That Works

After running multiple Strangler Fig migrations, we’ve converged on a sequence that minimizes surprises:

  1. Characterization testing first. Before touching anything, write tests that document the current behavior of the system — including the bugs. These aren’t tests of what the system should do, they’re tests of what it does do. Michael Feathers calls these “characterization tests” in Working Effectively with Legacy Code.

  2. Instrument the legacy system. Add logging and observability to understand actual usage patterns. You’ll discover that 40% of the code paths are never hit in production. That changes the migration priority order significantly.

  3. Identify the seams. A “seam” is a boundary where you can change behavior without modifying surrounding code. These are your migration insertion points. In a monolith, seams are often service class boundaries or database table groups.

  4. Build the facade, then migrate module by module. Never migrate two modules at the same time. The overhead of managing parallel bugs in two active migration streams is brutal.

System architecture diagram — routing between legacy and modern systems
The routing layer is the critical piece. It gives you control without commitment.

Common Mistakes We See (And How to Avoid Them)

Mistake: Migrating the database too early. The database is the hardest thing to migrate and the most dangerous to get wrong. We almost always recommend migrating the application layer first and keeping the same database, then migrating the data model as a separate phase.

Mistake: Skipping the parallel-run phase. It’s tempting to flip the switch as soon as the new module passes tests. Don’t. Run new and old in parallel, compare outputs, and only switch over after a defined validation period.

Mistake: Treating it as a purely technical project. Strangler Fig migrations succeed when the business understands and supports the approach. If product teams keep adding to the legacy system because “the new one isn’t ready yet,” you end up chasing a moving target.

Conclusion

The Strangler Fig pattern isn’t glamorous. You won’t get to throw everything away and start fresh. But it consistently delivers what rewrites promise and rarely deliver: a modernized system, running in production, without the 18-month nightmare.

The pattern works because it respects a fundamental truth about software: running systems contain knowledge that isn’t written anywhere else. The goal is to extract and preserve that knowledge, not discard it.

Start small. Start with the seam that’s clearest. Get one module into the new system, learn from it, and use that momentum to tackle the next one.

Free Strategy Session

Want Expert Guidance on Your Project?

Book a free 1-hour session. We'll apply these principles directly to your architecture, codebase, or team challenge.

Free 1-hour strategy session

Walk away with a clear, prioritised action plan — no pitch.

Book free session