Duplication isn't the enemy — premature abstraction is.
// The "Universal" Handler
function saveData(data, type) {
const db = connect();
// Coupling: Change one, risk breaking others
if (type === 'user') {
validateUser(data);
} else {
validateOrder(data);
}
save(type, data);
}
// Independent: Evolve freely
function saveUser(user) {
validateUser(user);
db.users.add(user);
}
function saveOrder(order) {
validateOrder(order);
// Orders might need an API call instead!
api.orders.submit(order);
}
Code that looks similar isn't always conceptually related. Merging unrelated code couples things that should evolve independently.
Shared helpers create invisible dependencies. Change one thing, break three others. The "Monolithic Object" anti-pattern starts here.
Every abstraction adds a layer of cognitive load. Too many layers make code impossible to trace for newcomers.
The Reality: Duplication is cheaper than the wrong abstraction. It is easier to fix duplication later than to dismantle a complex, incorrect abstraction.
Avoid Hasty Abstractions. Wait until you fully understand the pattern before you abstract.
Write Everything Twice. It's okay to copy-paste. Abstract only on the third time (or fourth).
You Aren't Gonna Need It. Don't build for hypothetical futures. Build for today.