DRY in the 21st Century

No Comments

It seems that nowadays, the “Don’t Repeat Yourself”(DRY) principle is one of the foundations of programming that is criticized the most. You can find tweets and blog posts questioning it. Also, it seems that critical voices are increasing.

But why is this happening right now? DRY, amongst other principles, is often used when distinguishing good from bad code. To me it was taught at university as an extension of SOLID (making it SOLIDD basically). The principle itself is easy to understand and apply. Examples showing the importance of the principle can be found all over the Internet. Still, there is some rejection.

I would like to offer my opinion here and state why I do not agree with the critics completely. But first, let’s start by looking at where and when the DRY principle was formulated.

The Pragmatic Programmer

As Wikipedia tells us, DRY was firstly formulated in “The Pragmatic Programmer” [1]. In chapter 7 “The evils of duplication” (pp. 26-33) the DRY principle is formulated as

“Every piece of knowledge must have a single unambiguous, authorative representation within a system.”

The authors also mention that

“[…] it is one of the most important tools in the Pragmatic Programmer’s tool box.”

As pointed out by several people, the principle is about “knowledge” – not code, not classes, not methods, not functions,… The principle is basically agnostic of code. But if we keep on reading the book we see that for the authors code is also a form of knowledge; next to comments and documentation. So, whoever points out that DRY is not about code at all did not read the complete chapter. Or, as programmers like to say:

The truth is in the code.

And therefore the knowledge is in the code.

Following reasons for duplications and their explanations, the authors point out that we should “Make it easy to reuse” our code. Regarding the structure of the book, this tip (all of the advice in the book is presented as tips) is as important as DRY, it just did not receive such a nice abbreviation (MIETR is hard to pronounce I guess). So, for the following discussion, I want to look at DRY and MIETR.

Historical Context

Talking about the Pragmatic Programmer, it is helpful to take a look at the time when it was written. Published in 1999, it will see its twentieth birthday next year. Regarding the rapid development in Computer Science 1999 might not be the Stone Age, but something like the Renaissance. Since I was more interested in my PlayStation back then, I asked my older colleagues what some of the hot topics those days were:

  • OOP
  • JavaScript
  • PHP
  • J2EE
  • CVS

Next to the technological aspects, there are also some other things worth mentioning:

  • Java 1.2 was the most recent Java version.
  • The Internet Bubble was still rising.
  • Netscape lost the Browser Wars.
  • The Apache Foundation was founded.
  • Apache Ant and Maven were not invented yet.

So what can we say about this time? The majority of applications were monolithic. Vertical scaling dominated over horizontal scaling. OOP was probably seen as the solution for all problems (which lead to huge class hierarchies, which we try to solve now by favoring delegates over inheritance). Building/packaging applications was most probably a bigger issue than it is nowadays.

All in all, I would say that reusing code, either within a project/company or outside of it, was a much bigger problem in 1999 than today. No Apache, no GitHub, SourceForge just started. I do not want to imagine having the problems of unreliable dependencies that you cannot look into because some closed-source library was bought from some vendor. Copying everything into big /lib directories is also an issue from the past.
In this context, having code that is easy (and fun) to reuse might have been a rare occasion. Therefore, raising developers’ awareness of MIETR seems more important than advising them to follow the DRY principle.

Having seen the past, let us go back to today.

Microservices, DDD, Serverless…

What we can easily agree on is that times have changed since 1999. Nowadays, we have Microservices or even Pico- and Nanoservices. Using a serverless approach for an application is also rising to the top on the Gartner Hype Cycle. Splitting your application into its domains and bounded contexts is also more mainstream these days.

From my point of view, as Microservices rose up, skepticism about DRY increased, too. Sharing code between different Microservices is considered bad (not without reason, of course). But still, extracting code into a shared library also encounters resistance, even within a project. So, the solution is obvious: code has to be copied. And this violates the DRY principle. One could argue that by trying to make DRY irrelevant, developers only try to retrospectively justify their code duplication, since they were either too lazy or too ignorant to create a shared library. But I think this argumentation falls short.

