Guide

Safe refactoring is many tiny steps that are obviously behavior-preserving: rename, extract function, move file, inline a temp, replace a conditional with polymorphism. Martin Fowler's catalog is the menu. The discipline is the workflow, not the catalog.

Mixing "rename half the module + fix the discount bug + add a feature flag" in one PR makes review impossible and bisect useless. Authors who separate refactors from features get faster code review and safer rollbacks.

Concept

Stay green against characterization tests. If you discover a bug while tidying, stop. Merge the structural PR, then open a second PR that fails first on the bug.

Walking through it

1. Tests green.
2. One structural change.
3. Tests green.
4. Commit.
5. Repeat.
6. Then behavior change in a different PR.

You need to add a new fee type to a 1,500-line switch. Preparatory refactor: extract each existing case into a strategy map keyed by fee code, with characterization tests for current fees. Behavior PR: add the new fee as one new entry. Reviewers see a clear addition instead of a conflict-prone edit in a monster function.

Hotfixes go the other way. Do not sneak renames into a pager fix. Keep the hotfix tiny and reversible; tidy when the pager is quiet.

When to tidy

Fowler's workflows pick when, not only how:

  • Litter-pickup — a quick mess on the path; tidy it, keep the commit separate.
  • Comprehension — you finally understand a block; put the names and dead-branch removal into the code before you leave.
  • Preparatory — the feature is hard in the current shape; restructure first so the feature is a small diff.
  • Planned — a campaign with milestones, tracked like a project.

Kent Beck's Tidy First? is the same idea: make the structural change that makes the feature easy, then make the easy change — in separate commits. Dedicated "cleanup weeks" fail when they lack a forcing product need. Prefer opportunistic tidying on the path of real work.

Trade-off

The anti-pattern is cleanliness with no link to delivery. The opposite is never tidying because "we only ship features," until every estimate quietly doubles.

Common mistakes

  • One PR that both tidies and changes behavior. Reviewers miss the behavior; bisect attributes the bug to "cleanup."
  • Tidying far from the next feature. Pick the module on the critical path.
  • Open-ended cleanup sprints. Without a product need, they stall and burn trust.
  • Sneaking refactors into a hotfix. Reversibility dies.

Check yourself

  1. You found an off-by-one during a 40-file move. What do you ship first? The move, green against characterization tests. Then a second PR with an explicit failing test for the bug.

  2. A 1,500-line switch needs a new fee. What is the preparatory step? Extract existing cases behind tests, then add the new fee as one entry.

Key takeaways

  • One structural change, always green, then commit.
  • Never mix refactor and behavior change in one PR.
  • Use litter-pickup, comprehension, and preparatory workflows so tidying stays tied to delivery.
  • Hotfixes stay tiny; tidy afterward.

Further learning

Fowler's "Workflows of Refactoring" names litter-pickup, comprehension, and preparatory work.

Martin Fowler @ OOP2014 "Workflows of Refactoring"heise conferences
Refactoring (Martin Fowler)Refactoringrefactoring.com

Next, scale the same discipline to systems in incremental migrations.