Skip to content

20 — Legacy Systems & Safe Change

Driving question: How do we improve design when the system is difficult to test and cannot stop changing?

Learning objectives

  • Create seams that enable characterization tests.
  • Separate behavior capture from design improvement.
  • Plan dependency-breaking changes incrementally.
  • Use branch-by-abstraction or strangler-style migration where appropriate.

Legacy code as code without sufficient safety net

The first challenge is often not “apply pattern X” but “create a place where change can be verified.” Techniques include:

  • characterization tests;
  • extract-and-override seams;
  • wrapper/adaptor seams around external systems;
  • dependency injection at selected boundaries;
  • golden-master testing for complex outputs;
  • branch by abstraction for staged replacement.

Refactoring economics

A technically cleaner target can still be a poor plan if migration cost, operational risk, or team capacity makes it infeasible. Graduate-level design includes transition architecture, not only end-state diagrams.

Design / research exercise

Choose one tightly coupled legacy dependency. Design the smallest seam that enables testing. Compare the seam with a full architectural rewrite and identify the migration risk avoided.

Suggested reading

  • Michael Feathers, Working Effectively with Legacy Code.