Part 14 · 1 chapters · ~8 min
Working Effectively with Legacy Code
Michael Feathers on changing code that has no tests: legacy defined as untested, cover and modify instead of edit and pray, the change algorithm, seams for substituting dependencies, characterisation tests that pin current behaviour, and sprouting or wrapping new logic.
16
Working Effectively with Legacy Code
code
// a characterisation test, then a sprout: legacy eligibility code in an Angular service
// 1. pin what it does today, including the odd case
it('characterise: settled balance of 0.004 naira is treated as eligible', () => {
expect(legacy.isEligibleForLetter({ balance: 0.004, status: 'SETTLED' })).toBe(true); // surprise: floating point
});
// 2. sprout the new rule as a small tested function, in kobo
export const eligible = (l: { balanceKobo: number; status: 'SETTLED' | 'PENDING' }) =>
l.status === 'SETTLED' && l.balanceKobo === 0;
// 3. call it from the old code; the characterisation test now fails on purpose,
// and the team decides the new behaviour explicitly (0.4 kobo is not zero: route to operations)| situation | technique |
|---|---|
| a service constructs its own HTTP client inside | object seam: inject the client through the constructor |
| a module imports a singleton you cannot run in tests | module seam: substitute at import with the test runner's module mocking |
| a 400-line method needs one new rule | sprout method: new tested function, one call added |
| you need logging or checks around existing behaviour | wrap method |
| nobody knows exactly what the code does | characterisation tests first, then decide |
your story in these terms
The KYC and verification module refactor in Angular is a legacy-code story. Telling it with this vocabulary (what you characterised, where the seams were, what you sprouted) makes it precise and credible, and keeps the scope honest.
WORKING EFFECTIVELY WITH LEGACY CODE
Michael Feathers, 2004: getting untested code under test so it can be changed safely
swipe the figure sideways, or tap expand for full screen
1/6
Legacy means untested
Feathers defines legacy code as code without tests. Without tests you cannot know whether a change broke something, so every change is slow and frightening. The definition shifts the conversation from "old code is bad" to "code we cannot get feedback on is risky".