Make the Change Easy, Then Make the Easy Change
When a change is hard, first reshape the system so the change becomes easy.
“For each desired change, make the change easy (warning: this may be hard), then make the easy change.”
To an executive
When a change looks costly, preparing the ground first can make the change itself cheap and safe. Small tidy-ups ahead of delivery reduce risk and surprises.
To an engineer
Restructure without changing behaviour, then make the behaviour change as a separate step. Ship them separately where you can, and tidy only when it pays off.
The idea
Kent Beck's advice, which he posted in 2012, separates two activities: restructuring the system without changing its behaviour, then making the behavioural change. Each step is simpler and safer than doing both at once. The parenthesis matters: making the change easy may itself be hard work.
In Tidy First? (2023), Beck explores the economics behind it: when to tidy before a change, when after, and when not at all. Structure changes and behaviour changes are different kinds of work, and he argues they are best kept apart.
Why it works
Mixed changes are hard to review and risky to ship. A diff that both moves code and changes what it does hides the behaviour change in the noise. Separate them and each is easy to check.
Restructuring can be made safe. Changes that do not alter behaviour can lean on existing tests, and are easy to roll back.
The second change is often cheaper than expected. Once the right seam exists, the feature that looked hard may be a few lines, and the next similar feature is cheaper still.
Where it applies
- Feature work in tangled code, where the obvious change touches many places.
- Migrations, where seams such as interfaces, adapters and feature flags let you switch implementations gradually.
- Adding a second variant of something, such as a second provider, region or channel.
- Code review practice, by splitting structural and behavioural pull requests.
How to apply
- Picture the design in which the change would be easy, before writing any code.
- Restructure towards it in small steps that keep behaviour the same, with tests passing at each step.
- Ship the restructuring on its own, so it can be reviewed and released safely.
- Make the behaviour change, which should now be small and obvious.
- Stop when the change is easy enough. The goal is the change, not a perfect design.
Example scenario
Adding a second payment provider meant touching 60 files, because the first provider's API calls were spread through checkout, refunds and reporting.
The team first spent a week extracting a payment-gateway interface and routing all calls through it, with no change in behaviour and the existing tests passing throughout. The new provider then took two days to add behind the interface, and a third provider later took one.
Questions to ask
- What change to the structure would make this feature easy to add?
- Can we ship the restructuring separately from the behaviour change?
- Is the preparatory work worth it for this change, or should we just do it the hard way?
When not to use it
- One-off changes in code that is about to be retired, where the preparatory work will never pay back.
- Urgent fixes, where you make the hard change now and tidy afterwards, if at all.
How it misleads you
Endless preparatory refactoring that never delivers the actual change. Restructuring should be in service of a specific change; if you cannot name the change it makes easy, it is not preparation, it is a separate project that needs its own justification.
Further reading
Last reviewed · Suggest an improvement