Here the same fact ("the tax rate is 18%") is written in n=3 places.
WHAT / WHY: we plug into the cost model because we want the concrete pain of this specific duplication.
Cedit=n⋅c=3×4=12 minutesPconsistent=(1−p)n=(0.9)3=0.729
So there is a 1−0.729=0.271, i.e. 27.1%, chance you leave one copy wrong. This is a real DRY violation — one fact, three homes.
(B) is the violation. The email-validation rule is one piece of knowledge; if the rule changes (e.g. allow + addressing), you must edit both copies.
(A) is not a violation — the two 25s answer different questions and would change for different reasons. Merging them into one constant would couple unrelated decisions. See The Wrong Abstraction.
Before (n=5):
Cedit=5×3=15 min,Pconsistent=(0.85)5=0.4437After (n=1):
Cedit=1×3=3 min,Pconsistent=(0.85)1=0.85
We cut edit time from 15 → 3 min and raised the "no bug" chance from 44.37% → 85%. This is exactly the Single Source of Truth payoff.
Do not extract yet. By the Rule of Three, we wait until the third occurrence before abstracting, because two examples cannot reveal the true axis of variation.
A function with 4 boolean flags is a classic Premature Abstraction: every call site must now pass build_report(True, False, True, False) — unreadable, and each flag adds a branch. The duplicated 12 lines are honest and local; the flagged abstraction hides the differences behind parameters.
Verdict: tolerate the duplication now. If a third report appears and the same pattern holds, extract then — the real shape will be visible.
Extra cost of the duplication per change: (n−1)⋅c=1×3=3 min.
Extra cost of the wrong abstraction per change: b=6 min.
The wrong abstraction is more expensive (6>3). This is why Sandi Metz says “duplication is far cheaper than the wrong abstraction.” The correct move when you find a wrong abstraction is to inline it back (re-duplicate), then re-extract along the right seam.
Step 1 — separate reasons to change (SRP). Email validation and country-code storage change for different reasons, so they belong in different homes — the Single Responsibility Principle tells us not to merge them just because they sit in the same class.
Step 2 — one home each (DRY).
Email rule → a single validate_email() used by both frontend and backend.
Country codes → a Single Source of Truth: one config/table both layers read.
Step 3 — avoid over-DRY. Do not fuse the validator and the country list into one "validation module" god-object; they are unrelated (The Wrong Abstraction risk).
Result: two independent single-source homes, each referenced everywhere. Both n values drop to 1.
Country list: before 4×5=20, after 1×5=5 → saves 15 min.
Email rule: before 3×5=15, after 1×5=5 → saves 10 min.
Total saved per change-pair = 15+10=25 minutes, and the consistency risk on both drops to the single-copy best case.
WHAT / WHY: we compare "pay E once" against "pay the duplication penalty every change," because whether to DRY depends on how often the code will change — a purely rational, YAGNI-aware criterion.
Extracting is worth it when the saved penalty over k changes exceeds the one-time cost:
k⋅(n−1)⋅c>E⟹k∗=(n−1)cE
Plugging in E=30, n=3, c=5:
k∗=(3−1)×530=1030=3
So if you expect more than 3 future changes, extract; fewer, and the duplication is cheaper. Notice this lands right on the spirit of the Rule of Three — a beautiful coincidence that the arithmetic and the folk-rule agree.
WHAT / WHY: we invert the consistency formula because we want a budget — how much duplication a given reliability target allows.
Require (1−p)n≥0.90, i.e. (0.95)n≥0.90. Take logs (log is the tool that turns an exponent into a multiplier we can solve for):
n≤ln0.95ln0.90=−0.051293−0.10536=2.054
Since n must be a whole number, the largest allowed is n=2. At n=3: (0.95)3=0.857<0.90, which fails. So under a 90% reliability bar and a 5% slip, you may keep at most 2 copies — again echoing "two is tolerable, three, extract."