working-with-legacy-code
Working Effectively with Legacy Code
A field manual for changing code that has no tests, distilled from Michael C. Feathers' Working Effectively with Legacy Code. Use it to get untestable classes into a harness, pin down current behavior with characterization tests, and make changes one safe, verifiable step at a time — without resorting to a rewrite.
Core Principle
Legacy code is simply code without tests. Not old code, not ugly code — untested code: without tests you cannot know whether a change preserves behavior, so every edit is a gamble. The craft is breaking dependencies just enough to get tests in place before changing anything — cover and modify, never edit and pray.
Scoring
Goal: 10/10. Rate changes to untested code 0-10 against the principles below. Report the current score and the specific steps needed to reach 10/10.
- 9-10: Change points covered by characterization tests before any edit; behavior changes and refactoring shipped as separate verified steps; dependencies broken with the least invasive technique
- 7-8: Tests at most change points, but occasional mixed refactor-plus-behavior commits or heavier dependency surgery than needed
- 5-6: Some characterization tests, yet key paths still changed on faith; sprouted code accumulating with no payback plan
- 3-4: Edit-and-pray with manual verification; tests written after the change, asserting whatever the new code happens to do
- 0-2: Untested edits straight into tangled code, refactoring and behavior change mixed in one commit, rewrite proposed instead of tests