Skip to main content

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

What This Concept Is​

Useful triggers include repeated knowledge, preparation for a feature, comprehension and small improvements encountered during other work. The rule of three is a heuristic for noticing duplication, not a threshold that proves shared policy.

Two similar calculations may belong to independently evolving policies. Conversely, a single unstable boundary can justify a preparatory change. Scheduled work can be appropriate for a dependency upgrade, ownership change or migration whose coordination cannot fit into incidental edits.

Why It Matters Here​

Refactoring consumes time and can introduce faults. Connect it to a concrete cost: repeated policy edits, a hard-to-isolate dependency, a blocked feature, or a misunderstood invariant. Compare the smallest change with leaving the design intact.

Define a stop condition and a revisit trigger. A dedicated improvement period needs a bounded outcome and evidence, just as feature work does; its calendar placement alone does not make it wasteful.

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. Investigate whether the policies truly vary only by rate before considering Parameterize Function; similar code alone does not establish that contract.

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. Whether the next read becomes easier is a prediction to check, not a measured result here.

Common Confusion / Misconception​

Three similar fragments need not encode one rule. Combining them can couple independent product decisions. Investigate whether they should change together and what differences the proposed abstraction hides.

Leaving an opportunity untouched can reflect priorities or risk, not weak tests. Record the reason rather than meeting a quota of edits.

How To Use It​

Record the observed problem, likely next change, candidate move, alternative and time budget. Characterize the affected boundary, attempt the smallest useful step and stop if the benefit is unsupported or the scope expands. Dedicated migrations also need an owner, exit criteria and a recovery plan.

Check Yourself​

  1. When do identical fragments represent different policies?
  2. What would justify a dedicated migration period?
  3. What observation tells you an opportunistic edit has exceeded its useful scope?

Mini Drill or Application​

Compare two repeated price calculations: one is a contractual invoice rule, the other a promotional estimate. Propose a shared numeric helper only where the semantics agree. Write a keep decision for the policies and a future change that would invalidate it. Do not invent time savings; measure them only if you actually run an experiment.

Engineering Extension: Budget the smallest useful change​

A shared helper for numeric rounding can be useful while separate invoice and estimate policies remain independent. Predict the next edit, measure the change surface and record a reason to keep the current design. Changed class counts alone do not establish lower maintenance cost.

Use the behavior contracts, labs and practice feedback to test the claim.

Read This Only If Stuck​

Locate this move through the Refactoring book index. The selective reading guide gives exact consulted local locators and primary documentation. Index links are not exact chapter links.