In Defense of DRY

What the DRY Principle is really about, and how to get all of its benefits with none of its drawbacks.

Issue 29 /

Hey friends 👋 I hope you had a fun week and an even funner—and funnier—weekend. My week was pretty good, but it was so busy. I had a backlog of things on my to-do list that were all due last week, so getting through all of them was a real struggle. Did I finish everything I wanted to do? No. But did I finish everything I needed to do? Also no.

Two quick things before we begin: the Fundamentals course is in early access right now, and I've scheduled the GA launch for the week of the 26th (about 8 days from now.) It's been a long and hard ride but we're almost there! I can't wait to share this with you all.

Also, this week I started experimenting with Substack. I've created a Frontend at Scale publication, and I'll be posting some of the old newsletter essays over there. The Substack is not a replacement for this newsletter, though. The idea is to publish the essays by themselves (no links, resources, or any of the other sections you might find in here), and I'm not even sure I'll use it for email yet. I thought it could be a good way for more people to discover how incredibly cool the Frontend at Scale community is, so I hope to keep the experiment going for a little while :)

In today's issue: we'll share some love for the DRY principle in honor of its 25th anniversary, we'll talk about refactoring messy React components, and we'll get permission from Kevin Powell to over-engineer our CSS.

Let's dive in.

SOFTWARE DESIGN

In Defense of DRY

Photo by Carolina Pimenta on Unsplash​

If you’ve been writing software for longer than a day, it’s probably safe to assume that you’re familiar with the DRY Principle, which stands for Don’t Rewrite Your-codebase-every-six-months—I mean, Don’t Repeat Yourself.

