Two things happened today that reminded me about a blog post I’ve been meaning to write for ages. First I had been talking about meta programming in Ruby with my Rails-developing colleague Johannes. On the topic of meta programming sometimes being overused he said “DRY isn’t everything”. Then later during the day, another colleague of mine, Mikael wrote this on Twitter: “Changing a model based on inheritance to composition. Damn, I wish I knew what I do now 2 years ago.”.
When I first started programming I built web sites using PHP and classic ASP. I often copied and pasted code all over the place creating quite a mess. But at least the sites worked pretty well. Later I studied programming in school, both at the high school level and at the university.
There I learnt about object oriented languages and basic patterns and practices. I especially remember being thought two things: that it’s important to comment your code and that it’s important to keep the code DRY. That is; to follow the Do not Repeat Yourself principle. I also remember being taught that a good way to ensure DRYness was to use inheritance. By grouping common methods into base classes sub classes can share common functionality.
Now, after having worked professionally with software development I’ve slowly come to realize that the focus on not repeating code during my education, and being taught that inheritance can be used for code reuse, is an experience that I’ve shared with many others. I’ve also come to realize that it’s a fallacy.
While DRY is an important principle it’s only just that. A principle. But by focusing so much on it in textbooks and lectures the education system made many of us believe it was the key to being good programmers. The key to being nice to the one maintaining the code once we we’re no longer there. The key to being professional.
Since the years of my education I’ve seen plenty of examples where a developer, often myself, has gone overboard with it. Making the code super dry, or making excuses when it isn’t. Often this level of DRY has been achieved by creating elaborate inheritance hierarchies or by having tons of static utility methods.
But DRY isn’t about ensuring that our applications doesn’t have two lines of code that look the same. It’s one of many guiding principles for writing code that is easy to change. Just like any other principle it has to be applied with the context taken into consideration. Two lines of code might do the exact same thing at a specific point in time. But that doesn’t mean that they are conceptually the same. And in order to effectively apply DRY we also have to know about other principles for writing software that is easy to change into account. Ranging from basic, but not easy, things such as thinking about how expressive our code is to principles such as the Open/Closed Principle and the Liskov Substitution Principle. To name a few. And when we do so we often come to realize that inheritance is often a lousy way to reuse code. Or that reusing code isn’t the key to delivering value.
I wish I had been taught more about the importance of variable and method naming in school. About writing code that speaks to the reader. Code that is clear and concise. Code that solves the problem in an efficient way but also communicates what it does and why from the perspective of the user.
I wish I had been taught more about how to write components that could be changed without actually changing that specific code at all. I wish I had been taught more about polymorphism and composition. About how to structure applications not with a focus on reuse but on change. About why abstractions matter. I’ve written and seen code that is super DRY but that doesn’t contain a single abstraction and that group things in huge inheritance hierarchies. Code that is about as flexible and reusable as a sunken ship.
Blindly applying DRY can quickly do more harm than good. And teaching us that applying DRY is the key to being professional is, well, unprofessional.
PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.
This blog is built with EPiServer Community, EPiServer CMS, ASP.NET MVC and a bunch of other great products. The source code is available for download at the projects page, where you also can read more about this site and my other projects.
read more
Comments
Jed Wesley-Smith 9 months ago
To be honest, this reads more as a treatise against inheritance as a reuse mechanism rather than against DRY itself. In other words, don't make the cure worse than the disease!
Joel Abrahamsson 9 months ago
Well, yes and no. I think exaggerated focus on DRY in conjunction with too little focus on other practices for flexible applications is often manifested as overuse of inheritance. But my point, and experience, is that there are other things to consider than just how DRY the code is. And that striving to be super DRY can often, but not always, lead to accidental complexity and less maintainable code.
Thomas 9 months ago
I really agree about obsession on using inheritance to follow the DRY principle. From what I remember, there was also a focus on writing optimized code that would keep the number of processor cycles to a bare minimum. While this is in some case important, in other cases it could be considered more important to write code that is easy to read and understand. With the processing power of modern computers it's not always necessary to save a few processor cycles. Could it be that the universities are not very up to date with the latest developments in software development principles in the industry?
Thomas 9 months ago
Forgot to mention in my last comment that it was meant in the context of university programming education.
Joel Abrahamsson 9 months ago
My guess would be that it's easier to teach hard concrete things such as that rather than softer things that often have to be teached in the context of real projects.
Bevan 9 months ago
There's an old saying - "learn the rules before you break them". I think stressing the importance of the concepts when learning about a subject is paramount. It forces you to think about what you are doing and leads you down a natural progression from undisciplined cut and paster to swinging too far towards a theoretically correct piece of software, until eventually you move back towards a nice middle path.
Daniel BArlow 9 months ago
It's not about finding a "happy medium" between DRY and C&P or about "knowing when to break the rules", not as such. It goes deeper than that.
First, it's about distinguishing the message "don't have code in two places that does the same thing" (a worthy goal, usually) from "don't have code in two places that looks the same" (a much less important goal) - you need to look beyond the syntactic similarity and ask whether the intent of the code is the same.
Second, it's about recognising that code needs to be changeable in response to requirements changes or changes in how well we understand the requirements. It has direction as well as state. If two modules A and B are refactored to be based on a common ancestor Z because right now they need to do the same thing, what are you going to do in six months time when it turns out that it was only a coincidence that they had this common need, and now one of them needs to do something else instead? Reverse that refactorisation? So should you really be doing it in the first place if it gives the reader a signal that the modules are more fundamentally linked than they actually are?
(These are actually overlapping concerns, in that A and B are less likely to diverge if they share intent and more likely if they merely share syntax.)
Rob 9 months ago
Perhaps a better title for this post would have been "I didn't know to favor composition over inheritance". It's clear in your answer to Jed above that you still conflate stressing DRY with using inheritance and somehow think it can lead to less maintainable code.
DRY is a principle for reducing complexity and maintenance effort and underlies (as opposed to conflicts with) many of the "other practices for flexible applications" and design approaches that wish you had been taught more about.
I would like to gently suggest that, until you understand the foundational role DRY plays, you don't really understand DRY and shouldn't be offering people advice on how to apply it.
Joel Abrahamsson 9 months ago
Daniel, thanks for adding that comment. In a first draft of the post I actually had an example similar to then one you made, and I think it illustrates much of what I was trying to say.
Rob, if that's what you thought I meant by the post (in fact I wasn't trying to advise anyone about how to apply DRY other than to not do it blindly) I'm sorry.
Eric 9 months ago
I liked your article, even though some others might see it as an attack on DRY. I think of it as more like 'moderation in all things' advice.
Peter Ritchie 9 months ago
I agree with Jed. Saying the equivalent of "DRY made me write bad OO code" or "I wrong bad OO code because of DRY" isn't productive.
Paul 9 months ago
Inheritance for code reuse is one of the worst things you can do hands down. It violates just about every good coding practice out there.
Alex Simkin 9 months ago
+1 to Paul
Inheritance is appropriate to express IS-A relationships and nothing else. Using inheritance for code reuse is an anti-pattern.
Now, if you reuse AddTenPercent() method instead of AddSalesTax() method just because the sales tax is happen to be 10% then you are making a mistake, but this is quite obvious.
Tim Scott 9 months ago
Good article. RY is not a problem in itself, but rather a clue that you have a problem. When I spot code that looks repetitive I ask myself two questions:
1) Is this landmine waiting to cause a bug when behavior needs to change?
2) Is my application missing an abstraction?
Answering both questions requires deeper though about the behavior that my application models and how that behavior is most likely to change. The answers tell me how to fix the problem or if there even is a problem.
There's a lot of anti-DRY talk going around lately, and I don't like it. General pronouncements to "not overdo DRY" are unhelpful and encourage lazy coding. I like your article because you peel back the layers a bit and encourage deeper thought.
Cowtowncoder 9 months ago
+1 for "please update the title" -- perfectly valid lamentation of All The Wrong Reasons to Use Inheritance, but this has less to do with DRY principle, which usually is understood to relate more (nowadays, anyway) to more general information duplication, like having to configure one thing in multiple places.