Skip to main content

The Two Hats: Refactor or Feature, Never Both

What This Concept Is

Kent Beck's two-hats metaphor, as Fowler uses it:

When I add functionality, I shouldn't be changing existing code; I'm just adding new capabilities. I measure my progress by adding tests and getting the tests to work. When I refactor, I make a point of not adding functionality; I only restructure the code.

Two hats, not one. At any moment you are wearing exactly one:

  • Feature hat: behavior changes. New tests get added. Existing tests may or may not change.
  • Refactor hat: behavior is preserved. No new tests unless you find one was missing. Existing tests keep passing without modification.

Swap hats frequently. But never wear both at once.

Why It Matters Here

Every later concept in this module depends on this rule. Extract Function is only safe because you are not simultaneously adding "and also log the amount." Replace Conditional with Polymorphism only stays behavior-preserving if you are not "and also handle the new subtype I just invented."

On the commit level, a mixed "refactor + bug fix" commit is un-bisectable. Git cannot tell you whether the regression came from the move or from the fix. You have merged two causes into one change.

Concrete Example

Mixed (bad):

commit: "refactor extract priceOrder and fix negative-discount bug"

Unmixed (good):

commit A: "refactor: extract priceOrder helpers (no behavior change)"
commit B: "fix: clamp discount to zero for quantity < 500"

If commit B introduces a new regression a week later, git bisect lands directly on B. If the commit was mixed, bisect lands on the mixed commit and you are debugging blind inside two concurrent changes.

Common Confusion / Misconception

"I had to rename a parameter while adding the feature." No, you did not. Rename first (refactor commit, tests stay green). Then add the feature that uses the new name (feature commit, new test). Two commits, two hats.

Another misconception: the two hats is only about commits. It is also about attention. While you wear the refactor hat, do not let yourself think about the feature. When you notice you are, stop, commit the refactor, and start over with the feature hat on.

How To Use It

At every edit:

  1. Say out loud which hat you are wearing.
  2. If you catch yourself changing a test to "match new behavior" while wearing the refactor hat, stop. Take the refactor hat off, put the feature hat on.
  3. Commit before switching hats. The commit becomes the boundary.

Check Yourself

  1. You are renaming a method. Midway you spot a genuine bug. What do you do right now?
  2. A PR says "refactor and add logging." What is wrong with that PR?
  3. Can a refactor commit ever add a test? When?

Mini Drill or Application

Find a PR in your own history (or a public open-source PR) that mixes refactor and feature. Rewrite its history as two commits: pure refactor first, then feature. Check that after the refactor commit the tests pass unchanged, and the feature commit adds a new test that fails without the feature code. This is a 15-minute exercise and it is the habit this entire module depends on.

Video and Lecture References

Article References

External Exercises

  • Refactoring.guru: process - a disciplined step sequence
  • Your own repo: audit the last 10 commits and tag each as Feature, Refactor, or Mixed.

Depth Path

  • Read This Only If Stuck - Fowler chunk 013 (Defining Refactoring and The Two Hats)
  • Optional deep dive: Kent Beck's Test-Driven Development: By Example, the "refactor" third beat of red-green-refactor