Case Studies Advanced 5 min read

Legacy Modernisation: Strangler Fig in Practice

How to modernise a legacy system without a big-bang rewrite. Strangler Fig pattern, route-by-route migration, and the ladder from old to new across 6-12 months.

Table of contents
  1. When does modernisation actually pay back?
  2. What is the cost of letting legacy stay?
  3. What does the Strangler Fig migration look like?
  4. What is the typical 12-month migration timeline?
  5. What does the migration tracking artifact look like?
  6. How does this scale to multi-team?
  7. What failure modes does modernisation introduce?
  8. When is modernisation the wrong answer?
  9. Where should you go from here?

The system that has run production for ten years has earned the right to retirement, not the right to a 6-month rewrite that fails. This case study shows the Strangler Fig migration that ships value each quarter, the artifacts that make the migration honest, and the traps that derail modernisation projects.

When does modernisation actually pay back?

Three signals.

Maintenance cost is rising. Bug fixes take longer; new hires take longer to ramp; tooling around the legacy is ageing out (old framework, old language version, vendor deprecating).

New features blocked. "We can't add this without refactoring foundation X" said for the third time. The legacy is no longer just slow to maintain - it actively prevents business outcomes.

Hiring difficulty. Engineers do not want to work on COBOL/old-PHP/legacy-stack. Attrition rises; replacement is hard. The cost of legacy includes the cost of not being able to staff it.

If maintenance is fine, hiring is fine, and new features ship, modernisation might be a vanity project. Run the numbers.

What is the cost of letting legacy stay?

Three slow-burn failure modes.

Compounding cost. Each year the legacy is harder than the last to change. By year 5 of "we'll modernise next year", the cost has doubled.

Talent flight. The engineers who could have led modernisation leave because no one is doing it. Now the legacy has fewer experts and is harder to migrate.

Strategic vulnerability. Competitors with modern stacks ship features faster. The legacy that was once a competitive advantage is now the moat preventing competition.

What does the Strangler Fig migration look like?

flowchart TB
    Client[Client] --> Proxy[Routing layer]
    Proxy -->|Route A| New[New system<br/>built alongside]
    Proxy -->|Route B and others| Old[Legacy system<br/>still serves]
    New --> NewDB[(New database)]
    Old --> OldDB[(Legacy database)]
    NewDB -.sync from.- OldDB

A routing layer (reverse proxy, feature flag, API gateway) sends some routes to new code and others to legacy. Each quarter, more routes move. Eventually legacy serves nothing and is retired.

What is the typical 12-month migration timeline?

gantt
    title Strangler Fig 12-Month Plan
    dateFormat YYYY-MM-DD
    section Q1 Foundations
    Routing layer + observability   :a1, 2026-07-01, 30d
    First route migrated             :a2, after a1, 30d
    section Q2 Read paths
    Read-heavy routes (lower risk)   :b1, 2026-10-01, 60d
    Read replica from legacy DB      :b2, 2026-10-01, 30d
    section Q3 Write paths
    Write paths with dual-write      :c1, 2027-01-01, 60d
    Validate consistency             :c2, after c1, 30d
    section Q4 Retirement
    Final routes migrated            :d1, 2027-04-01, 60d
    Legacy decommissioning           :d2, after d1, 30d
    Post-mortem + handover           :d3, after d2, 30d

Quarter 1 builds the migration infrastructure (routing, observability, parallel data store). Quarter 2 migrates read-heavy routes (low risk). Quarter 3 migrates write paths with dual-write for safety. Quarter 4 retires legacy.

What does the migration tracking artifact look like?

# Modernisation Tracker — {{ System Name }}

## Goal
Replace legacy {{ X }} with new {{ Y }} by 2027-06-30.

## Migration progress
| Route | Traffic Old | Traffic New | Status | Notes |
|-------|-------------|-------------|--------|-------|
| GET /products | 0% | 100% | Migrated | Stable since Q2 |
| GET /products/:id | 0% | 100% | Migrated | Stable since Q2 |
| POST /orders | 90% | 10% | In progress | 10% canary; expand Q3 |
| GET /reports | 100% | 0% | Not started | Q4 target |
| ... | | | | |

## KPIs
- Traffic on new: 35% (target Q3 end: 60%)
- Incident rate on legacy: 0.3/month (was 0.4)
- New-feature time-to-ship: 2 wks (was 4 wks for legacy team)

## Active risks (RAID)
- R1: Dual-write consistency edge case discovered in Q2
- R2: One legacy report has no documented schema

## Decommissioning checklist
- [ ] All routes at 0% legacy traffic
- [ ] Legacy DB last-modified > 30 days
- [ ] Legacy code archived to read-only repo
- [ ] On-call rotation removes legacy
- [ ] Servers turned off
- [ ] Cost savings reported to sponsor

The tracker becomes the status report attachment for sponsor reviews. Visible quarterly progress is the only way modernisation survives leadership changes.

How does this scale to multi-team?

flowchart TB
    Mod[Modernisation team<br/>owns routing + new system] --> Team1[Product Team A<br/>uses new system]
    Mod --> Team2[Product Team B<br/>migrates own routes]
    Mod --> Team3[Product Team C<br/>contributes domain]
    Mod --> Sponsor[Quarterly sponsor review]

A dedicated modernisation team owns the migration; product teams contribute their domain expertise. The modernisation team's RACI shows Accountable for migration, Consulted by product teams.

What failure modes does modernisation introduce?

When is modernisation the wrong answer?

Two cases.

Legacy actually fine. A boring system that works well, has low maintenance cost, and supports business priorities does not need modernisation. Reformatting a CSS file once a year is not enough reason.

No team available. If you cannot dedicate a team, do not start modernisation. Half-team modernisation drags forever and delivers nothing.

The migration earns its 12-18 months when the legacy actively costs the business money. Below that, leave it alone.

Where should you go from here?

Last case study: vendor managed project - the case where most of the work is outside your team. After that, the meta closing chapters (checklist, conclusion) wrap the series.

Frequently asked questions

What is Strangler Fig and why does it work?
From Martin Fowler. Named after a tree that grows around an existing tree, slowly replacing it. In software: build the new system alongside the old, route specific functionality from old to new, retire old paths once new ones are stable. Works because each migration step is small, reversible, and ships value before the whole thing is done.
Should we just rewrite from scratch?
Almost never. Big-bang rewrites fail because they take longer than expected, cannot ship until done, and leave the team with two systems to maintain in parallel. The historical evidence is grim - Netscape 4 to 6, Microsoft Project rewrite, etc. Strangler Fig ships incrementally and lets you stop if priorities change. The database choice chapter covers the storage migration ladder.
How do I sell modernisation to a sponsor?
In their currency: maintenance cost, hiring difficulty, time-to-market for new features. 'Adding a new field takes 3 weeks because of legacy testing'. Quantify pain with examples; project savings from migration. Avoid 'modernisation' as the value pitch - sponsors hear 'engineering vanity project'. Sell business outcomes.
What metrics tell me modernisation is working?
Three numbers: percentage of traffic on new system (rises each quarter), incident rate on legacy (should not rise as new takes over), and time-to-ship-new-feature (should fall on the new path). The observability chapter covers the metrics; track them in status reports.