practice

Refactoring Under Test

Changing internal structure without changing behaviour, with tests as the mechanism that makes the claim verifiable.

The definition matters: refactoring changes structure and not behaviour. A change that alters behaviour is a rewrite, and calling it refactoring is how untested changes get shipped under a reassuring label.

Tests are what make the distinction real. Without them, "no behaviour change" is an assertion. With them it is checked — which is why the first step when refactoring untested legacy code is to add characterisation tests that capture current behaviour, including the behaviour that looks wrong. Wrong behaviour may be depended upon, and discovering that during a refactor is much worse than discovering it beforehand.

Practices that make it safe at scale: small steps with tests run between them, so a break is attributable; automated refactorings from the IDE where available, which are mechanically correct; separate commits for refactoring and behaviour change, so a review can see which is which and a bisect can distinguish them.

The strategic version is the same discipline applied to architecture: preparatory refactoring — make the change easy, then make the easy change — is how structural improvement gets funded, because it is attached to feature work rather than competing with it. A proposed rewrite competes with delivery and loses.