The Strangler Fig Pattern: Migrating Legacy Systems Incrementally
Implementing the strangler fig pattern for legacy migration with request routing, data synchronization, feature parity verification, and a realistic migration timeline.
Akhil Sharma
March 26, 2026
The Strangler Fig Pattern: Migrating Legacy Systems Incrementally
The strangler fig tree grows around an existing tree, gradually replacing it while using it for structural support. Eventually, the host tree dies and the fig stands on its own. This is the best metaphor for migrating legacy systems — you build the replacement around the existing system, incrementally routing traffic from old to new, until the legacy system can be decommissioned.
The alternative — a full rewrite that launches on a single cutover date — fails more often than it succeeds. Big-bang rewrites take years, miss edge cases, and collapse under the weight of requirements that were "obvious" in the old system but undocumented.
The Pattern
Step 1: Install the Proxy
Before writing any new code, insert a reverse proxy in front of the legacy system. Initially, it passes all traffic through unchanged. This is the foundation — every subsequent migration step is a routing change in the proxy.
Key principle: The proxy addition should be invisible to users. Deploy it, verify that all traffic flows correctly, and only then start migrating routes.
Step 2: Choose What to Migrate First
Not all features are equal. Pick the first migration target based on:
- Low risk, high learning. A read-only endpoint or a simple CRUD feature teaches you the migration process without risking critical business logic.
- Well-understood behavior. You need to know exactly what the legacy system does. If the behavior is undocumented and full of edge cases, save it for later.
- Minimal data dependencies. Features that read from a single database table are easier than features that join across 10 tables.
- Clear boundaries. A self-contained feature (user profile management) is easier to extract than one that's deeply intertwined with everything else (order processing).
A practical first migration target: user authentication, user profile endpoints, or a standalone reporting feature.
Step 3: Data Synchronization
The legacy and new systems need access to the same data during the migration period. There are several approaches:
Shared Database (simplest)
Both systems read from the same database. The new system uses the legacy schema.
Advantage: No data sync needed. Both systems see the same data instantly.
Advanced System Design Cohort
We build this end-to-end in the cohort.
Live sessions, real systems, your questions answered in real time. Next cohort starts 2nd July 2026 — 20 seats.
Reserve your spot →Disadvantage: The new system is constrained by the legacy schema. You're building new code on old data models. Acceptable for the migration period, but plan to migrate the schema after the legacy system is gone.
Change Data Capture (CDC)
The legacy database's changes are streamed to the new system's database. Both databases stay in sync with minimal latency.
Advantage: New system has its own schema. Data models can evolve independently.
Disadvantage: CDC adds latency (100-500ms) and operational complexity (Debezium, Kafka). Bidirectional sync (if both systems write) is significantly more complex.
Dual Writes (use with caution)
The new system writes to both the new and legacy databases. This keeps legacy data fresh for features that haven't been migrated yet.
Warning: Dual writes are fragile. If one write succeeds and the other fails, databases diverge. Use an outbox pattern or accept eventual consistency. Only use dual writes for the migration period — remove them as soon as the legacy system no longer reads the data.
Step 4: Feature Parity Verification
Before routing production traffic to the new system, verify that it produces the same results as the legacy system. Run both in parallel and compare outputs.
Run shadow testing for 1-2 weeks. Investigate every mismatch. Common causes: date formatting differences, field ordering in JSON, rounding differences, and undocumented legacy behaviors.
Step 5: Gradual Traffic Migration
Once shadow testing shows < 0.1% mismatch rate, start routing real traffic:
Use the proxy for traffic splitting:
Realistic Migration Timeline
A medium-sized legacy system migration (20-30 features, 50K lines of legacy code):
This is ambitious. Most large migrations take 12-24 months. The key is continuous progress — shipping migrated features every 2-3 weeks keeps momentum and shows stakeholders that the migration is working.
Common Mistakes
1. Trying to improve while migrating. Resist the urge to redesign features during migration. First, achieve parity. Then improve. Combining migration with enhancement makes both harder and doubles the risk.
2. No rollback plan. Every routing change should be reversible. If the new system has a bug, flip the proxy back to legacy within minutes. Keep the legacy system running and maintained until you're fully confident.
3. Underestimating edge cases. Legacy systems accumulate years of special cases, workarounds, and undocumented behaviors. Budget 30-40% of your migration time for discovering and handling these.
4. Stopping too early. The last 10% of features is 50% of the work. Don't declare victory when 90% of traffic is migrated — the remaining 10% often includes the most complex, least understood features.
5. Not decommissioning. After the migration, actually shut down the legacy system. Teams that leave it running "just in case" end up maintaining it indefinitely. Set a hard decommission date and stick to it.
The strangler fig pattern works because it reduces risk through incremental progress. You never have a big-bang moment where everything changes at once. Each step is small, verifiable, and reversible. The legacy system keeps running until the new system has proven it can handle everything. That's not just good engineering — it's good risk management.
More in Architecture
Designing Data Pipeline Architecture for Real-Time Analytics
Real-time data pipeline design covering Lambda vs Kappa architecture, stream processing with Kafka Streams and Flink, and handling late-arriving data.
Zero-Downtime Deployments: Blue-Green, Canary, and Rolling Strategies
Deployment strategies for zero downtime with Kubernetes examples, database migration patterns, feature flags, and rollback procedures.