Guide

DRY (Don't Repeat Yourself) is advice about knowledge, not about text. Two blocks that look similar but change for different reasons should stay separate. Sandi Metz's line is the corrective: prefer duplication over the wrong abstraction.

YAGNI (You Aren't Gonna Need It) is the sibling: do not build the plugin system, the generic BaseService, or the config-driven workflow engine for a second use case you are imagining. Design for today's requirements with seams that allow tomorrow's change — deep interfaces and clear modules — without implementing tomorrow's features.

The big idea

Trade-off

Extract shared helper now vs wait. Extract when three call sites share the same rule and you can name the rule precisely (calculateProration). Do not extract when two call sites merely share a shape (map + filter that will diverge next sprint). Premature shared helpers become god-functions that every feature must bend around.

The wrong abstraction increases change amplification: every new caller must twist the helper, and every twist surprises the old callers.

Walking through a decision

Two checkout paths both map line items and filter zeros. Next sprint, EU needs tax-inclusive display and US does not. If you extracted normalizeLines last week, you now add a flag, then another, then a region argument. The helper is a god-function. Leaving the two loops duplicated for a week would have been cheaper.

Three refund paths all compute "amount already captured minus amount already refunded, never below zero." You can name that: refundableCents. Extract it. The rule is the same knowledge in three places.

Promotion evidence looks like: fewer production surprises in your modules, reviewers commenting that your diffs are easy to follow, and juniors able to extend your code without a pairing session every time. Saying "I'd make it clean" in an interview is weak; naming change amplification and information hiding is strong. Walk a before/after with concrete costs ("a one-line product change took three files; after extract, one").

Common mistakes

  • Extracting on the second similar map. Wait until you can name the rule.
  • Building BaseService for an imaginary third caller.
  • Treating similar text as the same knowledge.

Check yourself

Two functions share a map + filter that you expect to diverge next sprint. Extract now?

No. Duplicate until the rule has a name and a third real caller.

Three paths all compute captured-minus-refunded, never below zero. Extract?

Yes. That is one named rule: refundableCents.

Key takeaways

  • DRY is about knowledge, not text.
  • Prefer duplication over a helper you cannot name.
  • Extract at three call sites with one precise name, not two call sites with a similar shape.
  • YAGNI: seams for tomorrow, features for today.

Further learning

Next, the part you cannot change cheaply once callers exist: interfaces that age well.