Every legacy modernisation programme runs into the same uncomfortable question sooner or later: how do you prove a new system works correctly, when nobody can fully define what "correctly" means for the one it's replacing? The specification lives in code written years ago, in the muscle memory of staff who've long since moved on, and in workarounds nobody documented because they were never meant to be permanent.
The scale of the risk this creates is well established. The Standish Group's CHAOS Report, drawing on a database of more than 25,000 projects, found that only 53% of legacy modernisation projects met their definition of success: delivered on time, on budget, with a satisfactory result. The remaining 47% were challenged or failed outright. Industry analysis of ERP modernisation specifically shows a similarly stark picture. Gartner predicts that by 2027, more than 70% of recently implemented ERP initiatives will fail to fully meet their original business goals, with as many as 25% failing outright.
Every gap in testing strategy surfaces later as a cost somewhere. It might be rework, when a defect discovered post-launch has to be fixed under pressure; compliance exposure, when a regulator or auditor asks what changed and nobody can answer with evidence; or a delayed go-live, when a critical discrepancy is found too late to fix without pushing the release date. Each of these lands on a desk as a budget overrun, a reputational risk, or both. A deliberate testing strategy converts that difficulty into decisions made early and visibly, giving teams a documented basis for what changed and why.
This article looks at why legacy systems resist conventional testing approaches, how characterisation testing addresses that problem directly, what a testing strategy for modernisation needs to look like, and how to measure whether it's working. It's a natural companion to our earlier piece, Managing Hidden Waterfalls in Legacy Modernisation Projects, which looks at the delivery and methodology side of this same challenge; this piece goes deeper into the testing discipline itself.
Why Legacy Systems Resist Conventional Testing Approaches
Conventional testing assumes a specification exists, whether a requirements document, a user story, or at a minimum a shared understanding of what correct behaviour looks like. Legacy modernisation removes that assumption. The system itself becomes the closest thing to a specification, and it's an unreliable one: business logic embedded in code that was never formally documented, exceptions handled through branches and workarounds nobody captured at the time, and user behaviour that has adapted around system quirks until those quirks became de facto requirements nobody thinks to mention.
This creates a genuine testing problem alongside the documentation gap. A test asserts that a given input should produce a given output, but writing that assertion requires knowing what the correct output is. When the only available answer is "whatever the old system currently does," testing becomes an exercise in discovering behaviour that was never written down in the first place.
The people who could fill that gap through institutional memory are often unavailable by the time a modernisation programme starts. Systems that have been running for a decade or more have usually outlived several rounds of staff turnover, and the workaround someone built in 2014 to handle an edge case in a specific regional process may now be triggering silently on a small fraction of transactions, invisible until someone tries to replace the system and it stops happening.
Characterisation Testing: Capturing Behaviour Before You Change It
This is precisely the problem characterisation testing was designed to solve. The term comes from Michael Feathers' influential work on legacy code, which defines a characterisation test as one that documents the actual behaviour of a piece of code, rather than asserting what that behaviour should be. Instead of writing a test that says "this function should return X," a characterisation test observes what the function currently returns for a given input and records that as the baseline, regardless of whether the result looks correct.
This represents a genuine shift in what testing is for. It stops being a check against a known standard of correctness and becomes a check against continuity: does the system still do what it did yesterday? That distinction matters enormously in legacy modernisation, because the immediate goal is to make existing behaviour visible, so a deliberate decision can be made about which parts of it to preserve and which to change.
In practice, building characterisation tests means running the legacy system with a wide, representative range of real inputs, recording its outputs, and turning those observed input-output pairs into an automated regression suite. That suite then becomes the safety net against which any replacement or refactored system is measured. If the new system produces a different output for the same input, that difference gets surfaced explicitly, as either an intentional improvement or an accidental regression, months before it reaches a customer or an auditor.
Building a Testing Strategy for Modernisation
Characterisation testing gives a starting point, but a full testing strategy for legacy modernisation needs several layers working together.
Characterisation tests on the legacy system
As described above, this establishes the behavioural baseline before any redesign work begins. This should happen early, ideally during discovery, not retrofitted once development is already underway.
Parallel run and shadow testing
Running the old and new systems side by side against the same live inputs, then comparing outputs, catches discrepancies that static test suites miss, particularly those arising from data conditions or usage patterns nobody anticipated when writing test cases. This is especially valuable for financial, regulatory or high-volume transactional systems, where the cost of an undetected behavioural difference is high.
Risk-prioritised regression coverage
Not every piece of legacy behaviour deserves equal testing investment. Coverage should be weighted toward the areas carrying the highest business, regulatory or financial consequence if something breaks.
A deliberate decision process for behavioural change
Every discrepancy between old and new behaviour needs to be explicitly classified: is this a bug in the legacy system that we're correcting, or a regression we've accidentally introduced? Without this discipline, teams tend to default to reproducing every legacy quirk faithfully, which is exactly the trap "Managing Hidden Waterfalls" describes: testing effort shifting entirely toward proving parity with the old system, at the expense of validating that the new one actually solves the problem it was commissioned to solve.
The Role of AI in Legacy Testing
AI-assisted tooling has a real, measurable role in this work, and it's worth being specific about where that role sits. Generating characterisation tests at scale, by running legacy code against large volumes of synthetic or historical inputs and automatically capturing outputs, is exactly the kind of repetitive, high-volume task these tools are well suited to. The same applies to analysing legacy codebases to extract embedded business rules, map dependency chains, and flag areas of dead or redundant code, work that would otherwise consume significant manual analyst time on systems where documentation is thin or absent.
The 2026 State of Testing research found that large enterprises are already leaning on this capability heavily, with 65.5% using AI specifically for regression suite and script maintenance work, a meaningfully higher figure than smaller organisations report. This lines up with where the technique is genuinely strongest: sustaining and extending test coverage on systems that already have some automated baseline in place.
Deciding which legacy behaviours represent genuine business rules worth preserving requires domain knowledge and business context that sits with the team. The tooling can surface candidates at scale; the judgement about which ones matter stays a human responsibility.
Measuring Whether the Strategy Is Working
A testing strategy needs a way to show whether it's succeeding, beyond the general sense that testing volume has increased. Two measures matter most in legacy modernisation specifically.
Parity rate against the characterisation baseline:
This tracks the proportion of characterisation tests where the new system's output matches the legacy system's recorded behaviour, and the proportion where it deliberately diverges. A high, stable parity rate combined with a small, explicitly reviewed set of intentional divergences is a strong signal that the programme understands what it's changing and why. A parity rate that's low or declining late in delivery is an early warning that the team is discovering legacy behaviour later than it should.
Defect escape rate post-migration:
This measures how many behavioural discrepancies are caught before go-live versus reported by users or discovered in production afterwards. It gives a clearer picture of testing effectiveness than raw test count or coverage percentage, both of which can look healthy while missing exactly the undocumented edge cases legacy systems are prone to hiding.
Why Testing Needs to Start at Discovery
The organisational pattern that undermines legacy modernisation testing most consistently is treating it as something that happens at the end, once development is largely complete. By that point, the programme has usually already committed to an architecture and a delivery timeline that leaves little room to properly characterise the systems it depends on, and testing becomes a compressed, high-pressure activity squeezed into the final weeks before go-live.
Characterisation work belongs alongside early technical discovery, feeding directly into design decisions about what the new system needs to replicate and what it's free to change. This also gives programme leadership something more useful than a green sprint burndown chart: concrete evidence, in the form of a growing characterisation suite, of how well the team actually understands the system it's replacing.
Conclusion
Legacy modernisation testing starts from a genuinely different premise than testing greenfield applications. There's no clean specification to validate against, only years of accumulated behaviour that nobody wrote down. Characterisation testing gives teams a disciplined way to make that behaviour visible before they change it, turning an implicit, undocumented specification into an explicit, testable one.
Treating testing as a discipline that shapes a programme from its earliest stages is good practice for any software delivery, not something unique to legacy modernisation. What legacy modernisation changes is the cost of skipping it. Without a written specification to fall back on, there's no shortcut back to "what the system is supposed to do" once development is already underway. Organisations that invest in characterising legacy behaviour early, prioritise coverage by business risk, maintain a deliberate process for classifying behavioural change, and measure parity and defect escape rate are the ones that avoid discovering, late and expensively, that the system they built doesn't actually handle the behaviour the business depends on.


