Glossary
Legacy code
Code without tests that earns money. The definition by Michael Feathers is useful because it names the real problem: missing safety, not age.
Age alone does not make something legacy. A twenty year old system with tests, documentation and people who understand it is a solid system. A two year old service without tests whose developers have left the company is legacy. The difference is whether you can change it safely.
From that follows the most important consequence: legacy is not a state of the code, it is a state of the knowledge. What a system does is in the code. Why it does so was in the heads of people, and those people are gone. That is why the first step is never a rebuild, it is understanding what the system really does today, quirks included that nobody planned.
Those quirks are the core of the problem. Every grown system has behaviour that started out as a bug but that somebody now relies on: a rounding error that accounting has priced in, a special case for one large customer, an order of operations that happens to work. A rebuild from specification reproduces exactly none of that.
The workable way in is to secure before you change. Characterization tests pin down the behaviour of today, regardless of whether it is correct. Only once that net is in place is a change a change and not a bet.
Organizationally the bus factor is the most important metric: how many people can touch an area. At one, every holiday is a risk and every resignation is an incident. Spreading knowledge is cheaper than any modernization and still rarely gets planned.
And finally it pays to look honestly at what does not have to be touched at all. A stable area nobody changes costs nothing, even if it is ugly. You modernize where the work happens.
How you notice it
- There are no automated tests, or hardly any.
- The people who wrote the code are no longer with the company.
- Nobody can say why a particular special case exists.
- Changes cause incidents in places that have nothing to do with them.
Not to be confused with
- Technical debt
- The deferred effort. Legacy describes the state of missing safety. A legacy system can carry little debt if it is stable and rarely changed.
- Brownfield
- The starting point of "there is already something". Says nothing about testability or the state of knowledge. Every legacy project is brownfield, not every brownfield is legacy.
- Monolith
- An architectural shape. A well cut monolith with tests is not legacy, and distributed services without tests very much are.
When it fits
- The system is meant to keep evolving in business terms.
- Changes regularly cause incidents in unexpected places.
- Only individual people can still touch certain areas.
When it does not
- With a stable system that gets no more changes and will be replaced in the foreseeable future.
- As preparation for a rebuild that has been decided anyway: then understanding matters more than securing.
How to approach it
- Understand before you planRead the code, the infrastructure and the incident history. The incident history reveals more about the real weak points than any documentation.
- Write down the knowledge while somebody still has itWhoever knows the system will leave. What can be settled in a conversation today costs weeks of reconstruction once they are gone.
- Secure with characterization testsPin down the behaviour of today, including where it looks wrong. Whether it is correct gets decided later; right now this is about the net.
- Draw boundariesPut an anti-corruption layer in place so that new code does not inherit the quirks of the old one. Without that boundary legacy migrates into the new system.
- Replace step by stepStrangler fig instead of a rebuild. Every step goes into production on its own and can be rolled back on its own.
- Actually switch the old path offSwitching it off belongs in the same task as switching over. Otherwise you operate two systems instead of one, and the modernization has made things worse.
Frequently asked
At what point is a system legacy?
As soon as it can no longer be changed safely, because safety and knowledge are missing. Age is beside the point. A two year old service without tests whose developers are gone meets the definition; a twenty year old system with tests and people who know it does not.
Should legacy code be rewritten?
Almost never in one go. The rebuild has to reproduce behaviour nobody knows in full, while the old system keeps running and changing. Step by step replacement delivers parts into production from the first month and can be paused at any point.
How do you start when nobody knows the system?
By reading and observing: code, infrastructure, incident history, actual traffic. After that, characterization tests for the areas that are going to be touched. This approach is unspectacular and shortens every project that follows considerably.
What do you do with behaviour that is obviously wrong?
Pin it down first, then ask, then change it. Quietly correcting a rounding error can change a settlement somebody has relied on for years. The correction is a business decision, not a technical one.
