Scroll to top

That's not DRY

By Tzvi Melamed

Do you think your code is DRY? What about this code:

function isAdmin(user) {
    return user.role === ADMIN;
}

function canAccessDashboard(user) {
    return isAdmin(user);
}

function canChangeRoles(user) {
    return isAdmin(user);
}

I have seen code reviews in which people comment that this type of code violates the DRY principle. I don't believe it does. In this article, I'll dive into the true meaning of DRY as it pertains, not to code, but to knowledge. I hope to convince you that the code above is not only DRY, but actually much "cleaner" than this smaller alternative:

function isAdmin(user) {
    return user.role === ADMIN;
}

Dave Thomas and Andy Hunt, in their book "The Pragmattic Programmer" define DRY as follows:

the DRY principle: EVERY PIECE OF KNOWLEDGE MUST HAVE A SINGLE, UNAMBIGUOUS, AUTHORITATIVE REPRESENTATION WITHIN A SYSTEM.

(Hunt, A., & Thomas, D. (2010). The Pragmatic Programmer : From Journeyman to Master (p. 27). Addison Wesley Longman, Inc. ; emphasis is my own)

Notice that they don't define DRY as:

EVERY PIECE OF CODE MUST HAVE A SINGLE, UNAMBIGUOUS, AUTHORITATIVE REPRESENTATION WITHIN A SYSTEM

To understand this distinction, I'd like to look at a quote from Martin Fowler:

The fact that size isn't important was brought home to me by an example that Kent Beck showed me from the original Smalltalk system. Smalltalk in those days ran on black-and-white systems. If you wanted to highlight some text or graphics, you would reverse the video. Smalltalk's graphics class had a method for this called 'highlight', whose implementation was just a call to the method 'reverse'. The name of the method was longer than its implementation - but that didn't matter because there was a big distance between the intention of the code and its implementation.

(Fowler, M. (2016). bliki: Function Length. Martinfowler.com. https://martinfowler.com/bliki/FunctionLength.html; emphasis is my own)

In discussing code duplication, it can be helpful for us to understand why the DRY principle exists in the first place. Business requirements are constantly changing and code must adapt quickly.

Imagine we are told to create a website with blue buttons. Tomorrow, the PM comes to us and says "we actually decided to go with red instead of blue". If we had hard coded blue across every button in our codebase, we'd have to go change every place and we may miss some places leading to inconsistent UX.

On the backend side of things, we might decide that every user is identified by their email address. Suddenly, we decide to introduce user profiles with a user-chosen id and allow users to have multiple email addresses. Now, users need to be identified by a different unique id.

In embedded programming, we may be writing code for a certain piece of hardware, but then market conditions change and we swap out a new chip with a different pin configuration or instruction architecture.

In all these cases, there was a piece of knowledge that changed:

  • In the front-end example: "primary button color"
  • In the backend example: "unique identifier for a user"
  • In the embedded example: "pin that controls X" or "ISA"

In simple cases, we might have gotten away with creating a constant such as const BUTTON_COLOR = 'blue';. But imagine that blue was the primary color for the entire site. We migh have had something like PRIMARY_COLOR = 'blue'; and then hard-coded every button with color={PRIMARY_COLOR}. In this case, the knowledge that "the primary button color is the same as the primary color of the website" is duplicated throughout the code. One way to avoid this would be something like the following:

const PRIMARY_COLOR = 'blue';
const PRIMARY_BUTTON_COLOR = PRIMARY_COLOR;

// ...

color={PRIMARY_BUTTON_COLOR} // for each primary button

In this code, the alias of const PRIMARY_BUTTON_COLOR = PRIMARY_COLOR; is not duplication, but an additional piece of knowledge. When the PM comes to ask us to change the color to red, we simply change that one line:

- const PRIMARY_BUTTON_COLOR = PRIMARY_COLOR;
+ const PRIMARY_BUTTON_COLOR = 'red';

So, let's go back to our original question:

Do you think your code is DRY? Where are you reusing code in your codebase in the name of DRY while duplicating unrepresented knowledge?

Hopefully you enjoyed this brief exposition of the literature on the DRY principle and you now see how duplicating code can actually prevent duplicating knowledge.

If you have any ideas for how I could improve this article or anything else you wish to discuss, shoot me an email at tzvi@tzvipm.dev. Happy coding :)