Modernizing Legacy Without Breaking the Business
Audit honestly, bias against rewrites, and ship modernization incrementally.
This is the play for a production system that generates revenue or is mission-critical and is aging under technical debt. The business depends on it today, which means the work is constrained in a way greenfield work is not: you have to keep the lights on while you change the wiring.
When to use this play#
Run this play only when all of these hold:
- The system is in production and is revenue- or mission-critical.
- The client owns or can modify the code.
- Leadership has budget for modernization, not just maintenance.
- There is genuine willingness to ship incrementally.
- You can get production access.
If leadership only wants to keep things limping along, or insists on a single dramatic relaunch, this is the wrong play.
How to run it#
1. Start with a time-boxed audit. Spend two to three weeks on an audit delivered as a standalone deliverable: a written report plus a 60-to-90-minute walkthrough, not a slide deck. The report covers:
- Code health.
- A reverse-engineered architecture diagram, built from the code as it actually is.
- Operational health: uptime, incident history, deploy cadence, lead time.
- A security gap analysis.
- An estimate of what the debt is costing.
- A risk register.
- A sequenced roadmap.
Sort everything you find into three honest buckets: bad-and-worth-replacing, bad-but-load-bearing, and fine. The middle bucket is the dangerous one, where code looks wrong but encodes hard-won knowledge.
2. Counter every rewrite request with an incremental proposal. Bias hard against rewrites. When a client asks for one, your first counter-proposal is always an incremental approach, usually Strangler Fig. A rewrite is a last resort with a narrow set of preconditions, not a default.
3. Read the code before you change it. Especially the parts you are sure are wrong. Surprising code is often the residue of a bug fix, a compliance rule, or an edge case nobody remembers documenting.
4. Follow the execution rules every iteration. These are non-negotiable:
- Working software every two weeks.
- Characterization tests first, always, so you can prove behavior is preserved.
- Observability lands early, not after the migration.
- Deployment automation is built in, not deferred.
5. Don't change two big things at once. Do not redo UX and architecture simultaneously. Hold one steady while you move the other, or you will not be able to tell which change broke what.
6. Pick the right sub-play. Reference the four modernization sub-plays and choose deliberately:
- Strangler Fig for incremental replacement behind a routing layer (the default).
- Lift and Shift to move healthy code onto new infrastructure.
- Branch by Abstraction to swap a subsystem in place.
- Rebuild from scratch only when its strict preconditions are all met.
Common traps#
- Setting a big-bang cutover date. A single switch-the-whole-thing-on day concentrates all the risk into one moment with no rollback worth the name.
- Modernizing what is interesting instead of what the business needs. The roadmap should follow the audit and the revenue, not the engineers' curiosity.
- Skipping the audit. Without it you are modernizing on assumptions, and the assumptions are usually wrong about exactly the load-bearing parts.
- Disengaging the original team. They hold the institutional knowledge that is not written down anywhere. Keep them engaged throughout.
Signals it's working#
- Working software ships every two weeks and the business keeps running.
- Characterization tests catch regressions before users do.
- Incidents trend down and deploy cadence trends up as observability and automation land.
- The roadmap is being executed in sequence rather than reshuffled around shiny problems.
How it ends#
This play ends when the audit's roadmap is delivered: the bad-and-worth-replacing code is replaced, the load-bearing code is understood and tested, and the operational metrics that motivated the work have moved. The client team should be more capable of carrying the system forward than they were when you started.