The uncomfortable truth about replacing a long-lived system: the specification is the system. Fifteen years of decisions live in code, and the documentation describes what someone intended in year two.
Teams that rebuild from the documented process ship something that works correctly and disagrees with the old system in ways nobody predicted — usually at month-end, usually on money.
Why the code alone will not tell you
Reading the source tells you what happens. It does not tell you which behaviours are deliberate.
A fifteen-year-old system contains three kinds of behaviour that look identical in source: intentional business rules, workarounds for bugs that were fixed elsewhere years ago, and dead branches nobody has executed since a product line was discontinued.
Reimplementing all three faithfully is how a rewrite inherits the last decade of accidents. Reimplementing only what you recognise is how it loses rules that mattered.
The parallel run is the reliable instrument
Run both systems on the same real inputs and reconcile the outputs record by record. Every disagreement is a rule you did not know about, presented for inspection while both systems are still available to be compared.
Two disciplines make it worth its cost:
Reconcile automatically, not by sampling. A daily diff of every record beats a monthly review of twenty. The rules you most need are in the tail, and sampling is designed to miss the tail.
Explain every discrepancy in writing. Not "resolved" — the actual reason. A difference that nets to zero in aggregate while individual lines disagree is two errors cancelling, and they will not stay cancelled once volumes shift.
What that log becomes, by the end, is the specification the project never had.
What it typically surfaces
Rarely arithmetic errors. Almost always the rules that live in the gaps:
- A customer category with a different rounding treatment, applied since a dispute in 2014.
- Tax logic changed in a prior year and implemented as an exception rather than a rule.
- A branch whose data has always been loaded a day late, with every downstream report quietly compensating.
- A discount ceiling enforced in one code path and not another, which the sales team has known about for years.
- Date handling around a specific annual event — the fourth of April, the start of the fiscal quarter, the day the price list changes.
None of these would appear in a requirements workshop, because the people who know them have stopped experiencing them as unusual.
Interview the operators, not the managers
The person who described the process in the kickoff is usually not the person who performs it. The gap between those two accounts is where the rules are.
What works is observation before interview: watch the work happen, then ask about the specific thing you saw someone do that was not in the process map. "You typed that in twice — why?" produces better information than any open question about exceptions, because the operator has normalised the workaround and will not volunteer it.
The most productive question we know is: what do you do when the system will not let you do the right thing?
Turn each finding into a test
A recovered rule recorded in a document will be lost again within two years. A recovered rule expressed as an automated test is permanent, and it fails loudly if someone later removes the behaviour without understanding it.
The discipline: every discrepancy resolved during the parallel run becomes a test case, with a comment recording the reasoning and, where known, its origin. The next engineer inherits the why rather than only the what.
Why the timing is urgent
The scariest legacy risk is not the code. It is the retirements.
The people who know why the system does the strange thing in April are leaving, and their knowledge is written nowhere. Modernisation is the last structured opportunity to capture it — and it only happens if it is treated as a deliverable with a name against it, rather than as something the project will pick up along the way.

