agile3
Engineering Practices

Legacy Code Refactoring

Safely improving old code that has few tests, by adding tests first and changing it in small steps.

2 min read · Practitioner

In 30 seconds

  • Legacy code is often defined as code without tests.
  • Add characterization tests before changing anything.
  • Change in small steps; replace big parts gradually.

The approach

  1. Find the change point.
  2. Write characterization tests that capture current behaviour.
  3. Break dependencies at seams so code can be tested.
  4. Make the change and refactor in small steps.

Michael Feathers set this out in Working Effectively with Legacy Code (2004).

The strangler fig pattern

To replace a large old system, build new pieces around it and route traffic to them one by one, until the old system can be removed. Martin Fowler named the pattern after the vine that slowly surrounds a tree.

Avoiding big rewrites

Full rewrites take longer than expected and often recreate old bugs and technical debt. Incremental improvement delivers value along the way and keeps risk low.

Myth vs reality

Myth · Legacy means old.
Reality · Code without tests is legacy, whatever its age.
Myth · Rewriting is faster than refactoring.
Reality · Rewrites usually take far longer and carry more risk.
Myth · You need full coverage before changing anything.
Reality · Cover the area you're changing, then expand.

The AI angle

AI is useful for explaining old code and drafting characterization tests, which are often the slowest steps in legacy work. Review its explanations; they can sound confident and be wrong.

AI and Technical Debt →

FAQ

What's a characterization test?

A test recording what code currently does, right or wrong.

What's a seam?

A place where behaviour can be changed without editing the code there.

Should we stop features to fix legacy code?

Usually no; improve code as you work in it.