I’ve lost count of how many PRs I’ve seen stalled because someone pointed out two similar-looking blocks of code and demanded they be "unified." It sounds like a great idea until you’re three months deep into a "generic" validation function that now takes eight boolean flags just to handle one edge case in the billing service.
...And that’s the trap. We’re taught DRY like it’s some kind of moral imperative, but in a microservice environment, "DRYing" your code usually just means you've built a hidden dependency that’s going to make your next deployment a nightmare. I’ve lived through the "Shared Utils" era where changing a date formatter in one place broke the entire auth flow because someone thought it was "elegant" to share code between unrelated domains.
The "Wrong Abstraction" is a Debt You Can't Refinance
It is so much harder to "un-abstract" code than it is to just copy-paste five lines of logic. I once spent a whole Friday afternoon trying to decouple a GenericEmailService because the marketing team wanted a different template than the transactional system. If I’d just duplicated the two lines of SMTP setup, I would’ve been done in five minutes. Instead, I had to untangle a web of if statements and Optional parameters.
Duplication is Actually Cheaper
In a backend service, I’d much rather have ten lines of duplicated logic than a 50-line "utility" that handles five different use cases.
- Readability: I want the logic right there. I don't want to follow a trail of three nested helpers just to see how a simple validation is happening.
- Decoupling: If I duplicate the logic, I can change the billing service without even checking if I broke the inventory service. They can evolve at different speeds.
My Rule (Which I Change All The Time)
I don't even follow the "Rule of Three" anymore. I wait until it’s been duplicated five or six times, and even then, I ask myself if it’s really the same business concept. Sometimes things just look the same by coincidence.
I’ve started to think that DRY should only apply to "low-level" stuff—math, encryption, maybe some very stable protocol implementations. For anything that involves business logic? I'm perfectly happy to repeat myself. It’s a small price to pay for being able to delete a feature without having to perform surgery on the rest of the codebase.
The PR Review Weapon
My biggest gripe is when people use DRY as a weapon in code reviews. It’s the easiest thing to point out—"Hey, this looks like that other thing"—without actually thinking about the architectural cost of coupling those two things together. It’s lazy "clean code" dogma that ignores the reality of how software actually changes over time.
Maybe it's just my setup, but I've never regretted a little duplication, while I've definitely regretted almost every "clever" abstraction I've ever written.
