What is test-driven development in the context of legacy code?
Answer
Applying TDD to legacy code (code without tests) requires different techniques than greenfield TDD. The challenge: you can't refactor (safely) without tests, but you can't write tests easily for untested code (tight coupling, global state, hardcoded dependencies). Michael Feathers' book "Working Effectively with Legacy Code" addresses this. Techniques: (1) Characterization tests: write tests that document current behavior (even if wrong) to prevent regressions while refactoring. (2) Seam identification: find points in the code where you can substitute behavior for testing — object seams (dependency injection), preprocessor seams, link seams. (3) Extract and override: extract hard-coded dependencies into methods you can override in test subclasses. (4) Sprout method/class: add new functionality in a new, testable method/class called from the legacy code. (5) Wrap method: wrap the existing method with testable code. Apply the "Boy Scout Rule" — always leave the code cleaner than you found it, adding tests as you touch it.