43

DRY is about knowledge, not text

#
  • heuristic
  • context-dependent
  • pseudocode

Conflicts with: DRY ↔ bounded contexts

The same five lines in two bounded contexts are not duplication: they change for different reasons. “Eliminating” them couples things that must change independently.

Both extremes cost money. Copied knowledge, one rule physically living in three places, diverges silently: the fix lands in two of the three copies, the third keeps computing the old way, and nobody finds out until the incident. An eliminated coincidence, code from two contexts merged into a shared helper because it happens to be identical today, couples what must diverge: the first change that differs arrives as a flag in the signature. The question is always the same: is this one piece of knowledge, or two that currently look alike? The telltale sign of the latter is a package common / utils that grows without bound: a dumping ground has no owner and no reason of its own to change, because it has as many reasons as it has consumers.

Bounded context (14): a boundary within which words mean the same thing. Billing's “Customer” and marketing's “Customer” are different types even when their fields coincide. DRY applies within such a boundary. Across it, identical text is a coincidence, not duplication, and extracting it into a shared module glues two contexts together through code that belongs to neither of them.

Code example (pseudocode)
Gopseudocode
// DON'T: pkg/common/validation.go, where billing and crm both go for validateEmail

// DO: each context keeps its own copy; billing will soon need
// RFC strictness for invoices, CRM tolerance of junk marketing addresses.
// A shared function would force one of the contexts to lie.

Last technical review: 2026-08-04 · Reference · 112 principles