DRY Principle
debt(d5/e5/b5/t7)
Closest to 'specialist tool catches it' (d5). The term's detection_hints list phpcpd, phpcs, and rector — all specialist static analysis tools. phpcpd specifically detects duplicated code blocks, but crucially it cannot detect the subtler DRY violation: duplicated knowledge expressed differently. So the common surface case is caught by specialist tools, but the deeper violation (same logic in different forms) requires careful code review, landing squarely at d5.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix suggests extracting shared logic into a named function or class, which sounds simple, but the common_mistakes note that DRY violations often span architectural layers or involve utility classes with tight coupling. In practice, resolving a DRY violation properly (not just naively deduplicating) typically touches multiple files and requires understanding domain context, placing it at e5.
Closest to 'persistent productivity tax' (b5). DRY applies across all PHP contexts (web, cli, queue-worker) per applies_to, and its misapplication (premature deduplication, wrong abstractions, tight coupling from over-eager utility classes) persistently slows down multiple work streams. It is not quite architectural (b7/b9) but it is broader than a single localised component (b3), landing at b5.
Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception field states the canonical wrong belief directly: developers believe DRY means never writing similar-looking code twice, when it actually means every piece of *knowledge* should have a single authoritative representation. This distinction is non-obvious and contradicts the surface-level reading of the principle. The common_mistakes reinforce this: premature deduplication of code that only looks similar creates wrong abstractions, tight coupling, and cross-layer leakage. A competent developer reading the name 'Don't Repeat Yourself' will almost certainly apply it incorrectly at first.
Also Known As
TL;DR
Explanation
DRY (from The Pragmatic Programmer) states that duplication of knowledge — not merely text — leads to maintenance hazards where a change requires updates in multiple places. DRY is about knowledge, not code: two pieces of code may look identical but represent different concepts and should not be merged. Duplication is eliminated through abstraction: extracting functions, classes, or constants. WET (Write Everything Twice) is a pragmatic relaxation: accept one duplication before abstracting, to avoid premature abstraction.
Common Misconception
Why It Matters
Common Mistakes
- Applying DRY to code that looks similar but represents different concepts — premature deduplication creates wrong abstractions.
- Creating a utility class that does everything to avoid duplication — you trade duplication for tight coupling.
- DRY-ing across architectural layers (sharing a model between API and database schema) — some duplication reduces coupling.
- Refactoring to DRY before understanding the domain — wait until you see the pattern three times (Rule of Three).
Avoid When
- Premature deduplication of code that looks similar but has different reasons to change — wrong DRY creates coupling.
- Forcing unrelated concepts into a shared abstraction to avoid repetition — duplication is better than the wrong abstraction.
- DRY-ing up tests — test code benefits from verbosity and duplication for readability and independence.
When To Use
- Logic that has one clear reason to change and is duplicated in multiple places — extract to a single authoritative location.
- Configuration values used in multiple places — a single constant is easier to change than scattered literals.
- Repeated algorithmic patterns within the same domain — extract to a named function with clear intent.
- After the pattern has appeared at least three times — follow the Rule of Three before abstracting.
Code Examples
// Three controllers all doing the same auth check
if (!$user || $user->role !== 'admin') {
return response()->json(['error' => 'Forbidden'], 403);
}
// Extract to middleware or base controller
class RequireAdmin {
public function handle(Request $req, Closure $next): Response {
if (!$req->user()?->isAdmin()) {
return response()->json(['error' => 'Forbidden'], 403);
}
return $next($req);
}
}