Skip to main content

When to Refactor: Rule of Three, Preparatory, Opportunistic, Comprehension

What This Concept Is

Refactoring is not a scheduled activity. It has four real triggers:

  • Rule of three (Don Roberts, via Fowler): the first time you do something, just do it. The second time, wince at the duplication but repeat it. The third time, refactor.
  • Preparatory refactoring: before adding a feature, reshape the existing code so the feature fits in cleanly. "Check the map before driving east" (Jessica Kerr).
  • Comprehension refactoring: whenever you have to think hard to understand code, refactor so the next reader does not. Ward Cunningham: move understanding from your head into the code itself.
  • Opportunistic (litter-pickup): while fixing or adding something else, leave the campsite cleaner than you found it.

None of these are "let's schedule a refactor sprint." Almost all refactoring is inline with regular work.

Why It Matters Here

Scheduled "refactor weeks" usually get cancelled under feature pressure and teach nothing. The four triggers are what actually keeps a codebase alive. Each one creates a natural decision point that has a test of its own:

  • rule of three: is this the third occurrence?
  • preparatory: will the feature be easier after the move?
  • comprehension: did I just spend minutes figuring out what this does?
  • opportunistic: am I already in this file with green tests?

Concrete Example

A function chargeCustomer exists. You copy it to make chargeEmployee (strike two). You now see a bid to copy it again for chargeContractor (strike three). That is the rule-of-three signal. Rather than a third copy, Parameterize Function over the discount rate and delete two of the three.

Preparatory case: you are about to add tax to orders. The existing priceOrder inlines base + discount + shipping. Adding tax in the inlined form makes it 50 lines. Preparatory Extract Function first, then add applyTax as a new concern.

Comprehension case: you read a function named process for three minutes. You rename variables (x -> itemsRemaining, tmp -> promotedPrice) as a pure refactor. The next read takes thirty seconds.

Common Confusion / Misconception

"We will refactor after the release." No, you will not. The backlog grows; the debt compounds. The only refactoring that reliably happens is the kind that happens inline, triggered by work you were already doing.

Also: rule-of-three is not "do not refactor until you see three." If you already know the third use is coming next week and the setup is trivial, one copy can be enough. Judgment, not mechanical counting.

How To Use It

Check Yourself

  1. Name one real situation from your own work where a preparatory refactor would have saved an hour.
  2. Why is "let's stop feature work and do a refactor sprint" usually a warning sign?
  3. When does opportunistic refactoring become "rabbit hole"?

Mini Drill or Application

Over one working day, keep a tally. Every time you touch code, record which of the four triggers you felt, or "none." At end of day, count how many opportunities you acted on vs. left on the floor. If you left more than three unacted opportunities, your tests may be too weak to give you the confidence to move inline.

Video and Lecture References

Article References

External Exercises

Depth Path

  • Read This Only If Stuck - Fowler chunks 015, 016 (When Should We Refactor, parts 1-2)
  • Optional deep dive: chunk 020 (Refactoring Architecture and YAGNI) on the tradeoff with up-front design

Source Backbone

Refactoring is the canonical book backbone for this module. Use these sources after attempting the refactor and tests yourself.