The same code structure appears in two or more places — same shape with cosmetic variations, or copy-paste-modify patterns that drift over time. Each copy carries its own subtle history of fixes and feature additions; the signal-to-noise ratio of the codebase drops as the duplicated structure crowds out the differences between copies that actually matter.
One canonical home per behavior, with parameters for the variations. The duplicated structure collapses into a single named function; future variations become parameter choices rather than new copies. The comprehension cost of holding the behavior in mind drops to the size of one function, and maintenance cost per bug fix drops by the copy count.
Smellier version
function totalUSD(items) {return items.reduce((s, i) => s + i.price * i.qty, 0);}function totalEUR(items) {return items.reduce((s, i) => s + i.price * i.qty, 0);}
Fresher version
function lineTotal(items) {return items.reduce((s, i) => s + i.price * i.qty, 0);}
Bug fixes carry the blast radius of every copy; behavior diverges as copies age, multiplying maintenance cost. Fixing the bug in one copy ships a partial fix — the others continue producing the broken behavior. The team's verification cost on every change scales with copy count rather than with the change.
Extracting the shared form introduces a parameter list or hierarchy that all callers must learn; the abstraction's cognitive load has to earn its keep against the cost of indirection. Each caller now reads two definitions — the call site and the shared definition — and reviewers verify the contract on every change to either.
A bug fix or enhancement lands once, not in every clone — and the unifying name reveals shared intent. The team's debugging cost drops because the symptom's origin is one definition instead of N; enhancement cost drops because new features attach to the shared abstraction once rather than spreading across every copy.
Premature deduplication on superficial similarity — code that looks the same but means different things gets merged behind a leaky abstraction, and any future divergence forces awkward branches. The merge feels efficient at first but raises enhancement cost every time a new variation surfaces that the original abstraction can't cleanly express.