Legacy Systems
Overview
The most useful definition of a legacy system mentions neither age nor technology:
A legacy system is a system the organization is afraid to change.
The fear has concrete causes — no tests, lost knowledge, coupling, no environment to verify in — and each one has a different treatment.
A twenty-year-old system with good test coverage and a team that knows it is not legacy in this sense. A two-year-old system, written by someone who left, with no tests, is.
Why This Matters
Treating age as the problem leads to the wrong intervention.
diagnosis: "it's old" → replace it
diagnosis: "it has no tests" → add characterization tests
diagnosis: "nobody understands it" → archaeology and documentation
diagnosis: "it's coupled to everything" → isolate boundaries
The last three are far cheaper than the first, and they frequently solve the real problem. See legacy refactoring.
And there is a framing cost: calling a system legacy is a judgment that affects the people who maintain it. It devalues work that sustains the business, and produces the predictable effect — nobody wants to work there, which aggravates exactly the knowledge problem.
Core Concepts
What produces the fear of changing
no tests there's no way to know whether the change broke something
lost knowledge nobody knows why the code does what it does
coupling one change propagates in unpredictable ways
no environment there's nowhere to verify before production
risky deployment every release is an event
obsolete dependencies upgrading is a project in itself
Note that only the last has anything to do with age. The other five can exist in recent systems — and they do, with uncomfortable frequency.
The embedded knowledge is the most underestimated asset
A system in production for years has accumulated rules nobody documented:
edge cases discovered in incidents
exceptions for specific customers
workarounds for partner system behavior
business rules that changed and left a trace
That knowledge is in no document. It is in the code — frequently in the form of conditionals that look arbitrary.
It is the main reason rewrites fail: the new system is built from what people believe the system does, and not from what it does. See rebuilding.
Before discarding old code, it is worth asking of each strange fragment: why is this here? Frequently the answer is an incident from seven years ago.
Characterization tests capture current behavior
The technique that reduces the fear before any change:
conventional test verifies the desired behavior
characterization test captures the current behavior, whatever it is
It does not judge whether the behavior is right. It freezes what exists, so that any accidental change shows up.
That makes it possible to refactor safely without fully understanding the system — which is the real situation. See legacy refactoring.
And tests written that way document: they are the executable description of what the system does, produced from it.
The legacy system sustains the business
An observation the language of "legacy" hides: these systems work. They process transactions, serve customers, generate revenue.
That has two practical implications:
The risk of touching it is real. It is not conservatism — it is that the system does something important, and breaking it has consequences.
The value is locked in there. Years of refinement, of fixes, of learning. Discarding that is discarding the investment.
See modernization drivers — the decision to touch it has to outweigh that value.
Not every legacy system needs to be dealt with
The question is always the same: do we need to change it?
a stable system nobody needs to change → not a problem
a system that needs to change and resists → that's the problem
A legacy system that serves well and does not change can go on that way indefinitely. What it needs is containment — isolation so that it does not limit what is around it — and a plan for the people risk.
Common Mistakes
Diagnosing by age. It leads to replacement when something else would have solved it.
Discarding the embedded knowledge. The most common cause of rewrite failure.
Not writing characterization tests before touching it.
Treating as a problem a system that does not need to change.
Using "legacy" as a judgment. It devalues whoever maintains it and aggravates the knowledge problem.
Assuming the documented behavior is the real one. After years, they have diverged.
Real-World Example
An insurer had a claims calculation system written in 1998, pointed to internally as the example of legacy to replace.
Before approving the replacement, a team spent six weeks on archaeology — reading the code, interviewing whoever maintained it, and writing characterization tests.
What they found:
The technology was not the problem. The system was stable, fast, and the language had active support.
The knowledge was the problem. Two people maintained it, both with more than 15 years at the company. Nothing was documented.
The rules were far more complex than assumed. The characterization tests captured 340 behavioral cases. The specification that existed — written for an earlier, abandoned replacement project — described about 90.
The other 250 were two decades of accumulation: regulatory exceptions, agreements with reinsurers, fixes for cases that turned up in audits.
One of them was critical and existed nowhere else. A specific rounding rule for policies issued before 2003, required by a court decision. It existed as a three-line conditional, with no comment.
The decision changed:
Replacement deferred. The original motive — "it's legacy" — did not hold up.
Characterization tests kept as a permanent asset, integrated into the pipeline. They became the system's executable documentation.
Knowledge transfer prioritized: from two to five maintainers, over the course of a year, using the tests as material.
Incremental refactoring where change was frequent, with the tests providing safety.
Two years later, the system is still in production. It is no longer called legacy internally — not because it changed technology, but because the organization stopped being afraid to touch it.
What was recorded afterwards: the six weeks of archaeology cost a fraction of the replacement project, and revealed that the project would solve the wrong problem. The rounding rule, on its own, would have been lost in a rewrite — and would have produced a legal liability.
Related Concepts
- Modernization Drivers — when to touch it.
- Legacy Refactoring — characterization tests.
- Rebuilding — the risk of lost knowledge.
- Organizational Constraints.
Practical Exercise
Take a system your team calls legacy and list the concrete causes of the fear of changing it: lack of tests, knowledge, coupling, environment.
Each cause has a different treatment, and most are cheaper than replacing.
Interview Questions
- What is the useful definition of a legacy system?
- Why is embedded knowledge the most underestimated asset?
- What does a characterization test do that a conventional test does not?
Further Reading
- Feathers, Michael. Working Effectively with Legacy Code. Prentice Hall, 2004.
- Böckeler, Birgitta et al. Legacy Modernization. Thoughtworks, 2023.
- Newman, Sam. Monolith to Microservices. O'Reilly, 2019.