Legacy Lab

Change billing in a decade-old service whose behaviour nobody can fully state and whose tests do not exist. Six steps, in this order, because every one of them makes the next one possible — and skipping straight to the change is how a two-line fix becomes an incident.

Legacy code is code that is risky to change because its behaviour is poorly understood or weakly protected. Age is a correlate, not the definition — a service written last quarter with no tests and one author who has left is legacy, and a twenty-year-old system with a thorough suite is not. The loop below is what turns the first into the second, and the reason it is worth the discipline is that it converts an unbounded risk into a sequence of bounded, reviewable steps.

Step 1 of 6

Observe

Before touching anything, establish what the code actually does — not what it was meant to do. Read the call sites rather than the class. Log the inputs it really receives in production. Find out which of its branches have executed this year and which have not executed since the author left.

How this step goes wrong
Reading the code as a specification. The comment, the method name and the ticket all describe intent; the behaviour customers depend on is whatever the code does on the inputs it gets, including the parts that are accidents nobody has noticed. Legacy means unprotected and poorly understood — it does not mean old, and a service written last quarter with no tests qualifies.

The option that skips the loop

It is always on the table, it is always the most attractive thing in the room, and it is the decision this module is most careful about.

Where the modernization gets practised