(Don't rewrite your codebase every six months is still good advice, though.)

Depending on how much exposure you’ve had to the DRY principle, you likely also have feelings about it.

And by feelings, I mean you probably don’t like it very much.

We’ve all seen how bad things can turn once we start chasing every duplicated line of code in an effort to not repeat ourselves—messy abstractions, incomprehensible logic, and functions that do sixteen jobs at once are some of the most common symptoms of what I like to call “duplication removal gone wrong.”

And because it’s such a common problem, there’s no shortage of articles and conference talks that go deeper into this topic as well, sharing experiences and stories that we can certainly all relate to.

All this criticism about DRY can lead us to believe that it’s just not a very good principle to follow after all. If trying to remove duplication will inevitably result in growing complexity and poor abstractions, then maybe we would be better off without it.

But I think that’s the wrong conclusion to take from these experiences. So today, I want to share a few arguments in defense of the DRY principle to hopefully show you that we shouldn’t think of it as our enemy.

In fact, I’ll go one step further. I’ll try to convince you that, in the battle against complexity, DRY is one of the most powerful allies we have.

DRY Isn’t Just About Code

We have to start by talking about the most common misconception people have about DRY, which is that it’s just about removing duplicated lines of code.

In reality, DRY isn’t about duplication of code, but about the duplication of knowledge.

Here’s how Andrew Hunt and Dave Thomas defined the DRY principle in their classic 1999 book The Pragmatic Programmer:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

The book goes on to explain how duplication arises in software systems and what strategies we can use to remove it. But to be fair, there isn’t a clear explanation of what the authors meant exactly by “duplication of knowledge” and how it is different from mere duplication of code.

So in the 20th anniversary edition of The Pragmatic Programmer, Dave and Andy added a short note to the DRY chapter in an effort to set the record straight:

Let’s get something out of the way up-front. In the first edition of this book we did a poor job of explaining just what we meant by Don’t Repeat Yourself. Many people took it to refer to code only: they thought that DRY means “don’t copy-and-paste lines of source.”
​
That is part of DRY, but it’s a tiny and fairly trivial part.
​
DRY is about the duplication of knowledge, of intent. It’s about expressing the same thing in two different places, possibly in two totally different ways.

Duplication of knowledge often happens in the code itself, but this isn’t always the case. Take a look a this function, for example:

What do you think, is this code snippet DRY?

There is no duplication of code in this function, so at first glance, this snippet looks as DRY as those flavorless rice cakes I tend to buy when I’m trying to lose weight. But if we look at the block comment above (which is as much part of the system as the actual code), we’ll see that there’s a ton of duplication of knowledge.

The entire logic of this operation is defined in both the body of the function and the block comment. If you change one, you’ll have to remember to change the other. If you change the code and forget to change the comment, then you no longer have a source of truth.

This function already has pretty descriptive variable names, so to make it DRY, we'd only need to remove the math from the block comment. This isn't an argument against comments, of course, but if all they do is repeat what the code is doing, then we'd be much better off without them.

Here's where the DRY principle truly shines—it helps us avoid the contradictions that emerge when we forget to update all the places where a single piece of knowledge is represented.

And as Dave and Andy very eloquently put it in their book, “It isn’t a question of whether you’ll remember: it’s a question of when you’ll forget.”

Not All Code Duplication is Knowledge Duplication

Another very important—and often overlooked—aspect of DRY is that code duplication isn’t always knowledge duplication.

Take these two functions, for example:

How tempting it is to create a shared function for this validation logic, huh?

There’s certainly code duplication here—the bodies of the two functions are identical. But is it knowledge duplication? Not quite.

The validation rules for username and filenames are two separate pieces of knowledge. Yes, they happen to have the same exact rules in this case, but that’s just a coincidence.

In this example, moving the validation rules to a reusable shared function in an attempt to remove the duplicated code is a surefire way to create a poor abstraction. The shared function would have two completely different jobs (violating the Single Responsibility Principle), it would make it very hard to change the validation rules of just one of these entities in the future, and also, what would we even call that function? validateUsernameOrFilename? That just doesn’t sound right.

Aggressively trying to remove all duplication from our codebase as soon as we find it is one of the most common reasons we end up with poor abstractions and hard times.

But as we just saw, this isn’t what the DRY principle says, so I think it’s a bit unfair to blame it for that.

DRY and Reusability

Ok, but what about when the duplication of knowledge is in the code? It’s definitely OK to use DRY to create a reusable abstraction in this case, right?

Yes, absolutely. But also, not so fast.

Reusability is certainly one of the main benefits of DRY, but attempting to create a reusable abstraction too soon has a tendency to blow up in our faces in the not-so-distant future.

You’ll know it’s too soon if your reusable component ends up solving multiple problems at once, has tons of conditionals to support multiple use cases, or tries to solve problems you don’t yet have—which is a code smell known as speculative generality.

Identifying this “premature reusability” is something that mainly comes with experience and thinking deeply about the code you’re writing. You’ll likely fail at it more than once, but over time, you’ll develop an intuition about when it’s the right time to come up with a reusable abstraction.

And when in doubt, remember that it’s always safer to live with the duplication a bit longer. Because, as Sandi Metz taught us over a decade ago, it is far easier to deal with a bit of duplication, than it is to deal with the wrong abstraction.

That's me with my copy of The Pragmatic Programmer—the birthplace of the DRY Principle, and a very good book you should definitely check out.

A couple of issues back, we talked about the role of coupling in software design and how there’s a strong correlation between the level of coupling in a system and the cost of maintaining it.

We also saw that, in the original definition of coupling in the book Structured Design, the authors described it as “the probability that in coding, debugging, or modifying one module, a programmer will have to take into account something about another module.”

A big part of our jobs as developers, codebase maintainers, and protectors of the realm is to reduce this probability as much as possible—because in doing so, we’re making the entire system easier and cheaper to maintain.

And the DRY principle, despite its obvious flaws and sometimes poor reputation, is a fantastic way of doing just that.

ARCHITECTURE SNACKS

Links Worth Checking Out

Conquering Complexity by Phil Nash

đź“• READ

  1. Alex Kondov wrote a step-by-step guide on how to refactor a messy React component and hopefully prevent complexity from takign over your codebase.
  2. Interesting case study of how Notion improve their app's performance by implementing a client-side cache using SQLite.
  3. Why use the best tool for the job when you could, as Markus WĂĽstenberg does, use Go to build pretty much anything?
  4. If you're looking for a way to keep your frontend codebase organized, check out Feature-Sliced Design—an architectural pattern for component-based applications.
  5. Kent C. Dodds wrote a deep dive on how to deal with forms in modern React. No useState required.

​

🎥 WATCH

​Start over-engineering your CSS by Kevin Powell

Over-engineering, you say? Finally, something I'm actually good at. If you've been struggling with trying to incorporate some of the latest features of CSS into your projects, this talk by Kevin Powell (who you might know from his excellent YouTube channel) at CSS Day 2024 has some pretty good advice. As Kevin says, the fact that a pattern is new and unfamiliar (I'm looking at you, clamp() function) doesn't necessarily mean that it's worse than the existing conventions. The latest features of CSS are incrediblity useful and, in many ways, pure magic—and the only way to familiarize ourselves with them is to use them more often.

​

🎧 LISTEN (OR WATCH!)

​Getting started with frontend architecture w/ yours truly

I had the pleasure of joining Ido Evergreen on his YouTube channel a couple of weeks ago to talk about all things frontend architecture and software design. This was a pretty fun chat where we covered everything from what exactly is frontend architecture, some tips for getting started with this discipline, and my favorite resources to take your skills to the next level. We also talked a bit about the origins of Frontend at Scale, which is something I've never had the chance to talk about before. If my ramblings in this newsletter are somewhat useful or entertaining, I'm sure you'll enjoy listening to this conversation with Ido.

That’s all for today, friends! Thank you for making it all the way to the end. If you enjoyed the newsletter, it would mean the world to me if you’d share it with your friends and coworkers. (And if you didn't enjoy it, why not share it with an enemy?)

Did someone forward this to you? First of all, tell them how awesome they are, and then consider subscribing to the newsletter to get the next issue right in your inbox.

I read and reply to all of your comments. Feel free to reach out on Twitter or reply to this email directly with any feedback or questions.

Have a great week đź‘‹

– Maxi

Is frontend architecture your cup of tea? 🍵

Level up your skills with Frontend at Scale—your friendly software design and architecture newsletter. Subscribe to get the next issue right in your inbox.

    “Maxi's newsletter is a treasure trove of wisdom for software engineers with a growth mindset. Packed with valuable insights on software design and curated resources that keep you at the forefront of the industry, it's simply a must-read. Elevate your game, one issue at a time.”

    Addy Osmani
    Addy Osmani
    Engineering Lead, Google Chrome