The case against DRY

Yoav Nordmann
Israeli Tech Radar
Published in
6 min readFeb 27, 2023

In programming we try to adhere to a lot of principles: YAGNI, KISS, and DRY just to name a few. I myself am a big advocate of adhering to principles and standards, not only in programming, but I digress. While I am very fond of KISS and YAGNI I never really felt at ease with the DRY principle.

What is DRY

DRY stands for don’t repeat yourself. According to Wikipedia, the DRY principle is stated as “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system”.

While this notion sounds very appealing and right, in practice trying to implement this we might sacrifice much more than we gain.

How I see it

I’ve been teaching about microservices for some time now. Having a lot of experience with Micro Service Architecture, I believe it was time to teach others about it, especially where you would go wrong. One of the hot topics is the “shared library” topic. That’s when the debate is heating up.

We’ve all encountered that one library that every project is using. That “commons” library. While the intentions are good, this more often than not becomes one of the worst nightmares of the microservice architecture. It usually creates a dependency structure that leads to a distributed monolith, and that is where all the benefits of a microservice architecture die.

So what’s the problem?

The following items might describe best what I think is wrong with this principle in today’s age, and why I feel so strongly on the subject.

  1. DRY comes from the Monolithic Age
  2. The “only change in one place” excuse
  3. Increased coupling due to Star Schema Pattern
  4. Garbage Collection
  5. Emotional Baggage

Let’s get Down to business

Let me explain those issues in detail in the following sections

DRY is a Monolith Remnant

Looking back when we were developing monoliths, DRY made somewhat sense. All code was in the same codebase and modules were connected to others in intricate ways. Such was life. I am not saying that everything was perfect, but in that scenario, having a central module with reusable functions did not stand out, yes, even made sense.

The essence is that the code was in the same codebase, took part in the same tests and if, god forbid, you were working with a compiled language, chances of discovering a bug upon changes to one of those central pieces of code were likely to be discovered at a very early stage in the development cycle. Some of the reasons against DRY I am mentioning further on still apply even when working with a monolith.

The “only change in one place” excuse

How many times have you heard this? “In case there is a bug, you will have to change the code only at one place instead of in many different places”. Ok, now the flipside: “In case you add a bug to your code, you now added the same bug to all of your different services and modules”. So I don’t buy that argument one bit.

This does not mean I am against separation of concerns. On the contrary, I am totally for it, just not like this. See, in a microservice architecture, a shared library might become a liability if it is not put into the right context.

A shared library that constantly changes, adds a lot of uncertainty to each build and makes the microservice much more dependent on the shared library than I think is healthy. One of the main principles of microservices is their lack of dependencies or their ability to be independently deployable. Having one service introduce a change that causes another microservice to update its dependencies is somewhat playing with that thin line in an uncomfortable manner.

Increased coupling due to Star Schema Pattern

For many reasons, some by design and some just by sheer bad luck, this one shared library will be used by all microservice, causing a star schema dependency pattern which, as stated above, is a big red flag in a microservice architecture.

What is even worse: How will you ever know what microservice is using which functionality of the shared library? If you have only a few microservices, you can still research them, but what if you have 10s or 100s of those?

Garbage Keeps Piling Up

Do any of the following names appear familiar to you: “commons, util, commons-lib, commons-util, next-gen-commons”? I have seen all of them, and the list is not even complete.

Imagine you have a couple of specific functions parsing dates from strings, and you create a “string-utils” module. Good. Now comes the next developer, and adds his “escape characters” string functions as well. Great. And now another developer adds his “to_lower(), to_upper(), to_shorter(), to_longer()” functions as well. After a couple of such developers, you now have an unmaintainable incohesive module of collectible functions. And that’s just the start.

Now, ask yourself what happens if someone needs to introduce a small change to one of those functions. Why bother when he can just add a new one right? Scarier still is if he attempts to fix/change one of those functions…. BAD IDEA!

Emotional Baggage

Say there is a certain capability that is needed in 2 microservices. Just two. Not more. But now you’re thinking: “Should I create a new shared library? What if more services need this functionality as well?” And so you introduce more code, more potential bugs, and more dependencies to all of your microservices, without the actual need for it.

The problem in a Nutshell: Shared Libraries

A basic rule of thumb for shared libraries in a microservice architecture is the following: It shouldn’t be a library if it:

  • Contains business logic or domain-specific code
  • Has frequent changes based on new requirements
  • Causes coupling between consumers

If you want to have shared libraries, and I actually suggest you do, they need to be specific, with the bare minimum of functionality and dependencies, and with a specific and clear name that cannot be misused!

Summary

By now I am sure you understand: I am not a fan of DRY.

I believe the separation of concern must not infringe upon the right of each microservice to be master of its own faith and fortune.

Too many times have I seen a distributed monolith that started when a shared library was introduced with all the best intentions. Beware of the distributed Monolith!

--

--

Yoav Nordmann
Israeli Tech Radar

I am a Backend Tech Lead and Architect for Distributed Systems and Data. I am passionate about new technologies, knowledge sharing and open source.