One of the most common lessons software engineers learn early is DRY: Do Not Repeat Yourself.
It is usually interpreted as: “if two pieces of code look the same, combine them.”
That instinct is understandable, and sometimes it is right. But it is also where a lot of bad abstractions begin.
The real purpose of DRY is not to eliminate every repeated line of code. It is to avoid duplicating knowledge. In practice, that usually means avoiding the duplication of business rules, domain decisions, and sources of truth.
That distinction matters because two pieces of code can look almost identical while still representing completely different business ideas.
Consider this example:
function isCustomerEligibleForRefund(daysSincePurchase: number): boolean {
return daysSincePurchase <= 30;
}
function isEmployeeEligibleForExpenseClaim(daysSincePurchase: number): boolean {
return daysSincePurchase <= 30;
}
These methods are identical today. A strict “remove all duplication” mindset might push you to combine them into a generic helper:
function isWithinAllowedDays(daysSincePurchase: number): boolean {
return daysSincePurchase <= 30;
}
But that abstraction loses something important: meaning.
The original methods answer two different business questions. One is about customer refunds. The other is about employee expense claims. They happen to share the same rule right now, but they do not necessarily belong to the same business concept.
And that is the key point.
If those policies change independently in the future, keeping them separate makes the change easier and safer. If they were merged too early, you now have to untangle an abstraction that implied they were one rule when they never really were.
This is where many engineers misunderstand DRY. They treat similar code as proof that two things are the same. But similarity in implementation does not always mean similarity in responsibility.
A better way to think about DRY is this:
Do not duplicate business knowledge. But do not be afraid to keep code separate when it represents separate intentions.
That is often the more maintainable design.
As engineers gain experience, they usually become less obsessed with eliminating every duplicate line and more focused on preserving the right boundaries. That is because the real goal of good design is not compactness. It is making future change safe.
So when you spot duplication, do not ask only:
“Can I merge this?”
Ask:
“Is this the same business rule?”
If the answer is yes, abstraction may help. If the answer is no, keeping the code separate may be the better design, even if it looks repetitive.
Because in the end, DRY is about shared knowledge, not just shared code.