Great article - DRY
DRY Is the Most Misunderstood Rule in Programming
Every developer learns DRY early, and almost everyone learns it wrong. Don't Repeat Yourself. See two pieces of code that look the same, extract a method, delete the duplicate. I did this for years and wrote some of the worst code I've ever had to maintain:
Every one started as an innocent attempt to not repeat myself. |
What DRY Actually SaysHere's the part most people skip. The original definition, from Andy Hunt and Dave Thomas in The Pragmatic Programmer, says nothing about code: Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. It's about knowledge. A single fact about your domain, like a tax rule or the format of an invoice number, should live in exactly one place. When that fact changes, you change it once instead of hunting for seven copies. |
The Mistake: Deduplicating Code, Not KnowledgeTwo pieces of code can look identical and represent completely different knowledge. Say you validate two addresses. One is a customer's shipping address, the other is a warehouse address. Today the rules are identical: The DRY reflex says extract one validator and call it from both places. But these are different concepts that happen to share rules this week. The day the warehouse needs a loading-dock code, you're back in the shared method bolting on a flag to keep the other caller working: That boolean is the tell. The first time a shared method grows a flag so one caller behaves differently, you didn't have duplication. You had two things that looked alike and glued them together. Give it a year and the signature has three more flags, each one a place where the two concepts were never actually the same. |
The Wrong Abstraction Costs More Than DuplicationDuplication is far cheaper than the wrong abstraction. Copy-paste is visible and local. You can see both copies, and if they drift apart, that was always allowed. The wrong abstraction is invisible and global. Every caller depends on one shape and bends it to fit, the flags pile up, and you end up afraid to touch a method you no longer understand. I've spent more time deleting bad abstractions than I ever saved writing them. This is the hidden coupling cost I wrote about in the abstractions piece, and DRY-by-reflex is one of the most common ways it sneaks in. |
Where It Hurts Most: Across BoundariesInside one class, a bad helper is annoying. Across module boundaries, it's structural damage. Picture a modular monolith with a Now Billing and Shipping can't evolve independently. A change to billing's order forces a recompile, re-test, and redeploy of shipping. You took two bounded contexts that were supposed to be decoupled and welded them together to save a few properties. Two modules each owning their own |
The Rule I Use: Wait for the Third TimeI don't deduplicate the second time I see something. I wait for the third, and I ask one question: if this rule changes, do both copies have to change together?
Let the code repeat until the right abstraction becomes obvious, because good abstractions are discovered from concrete cases, not guessed up front. Some people call this AHA, for "Avoid Hasty Abstractions." A practical tell: extract when you can name the concept. A real domain name like |
When DRY Is RightApplied correctly, DRY is invaluable. A business rule belongs in exactly one place. Watch what happens when "an order over $1,000 needs manager approval" gets copy-pasted across three services: You will eventually update two of them and ship a bug. That drifted third copy is exactly how it happens. Push the rule into the domain model where it has one home: That's the single authoritative representation DRY is actually about. |
Summary
The next time you're about to delete a duplicate, don't ask whether the code looks the same. Ask whether it means the same. That one question will save you more maintenance pain than DRY ever saved you typing. |
Comments
Post a Comment