Why the DRY principle isn't always the right answer

Looking into the various ways we use the 'Don't Repeat Yourself' principle and the reasons against sometimes using it.

By Luke Whitehouse on

Workflow

Article

The Don't Repeat Yourself (DRY) principle is a core design pattern across the whole spectrum of Software Development and the Web Industry is no exception.

Whilst a great principle to follow, there are times where the trade off from this workflow outweighs it's benefits and it's those moments that I see developer's struggle with time and again.

When do you follow the DRY principle and when do you decide it's not fit for purpose?

In this post I'll aim to answer exactly that. As a refresher for you veterans and an introduction for those of you not familiar, I'll go over the intricacies of the pattern to understand what it is and why it's so useful, and then provide examples in order to determine why the principle shouldn't be used in all scenarios.

What is the Don't Repeat Yourself (DRY) principle?

As the title may suggest, DRY revolves around removing repetition in your projects. Whilst not the case for all situations, generally we look for the best possible product with the least amount of code. The more code we have, the longer it will take to operate, which increases code complexity, decreases maintainability and eventually reduces performance on the whole.

It's the decision of "should I, shouldn't I" that is a constant struggle for developers new and old and one that DRY aims to solve.

The Don’t Repeat Yourself (DRY) principle states that duplication in logic should be eliminated via abstraction; duplication in process should be eliminated via automation.

DevIQ - Don't Repeat Yourself principle

By committing to this philosophy, you will be providing your projects with some significant benefits that far outweigh the hand-strain from writing out the same thing twice (although wrists have feelings too!), these include...

Project fragility

Splitting code into separate files (also known as Abstraction) ensures that any references to the same functionality are housed in one central location. Should you need to change that piece of code then you only need to update it in one place, rather than having to manually update every reference throughout your project. This is a common practice in Software Development called the Single Source of Truth (SSOT).

We can utilise this idea of SSOT in many different ways, even in how we organise our CSS code using more modular techniques such as Object Orientated CSS, BEM and SMACSS. Example time!

Going to the popular Web Design publication, Smashing Magazine, you'll see a number of components on their website. For this demonstration I'm interested in two:

  • Author component: Shown at the bottom of blog posts to credit the author of the post.
  • Sidebar promotions: Seen in the right-hand sidebar for advertisements.
Fig 1: Screenshot of the author component on the Smashing Magazine website.
Fig 1: Screenshot of the author component on the Smashing Magazine website.
Fig 2: Screenshot of the promotional sidebar component on the Smashing Magazine website.
Fig 2: Screenshot of the promotional sidebar component on the Smashing Magazine website.

Whilst these components are visually different, structurally they are very similar and can ultimately be built from the same foundations. They both feature the idea of an image floating to the left of text, so we can tackle both of these instances with one module, the media object.

Note: These examples will be responsive so take a look at the desktop view for the desktop view, etc

http://codepen.io/_lukewh/pen/ggmPBV

Rather than duplicating our code twice, we can use these classes in conjunction with others to create the differences we see in our Author and Sidebar Promotions components, such as: positioning, colours, sizing and more.

http://codepen.io/_lukewh/pen/EgdYwR

This is one way where the DRY principle can shine, rather than creating new classes for our two components and repeating our code, we're reusing what we already have, essentially halving our code.

Cost efficiency

Writing less code is a natural money saver as your time can be spent on other areas of your applications. Going back to the previous example on our CSS components, if we're saying that one component takes 10 minutes of time to build, then building both of them from scratch could take double that. Of course, this is only a small example, but applying this principle to a more complex situation can help solidify it's importance.

An example might be a blog that requires a date formatting. On this blog the date is formatted in two places, on the listing and post pages.

Using the Carbon DateTime extension for PHP, we can create a new instance of the class and then echo out the correctly formatted date.

// listing.php
$published_date = new Carbon($post->published_at);
echo $published_date->format('jS F Y');

// post.php
$published_date = new Carbon($post->published_at);
echo $published_date->format('jS F Y');

Instead of duplicating our code like this, we can create a universal date formatting function that can be used throughout our application, even for future uses of dates that are not currently implemented.

// helpers.php
function formatDate($datetime, $format) {
  $date = new Carbon($datetime);
  $date = $date->format($format);
  
  return $date;
}

// listing.php
echo formatDate($post->published_at, 'jS F Y');

// post.php
echo formatDate($post->published_at, 'Ymd');

In addition, we've also continued the flexibility of our code by not assuming that every date uses the same format. By allowing for the format to be set when using the function you're not pushing yourself or other developers into a hole to climb out of should you need to change this at a later date.

Reasons not to use the principle

So you may be thinking that the DRY principle rocks. Let's use it everywhere!? No! As with everything in web development, there are pros and cons. Let's take a look at the cons.

Code complexity

If not left unchecked code abstraction can leave a project susceptible to a form of the very thing we're trying to avoid - technical debt.

Technical debt is a concept in programming that reflects the extra development work that arises when code that is easy to implement in the short run is used instead of applying the best overall solution.

Techopedia dictionary

Usually Technical Debt refers to the idea of taking the lazy approach to your code. However, elements of this can still linger even with good intentions.

Learning by example, let's take a look at some more CSS.

.btn {
  display: inline-block;
  padding: 1em .5em;
  color: white;
  background: skyblue;
}

.promotion {
  display: block;
  padding: 1em .5em;
  color: orange;
  background: skyblue;
}

.author {
  padding: 1em .5em;
  color: black;
  background: skyblue;
}

Assuming this snippet is part of a larger project, a budding developer seeing this may take one look and realise there is repetition in the code, so abstraction is needed.

.padded-blue-box {
  padding: 1em .5em;
  background: skyblue;
}

.btn {
  display: inline-block;
  color: white;
}

.promotion {
  display: block;
  color: orange;
}

.author {
  color: black;
}

Cool, so that's going to reduce our bytes a little, especially compressed, which in a large application may save us a few KB. However, there are side effects.

The first is that our code is harder to read. A new developer coming onto the project will need to ensure that they know which components are using this new class, requiring them to look in HTML files rather than just the CSS and increasing risk as things may be missed.

The second is that we've dug ourselves into a corner. In order to reduce file sizes by a few bytes we're now assuming that all .btn, .promotion and .author components will always have the same padding and background color. If this ever changes, then you'll likely be increasing the source code to add a new version of this, or have to remove that completely altogether.

Before abstracting your code first ask yourself whether or not you're abstracting because of coincidence or because the functionality is supposed to be the same. In addition, weigh up the benefits. What are you actually gaining from doing this? If it's a few bytes, would the developer uptake on this abstraction not suffer? Would time not be better spent in other areas?

Unfortuantely there's no right or wrong answers with these concepts. Principles by nature are not set is stone and therefore should not be followed 100% of the time. By asking whether or not we should do something, we can save the project from future technical debt moving forward; future you will be grateful.

If there's one thing I hope you take from this it's that there is a trade off between abstraction and maintainability that needs to be weighed up at all times. Principles are set of guidelines to follow when possible, not a set of rules we must adhere to at all times.

Until next time 

Follow us on Twitter, Facebook or Github.
© Copyright 2021 Assortment.
Created and maintained by Luke Whitehouse.