Philosophically put, the problem might be within our education as developers. Everything is binary, DRY is either ON or OFF. There is no 30 % DRY. MIETR is not (easily) measurable, and what we cannot measure, we cannot control and therefore is ignored.

Having the current context and a philosophical thought, let’s take a look at some concrete examples.

Examples

In this section we will take a look at two examples to see how DRY helps us keep our code clean. Hopefully, I can convince you that the application of DRY, at least in those examples, is advantageous.

UriComponentsBuilder

A class that I have used a lot recently is the UriComponentsBuilder. This class helps us transform a URL with placeholders like http://{host}:{port}/{username} into its expanded form http://localhost:8080/admin. Additionally, it can add query parameters to the request.

If I was not using this class, I would have to implement my own string replacement logic. And this replacement logic would have to be copied across all places where I would need a URL with placeholders. Changing e.g. the markers of a placeholder to something different than braces would require a lot of changes in the code. Of course I could create my own Helper/Util/… class that contains the replacement, but this would be DRY, nearly as DRY as using the Spring class.

But why would I want to violate DRY here? This class serves me very well. MIETR is also “high” because the API is well documented and I can use the class without looking into its internals.

Table Name

Another example would be a table name that I need in my code. Following the recommendations from The Pragmatic Programmer, I would create a constant somewhere in my code (or even extract it from the SQL file that creates it) that contains the table name. Whenever I have to access the table, I use the constant. If the table name changes, there is only a single place where the code has to be changed. Again, simply following DRY. Without this constant, several files would need to change in case of a renaming. This would be quite error-prone because I could easily forget a place to change it (and according to Murphy’s Law, I would). Importing a constant somewhere in my code is also quite easy to do, so MIETR is taken into account, too.

Putting DRY into Context

I hope that you agree with me so far on the DRY principle.

Of course you might argue now that those are only technical examples. The UriComponentsBuilder is a helper regarding my HTTP communication and a table name is also technical, nothing businessy. But still, they represent knowledge. The knowledge about what placeholders in my URLs look like and the knowledge about the table name.

So, to put it in a more abstract way, within the context of URLs, the UriComponentsBuilder is my single source of truth for replacements. And within the context of my database, the constant with the table name is the single source of truth for that name. I got to admit that my knowledge in this area is rather limited but this sounds like a Bounded Context to me.

So, what if – and only if – we should no longer apply DRY “globally”, but rather within a Bounded Context?

Why DRY is still relevant

In my opinion we will return to darker times if we start praising the rejection of DRY as being a trait of experienced programmers. The principle is still relevant and should be taken into account when writing code.

What I also think is that we should adjust DRY to the current times. If your shopping application needs to represent a customer in the context of purchase and in the context of shipping – go on, copy it. Create two customer representations. As the DRY opponents state, those two representations will differ more and more over time and forcing it into the same class for the sake of DRY would cause more harm than good. Or, as the author of “Domain Driven Design” [2] stated (p. 344):

“Code reuse between Bounded Contexts is a hazard to be avoided.”

Also, if I have to instantiate an object with parameters I only use in half of my application or which I set to null just because something has to be passed into the constructor, it is no convenient reuse. It is rather a burden and a possible source of errors.

Blindly applying DRY is as false as ignoring it. I personally like the rule of thumb that if something occurs identically three times or more, then it should be refactored and stored in a common place.

To summarize, apply DRY within your context or unit of deployment. Don’t DRY out everything up front but rather look for reccurring patterns. And if you extract and abstract something, keep MIETR in mind.

Lastly, to repeat myself: Don’t repeat yourself within your context.

References

[1] Andrew Hunt and David Thomas. 2010. The Pragmatic Programmer: From Journeyman to Master. 25th Edition. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA.
[2] Evans. 2004. Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA.

Ronny Bräunlich

Ronny works since May 2017 for the codecentric AG. He is convinced about TDD and works mostly in the JVM ecosystem.

Comment

Your email address will not be published. Required fields are marked *