Doesn’t matter what it originally meant, people take it to mean you shouldn’t have repeated code and that’s the DRY we all live with which results in bad abstractions. I don’t think it’s a straw man at all, unless you use your specific definition of DRY which isn’t very useful
People are welcome to co-opt the acronym and give it another meaning. The issue is that the original DRY is a damn good principle, and it is more important to give it a name and propagate that knowledge.
If all we do is rail against the "new" DRY and forget the original one, then we are at a net loss.
I'm with you - the violations of DRY I still see regularly are clear cases of copying and pasting exactly the same logic (or magic literal value) when there was no reason not to put it in a helper function or named constant that could be referred to in both places.
A code review I did yesterday had that - there were 5 or 6 lines of code that, starting with a particular regex, did some data massaging. It was determined in some cases a different reg-ex was needed for a second pass over the data, and so the submitter had simply copied the 5-6 lines of code and just changed the reg-ex used. I see that sort of thing 20 or 30 times more often than code that gets itself into knots because of excessive abstraction trying to avoid code repetition.
Some people feel productive when they can type a bunch of code quickly. And the only way to do that is to get muscle memory for writing the same code over and over and over.
To be fair, in your example it sounds like the repetition is very local and easily recognised for what it is. Not ideal, but hardly a poster child for when DRY is impactful.
If the change was otherwise good, I would remark on the repetition as "here's how I would write it differently" and not "go back and fix it now".
The first time someone changes four of the cases the same way but misses the fifth, though. That sounds like a good time to refactor.
Just came across another slightly more interesting example. We have code that has to do essentially the same thing for three different document types: it loops through a passed-in list and for each item, adds an object to a document, then initialises that object with details from the item.
The "logic" in all 3 cases is exactly the same, yet there were 3 different implementations, one for each document type. The only difference between the 3 is that the function you need to call to add the object is different for each document type, and the library we're using (the MS DocumentFormat.OpenXml library as it happens) doesn't provide an abstraction for just calling the same function regardless of document type. In fact, creating such an abstraction wouldn't be complex at all - if you look at the decompiled MS source, they've basically implemented the same function 3 times, but using an internal function not available to us.
As it happens there was a bug in our initialisation code (missing null check!), but of course it was duplicated across all 3 functions. I basically had 3 options:
a) make the same null check fix in all 3 versions, thus resulting in 3 even longer functions with the same logic
b) collapse all 3 functions into one and provide an abstraction for the "adding object to document" part
c) refactor the 3 functions to use the same helper function just to do the object initialisation
In the end I went with c) because it required the least amount of code, and the only genuine duplication really is the foreach statement. But arguably if MS had kept their library DRY in the first place we would never have ended up with so much code duplication.
I love the principle but do we want to save the principle or save the acronym? At this point IMO it's a lost cause. I wish someone with a following would make a retronym of TIE or SYNC to express it.
I remember when The Pragmatic Program 20th Anniversary came out, the authors, in interviews and the new edition itself, described DRY as "the most misunderstood" concept in the book. If for 20 years that is the most misunderstood concept, then maybe the name is not the best.
Well, DART (Don’t Assert Redundant Truths) might be a better name for the same principal, though its perhaps somewhat opaque when doing imperative rather than declarative (e.g., functional/logic/relational) programming, since people are less likely to consider the former to constitute “asserting truths” in the first place. But it does get more to the point that thing you want to avoid isn’t code that looks similar, or even which mechanically does the same thing, but code that represents the same facts.
I have a set that’s harder to corrupt, but still not bulletproof:
Source of truth, system of record.
Business decisions should have a source of truth. But the domain of things we want to duplicate or not duplicate is bigger than just the business rules.
If you are asking two questions, it’s okay to have two implementations. If you ask the same question twice, you should get the same answer, not just the same output.
But miscreants can twist the meaning of five different parts of what I just said. Like what even is a business rule? It’s whatever the last thing they said before you got them to stop talking. But if they come back later and want something that disagrees with what they already asked, they’ll wriggle like a fish on a river bank trying to gyrate a way to interpret what they asked for to say you’re wrong (and therefore you should work nights and weekends and we don’t owe you a raise).
The problem is with the entire concept of development "principles". They are a bad way to propagate knowledge. I suspect more people have an incorrect understanding of DRY than not. Seems like a net loss to me.
We should ditch these principles altogether and focus on teaching a deeper understanding of these concepts that captures the nuances.
I don't think you can move away from principles in general. The reality is that most SW design is subjective. Not reusing code is a generally good principle. It's just that the misapplication of DRY is following one good principle but violating another one (requirements should be decoupled).
In any case, the reason I go on the anti-rant rant each time is because when I use DRY appropriately, I don't want some idiot flagging me in a code review saying "Don't do this. DRY is bad. Here are N blog posts explaining why" - when none of the blog posts are complaining about what I am doing.
I don't think you can separate the principle from its misapplication. It's misapplied because it tries to stuff useful knowledge into a memorable phrase and the nuance is lost.
Flagging code in code review is another great example of harmful behaviour principles encourage. I've stopped referencing principles altogether in code review and I encourage others to do the same. Instead I focus on trying to explain the specific impact the code will have on our specific codebase.
> I don't think you can separate the principle from its misapplication.
In practice you are right, but this is just part of the human condition. There is no substitute for experience. Wisdom can’t be taught. The map is not the territory. Yada yada.
Principles still have value as short-hand for knowledgeable practitioners though. In fact, they have outsize value in this case because strong programmers will recognize and reflect on both the upsides as well as downsides discussed here. Communication bandwidth is the single most important thing to scale teams working on irreducibly complex domains.
I also sign up into thought school where “principles” went bankrupt. When I see someone quoting principle as a sole reason for code change it automatically shouts it is shallow explanation without much thought.
We could adopt a principle to solve this problem: something that reminds us that abstractions aren't free.
This is the source of the problem of the blind application of principles - which tend to increase the number of abstractions. They aren't free, and people act as if they are.
Even good abstractions have a cost. But in a good abstraction, the benefits outweigh the costs.
I don’t think it was co-opted, I think it never stuck. PP didn’t invent the idea of deduplication. I’m not even sure they invented DRY. But when that book was brand new there were already people misusing the idea of deduplication.
Structuring code so that abstractions don't depend upon implementation details is in my top 3 principles of all time (along with pure functions and good typing).
DI frameworks a la Spring and Guice just annoy me.
Yep. And then you get bloated constructors taking in a dozen arguments many of which are just needed to pass along to the parent class constructor, and - drum roll - the CI tools complain because your constructor is similar to one in another completely different class that just happens to need similar dependencies due to the abstractions and that's a copy-paste-detection fail.
So you refactor everything, factor out the constructor, and then it passes but now you need to add a new dependency so you're right back to the same nonsense. And/or you have tons of classes getting dependencies they don't even need, because some do so the parent has to have them all.
Traits can help some.
But the abstractions and DI that were supposed to make things easier still often make things more complicated.