The fear that stops most modernisation is reasonable: the system you want to change is the system the business runs on. Touch it wrong and orders stop, invoices break, customers notice. So the work gets deferred, the system gets older, and the eventual change gets riskier. The good news is that the software industry solved this problem years ago. There is a well-established way to replace a live system piece by piece, with the lights on, and a way to undo any single step that misbehaves. None of it is exotic; all of it is plain once you see the shape.
Every technique below is a version of the same principle: make the change small, make it reversible, and verify it before you rely on it. That principle is the whole game. The named patterns are just disciplined ways of applying it to different situations.
Strangler-fig: grow the new around the old
The strangler-fig is named after a rainforest plant that grows around a host tree and gradually takes its place. Applied to software, you put a thin layer in front of the old system that decides where each request goes. At first, everything still goes to the old system. Then you build one capability in the new system, and quietly redirect just that capability’s requests to it. The old system keeps handling everything else. Over time you move capability after capability across, until the old system is handling nothing and can be switched off. At no point is there a dramatic switch-over — the change happens one slice at a time.
Like the fig, it begins with small additions, often new features, that are built on top of, yet separate to, the legacy code base.
Branch-by-abstraction: for the parts buried inside
The strangler-fig works beautifully when a request comes in from outside and you can redirect it. But some of the most important behaviour lives deep inside the system, where no incoming request maps to it cleanly. For those, you use branch-by-abstraction. In plain terms: you put a clean dividing line — an internal "switch" — between the part that uses a piece of functionality and the piece itself. Behind that switch you build the new implementation alongside the old. When the new one is ready and proven, you flip the switch. The system keeps building, running and releasing the entire time, with no long-lived parallel version of the code drifting out of date.
Parallel running: prove it before you trust it
Before you let a new piece take over, you want evidence that it behaves exactly like the old one — not a promise, evidence. Parallel running gives you that. For a period, you send each real request to both the old and the new implementation. The old one’s answer is the one you actually use; the new one’s answer is recorded and compared. If they ever disagree, you have found a difference in the safe place — in a comparison, not in front of a customer. Only once the new path matches the old across real traffic do you let it take over.
Feature flags: separate shipping from switching on
The thread tying these together is a simple, powerful separation: deploying a change is not the same as turning it on. A feature flag is a switch in configuration that decides whether a new piece of behaviour is active. You can put new code into production with its flag off — present, but doing nothing — then turn it on for a small group, watch, and widen it gradually. If anything goes wrong, you turn the flag off. No emergency redeploy, no panic. The change is reversible by flipping a setting.
I can deploy something without releasing it.
The safety net underneath all of it
There is one foundation that makes every technique above trustworthy: tests that capture what the current system actually does. Most ageing systems have few or none, and that absence — not the age of the code — is what makes them frightening to change.
Legacy code is code without tests.
The move is to write what are called characterisation tests — tests that record the current behaviour exactly as it is, warts and all, before you change anything. They are not a judgement about whether the behaviour is right; they are a recording of what is true today. Once that recording exists, you can change the code underneath it and instantly see if anything shifted. The test is how you ground your knowledge of a system you no longer fully understand — and it is what lets a person, or an AI tool, refactor safely instead of guessing.
That last point is worth being honest about, because it is where AI now enters the picture. AI tools genuinely help with the laborious parts of this work — reading an undocumented codebase, drafting the safety-net tests, proposing the new implementation behind the abstraction. But they produce code that is plausible, not guaranteed correct, and an experienced engineer keeps the architectural judgement and treats the equivalence tests as the source of truth. AI accelerates the mechanical work inside these guardrails; it does not remove the need for them. Used that way it is a method, not a shortcut — the discipline above is exactly what stops fast generation from becoming fast breakage.
Frequently asked
- Will modernising mean downtime for the business?
- It should not. The entire point of techniques like strangler-fig, parallel running and feature flags is that the existing system keeps running and serving customers while change happens around it, one piece at a time. Each step is small and reversible, so there is no high-risk "go-live" moment where everything switches at once.
- How is this different from a rewrite?
- A rewrite builds a whole replacement separately and switches over once — a single, large, all-or-nothing bet. This approach changes the live system gradually, moving one capability at a time and verifying each move before the next. You always have a working system, and you can stop or reverse at any point.
- What if our old system has no tests and no documentation?
- That is the normal starting point, not a blocker. The first step is to write characterisation tests that pin down what the system currently does, then change it safely under that net. You do not need to understand every line up front — you need a way to detect when behaviour changes, and tests give you that.
- How do you use AI safely in this?
- As an accelerator inside the guardrails, never as the decision-maker. AI helps read undocumented code, draft tests and propose changes; a human keeps the architectural judgement and the equivalence tests decide what is correct. The discipline of small, reversible, verified steps is precisely what keeps AI-generated change honest.
The honest takeaway is that "modernise without breaking the business" is not a slogan — it is a known engineering practice with a name for each move. The hard part is sequencing it for your specific estate: which capability to move first, where the buried behaviour lives, what to pin with tests before you touch it. Our free Software Health Check is built to produce that sequence — a safe, ordered plan for your systems — at /application-modernisation/free-assessment.