DRY, WET, AHA: Finding the Right Balance in Code Reuse
Every developer learns "Don't Repeat Yourself" early in their career. But like most principles, applying it dogmatically can cause more harm than good. Let's explore three philosophies around code duplication and when each makes sense.
DRY: Don't Repeat Yourself
The DRY principle states that every piece of knowledge should have a single, unambiguous representation in your system. When you spot duplicate code, you extract it into a shared function, class, or module.
// Before: repetition
function calculateUserTotal(user) {
return user.items.reduce((sum, item) => sum + item.price * 1.23, 0);
}
function calculateGuestTotal(guest) {
return guest.items.reduce((sum, item) => sum + item.price * 1.23, 0);
}
// After: DRY
function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price * 1.23, 0);
}
Benefits: Fewer bugs (fix once, fixed everywhere), easier maintenance, smaller codebase.
The trap: Developers often merge code that looks similar but serves different purposes. When requirements diverge later, the abstraction becomes a tangled mess of conditionals.
WET: Write Everything Twice
WET is sometimes jokingly expanded as "We Enjoy Typing" or "Write Every Time," but its real meaning is more nuanced: don't abstract until you've seen the pattern at least twice.
The idea is simple—duplication is cheaper than the wrong abstraction. The first time you write something, you don't know if it will be reused. The second time, you start seeing a pattern. Only then should you consider extracting shared logic.
// First occurrence: just write it
function processOrder(order) {
const tax = order.subtotal * 0.23;
// ...
}
// Second occurrence: still okay to duplicate
function processRefund(refund) {
const tax = refund.amount * 0.23;
// ...
}
// Third occurrence: now consider abstracting
function calculateTax(amount) {
return amount * 0.23;
}
Benefits: Avoids premature abstraction, keeps code straightforward, easier to understand each function in isolation.
AHA: Avoid Hasty Abstractions
Coined by Kent C. Dodds, AHA programming takes WET further by emphasizing that you should optimize for change, not for reducing duplication.
The core insight: duplicated code is easy to refactor later, but a bad abstraction is painful to undo. When you merge two similar pieces of code prematurely, you couple their evolution. When one needs to change independently, you either add ugly conditionals or painfully untangle the abstraction.
AHA suggests you ask yourself: "Do these really need to change together?" If not, duplication might be the better choice.
// Looks similar, but serves different business purposes
function validateUserRegistration(data) {
if (!data.email) return { valid: false, error: 'Email required' };
if (!data.password) return { valid: false, error: 'Password required' };
if (data.password.length < 8) return { valid: false, error: 'Password too short' };
return { valid: true };
}
function validateNewsletterSignup(data) {
if (!data.email) return { valid: false, error: 'Email required' };
return { valid: true };
}
// Don't merge these! They'll evolve differently as business rules change.
Practical Guidelines
Here's how to decide:
-
First occurrence — Just write the code. Don't think about reuse yet.
-
Second occurrence — Notice the duplication but resist the urge. Copy-paste is fine.
-
Third occurrence — Now evaluate: Do these cases share the same reason to change? If yes, abstract. If not, keep them separate.
-
When in doubt — Prefer duplication. It's easier to merge duplicate code later than to split a bad abstraction.
The Real Enemy: Wrong Abstractions
Sandi Metz put it best: "Duplication is far cheaper than the wrong abstraction."
A bad abstraction doesn't just fail to save time—it actively costs time. Every developer who touches it must understand the abstraction's quirks. Every new requirement must work around its assumptions. Eventually, someone rewrites it from scratch.
Conclusion
DRY is a useful heuristic, not a law. WET reminds us that some duplication is acceptable. AHA teaches us to wait until we truly understand the pattern before abstracting.
The goal isn't eliminating all duplication—it's building code that's easy to change. Sometimes that means sharing logic. Sometimes it means keeping things separate. Wisdom is knowing which situation you're in.



