Looking for more podcasts? Tune in to the Salesforce Developer podcast to hear short and insightful stories for developers, from developers.
102. Whether or Not to Repeat Yourself: DRY, DAMP, or WET
Hosted by Robert Blumen, with guest Ev Haus.
There are many different ways to architecturally structure a program, which has invariably led to debates on which system is "the best." We'll explore several of these strategies--nicknamed DRY (Don't Repeat Yourself), DAMP (Don't Abstract Methods Prematurely), and WET (Write Everything Twice)--with Ev Haus, the Head of Technology at ZenHub, including when each is best to use.
Robert Blumen is a DevOps Engineer at Salesforce, joined by Ev Haus, Head of Technology at ZenHub. Together, they're going over a critique over several methodologies when writing code as part of a large team. First, there's DRY, which stands for Don't Repeat Yourself. It's the idea that one should avoid copy-pasting or duplicating lines of could, in favor of abstracting as much repeated functionality as possible. Then, there's DAMP, or Don't Abstract Methods Prematurely, which is somewhat in opposition to DRY. It advises teams to not create abstractions unless they are absolutely necessary. Last on the list is WET, or Write Everything Twice. This is the idea to embrace duplication whenever possible.
Ev notes that, like many programming absolutes, the success of each strategy depends entirely on the context. DRY, for example, sounds like a really good idea, until it happens everywhere. Suddenly, a chunk of code becomes difficult to reason, as a developer jumps around various method definitions to piece together a flow. DAMP often makes sense as a counterpart to DRY, because if you abstract too early in your codebase, you may find yourself overloading methods or appending arguments to handle one-off cases. DRY is typically best suited for testing environments, where an absolutely reproducible set of explicit steps is often preferable in order to quickly understand what is occurring.
No matter the strategy you use, the core tenant is to solve the problem first. Try to accomplish the goal you need to, whether that's adding a feature or squashing a bug. Don't over optimize until you've finished what you need to, and don't think too far into the future about all the possible edge cases. The rest of the balance comes with experience. Some duplication is bad, but not all of it. Figuring out the absolute perfect solution is unlikely, so you've got to put the code out into the real world to find out what works. After that, bake some flexibility into your processes to adjust hot code paths or refactor them when needed!
Links from this episode
- ZenHub is an agile project management tool for GitHub
- Wikipedia's definition of DRY
- "Using DRY, WET & DAMP code" is Ev's article on different coding methodologies
- Codewars is a website with programming puzzles and challenges
- The Pragmatic Programmer by Dave Thomas is a popular book highlighting some of these concepts
- DRY code, DAMP DSLs by Jay Fields and DRY vs DAMP in Unit Tests by Vladimir Khorikov are more write-ups on the subject
Robert: For Code[ish], this is Robert Blumen. I am a DevOps Engineer at Salesforce. I have with me today, Ev Haus. Ev is Head of Technology at ZenHub and is the author of an article called, Using DRY, WET & DAMP code, which will be the subject of our discussion. Ev, welcome to Code[ish].
Ev: Hi, thanks for having me.
Robert: Ev, I wanted to have you on to talk about the concepts in this article. You present a critique of DRY but before we get to that, I want to understand what it is and why a lot of people believe in it? Will you first tell us what does it stand for and what is the principle that is being advocated?
Ev: Yeah, sure. So DRY stands for, Don't Repeat Yourself. It's a pretty simple programming principle, I believe. I think the fundamental idea here is that, it's to avoid copying and pasting or duplicating lines of code and instead trying to embrace programming language functions, loops, while statements, things like that. So let's imagine for example, we want to write some code that converts every letter of the alphabet to its Unicode value. So you might solve this by writing a bunch of if statements, like if the letter is A, do this, if the letter is B, convert it to that. That would be a very naive way of doing it, right? And so the DRY principle essentially tells you that, "Hey, it's probably better if you loop through every character and invoke some kind of converting method." So basically applying the features of the language itself to try and reduce the amount of duplication and make your code easier to use and to read.
Robert: Have you ever looked at some code you thought could benefit by applying that principle more strictly than it did?
Ev: I've written a lot of code that could also use that principle. I think it's a pretty common principle and I think that's actually something that I think when most developers write code, it's probably something that they encounter literally every single day. Our natural instinct is to solve things in a very human way and humans naturally have a lot of repetition in the way that we talk, in the way that we problem-solve, but repetition is not always the best way for a machine to solve something.
Robert: When you see some code that is not DRY enough, what are some signs of that?
Ev: So the first sign is probably that it's very difficult to work with and probably I would argue, performs pretty poorly. So if you have code that isn't, I suppose DRY enough, it's going to have a lot of repetition in it. So, like that if statement example that I provided and it's probably going to have just a lot of code itself. So it's not very concise, it's not very elegant and so that's probably a good sign that it could use a little bit of DRYness.
Robert: Ev, now that we've covered what DRY is and why people like it. Let's look at your critique. Everything is only good up to a point and you can always have too much of a good thing. In your article you say that overdoing DRY creates code that is difficult to read and understand. What did you mean by that?
Ev: So although the DRY principle I think is a sound principle, I do think you can take it too far. I would argue that I think the DRYer your code, the harder it is to read for humans. There's a pretty excellent statement that I think I first heard on the React Podcast but I don't know where it actually originated but somebody said, "Hey, that code was so DRY that it chafed." And actually I found that to be quite a memorable way to describe it, so it's code that it's so DRY that it's actually painful. Let me ask you this, are you familiar with the app, Codewars? Have you heard that one before?
Ev: So Codewars is a pretty fun website where essentially, it's for developers, you can log in, create an account and it's a place where you can solve programming puzzles and challenges. There's a set of questions and then you can write some code to try and solve that particular challenge. One of the things that I really like about that platform is once you've submitted your correct answer, you get to see the other correct answers from everybody else that's submitted one. And there you can often find the most beautifully concise DRY examples. Somebody always finds the one-liner solution to any given programming problem. And although those examples are incredibly DRY, they're very concise, they have very little duplication, oftentimes they're incredibly difficult to understand, incredibly difficult to read and I would argue are pretty horrible things to see in like a real production use case. So I think DRYness needs a bit of balance. It's a bit of a scale. Things can get too DRY.
Robert: Could you explain in more detail why DRY makes code harder to read?
Ev: Yeah, for sure. So let's take some example, let's say we have a lot of duplication in our code and now we want to try and apply some of the DRY principles, make it a bit more reusable, pull out some pieces that we might want to share, create some utility methods, for example. So the question becomes, where do those utility methods go? You got to put them somewhere. And what I find is more often than not, those pieces of code tend to go all over the place, you've got utility methods in one folder, you got some service definitions over there, configurations probably somewhere else. So it's all nice and DRY, there's no repetition anywhere, the logic is very efficient, but now nobody can really figure out how to read the damn thing because it's all over the place.
Ev: I'm always reminded of... You ever see those TV shows where they're trying to solve a crime of some sort. So they put up all the various little pieces of evidence that they have on a wall, they draw these connecting lines between them, sticky notes everywhere. So I find like our code is a little bit like that, where it's like, we have to draw these connecting lines between all of the things that relate to each other. And sometimes it would be much more efficient to just put all the things that do the same thing in one place. And so I find that DRY code optimizes very well for computers but it's actually quite terrible for humans that are reading it.
Robert: **Another writer on this topic named Vladimir Khorikov has written something to the effect that once you refactor code into methods, you can no longer understand what the code does by reading one piece of code. You have to understand the entire class in order to understand what one piece of code does, is that where you're going with your discussion?
Ev: That's exactly right. I think there's something to be said for making your code easy to read. And I would even argue like reading it from top to bottom is actually quite important, because you have to remember that at the end of the day, you kind of have to consumers for your code, right? The first one is the compiler that's going to read that code, but the other one is all of your programming buddies, all of your colleagues, people that are going to have to maintain that code for however many years to come. So, sometimes purely optimizing your code to be readable for computers is not always the best approach.
Robert: Is there a trade-off between making code easier to read and harder to maintain?
Ev: I think with anything in programming, there's always trade-offs, and that's probably one of the hardest things that we as engineers have to deal with, is finding that right trade-off. It's not always immediately clear for any given problem that you're trying to solve, any given piece of code that you're writing, which principle is the right one? And in fact, the right answer is it's a little bit of all of these types of things.
Robert: Now, that we've covered DRY, there are some other models out there that have their own catchy acronyms, the first one being DAMP. What does that stand for?
Ev: So DAMP is an interesting one because when I started looking into some of these alternatives, I actually found two versions of DAMP, some of these catchy acronyms. So the first one was, Descriptive And Meaningful Phrases and the other one was, Don't Abstract Methods Prematurely. So maybe let's talk about that first one first, the Descriptive And Meaningful Phrases.
Robert: Go ahead.
Ev: All right. So I think for that term, I think the idea is that rather than entirely focusing on the DRYness of your code and removing duplication, I think DAMP tries to say that it's also important for your code to be very descriptive. It's almost like you want to over-communicate what your code is doing, almost to favor humans above computers that are going to be reading that code. So examples of how you might apply some DAMPness to your code, so to speak would be to write more comments or to make the names of your files and functions, components more descriptive, sometimes using just longer variable names and overall, I think the idea is to focus less on code reuse and focus more on code maintainability and legibility.
Robert: All those sound like great ideas but from what you've said so far, I think you could do all those things and refactor code into methods. Are there places where a DAMP and DRY are in conflict with each other and you need to choose DAMP over DRY?
Ev: Yeah, I think it's possible. So, one example I can think of is a while back, I was having lunch with a couple of engineers from Google and I asked them, "Hey, you guys work on a massive code base at Google. How do you reuse your code? Let's say, you got an engineer from the team at Gmail and they want to build some maybe UI element and then the Google Cloud team wants to use it. How do you deal with that? Where do you put it?" And the answer that I heard was actually quite surprising. The answer that they said was that they don't. They don't try to attempt to reuse code, specifically because they're afraid of the cost, right? So they found that the maintenance cost of duplication is actually different than the cost of having to maintain this separate abstraction.
Ev: And so they tend to apply more of some of these DAMP principles when it becomes clear that trying to reuse a piece across lots of different teams with perhaps very different types of problems, just doesn't scale. Now this is something that works for Google at their scale but it might not always work for you at a different size, maybe a start-up. So you have to kind of use your head and try to understand which principles apply in what condition is best.
Robert: You've raised a different point there, which is that trying to refactor many different pieces of code into a single method that works in all cases could turn out to be harder than everyone having their own slightly different copy of the same code. You said there is another meaning of the acronym DAMP-
Ev: Mm-hmm (affirmative).
Robert: ... Don't Abstract Methods Prematurely. Can you talk about that?
Ev: Yeah. So I actually like that acronym a little bit better, I think. I've also heard it being called, the AHA principle as well, which I think stands for Avoid Hasty Abstractions. So the idea with this one's a little bit different. So this one is less about the emphasis on the clarity of legibility of the code, and more about the abstractions that come with trying to re-architect or re-engineer your code. So one of the challenges with DRY is that you have to take that duplication and find some kind of logic, find some tools to remove the duplication, try to make your code more reusable. That act of doing that is essentially to create an abstraction around that. And oftentimes what happens is developers are so passionate about looking for the DRYest way to solve something, is that they create these abstractions quite early in the process before they really understand what this code is going to be doing. And so this principle which says, Don't Abstract Methods Prematurely, essentially warns developers of not trying to do this too soon.
Robert: Do you advocate starting out by writing code as you need it? And then when you see what the abstractions are, you refactor similar code into methods?
Ev: Yeah, I would say so, one of the core tenets that I believe in is start by solving the problem first. Typically you're given some kind of task, you're trying to accomplish something, find whatever it is that you need to do to solve that. And then think about what might be coming down in the future and how you can potentially make this code a little bit more reusable. If you try to account for all the possible edge cases to begin with, well first of all, it's going to take you much longer. You're going to spend a lot of time but the risk which I think is the biggest risk is that you might create the wrong abstraction and the wrong abstraction can often be sort of the disease that hurts your code in the long run because changing that abstraction later on is quite difficult.
Robert: Do you have any stories or examples where you either got this right or got it wrong?
Ev: Even I get this wrong all the time and I don't know of any engineer really that does this perfectly because to be honest with you, this is a hard problem. This is why developers get paid a lot of money to solve these types of problems. So one example that comes to mind is so I tend to work a lot with front-end development code. So I currently work at a company called ZenHub where we built like a task and project management platform for GitHub. So it's like a UI with a lot of dense features, there's a lot of buttons, a lot of elements, drag and drop and that sort of thing. And one of the things that we're considering doing at the moment is to do a bit of a UI pass, to try and think of, "How can we create better standard components for all of our UI elements?"
Ev: A big part of that is probably we're going to need to build a new button, right? Buttons are all over our interface and so what do buttons need though? They need to support different colors, probably different sizes. Some buttons are going to have icons, some of them are not going to have icons, some of them are going to have text, some of them are not going to have text. It'd be great if the button could maybe have a drop down menu, maybe we might want to group buttons together so they can create toolbars. We also don't want to forget about accessibility and keyboard controls, anyway so you can see that the list of these features for just even a simple button can get quite big. And so if you don't adhere to this kind of principle of Avoiding your Hasty Abstractions, you might tend to want to solve all of these problems right from the get-go. And a lot of people do, a lot of people try to build the almighty button that supports all the possible conditions. And they typically find that it becomes very difficult to maintain.
Robert: And what's a better way to handle that?
Ev: So I think it depends. So sometimes duplication is the right answer to do that. Maybe instead of the one almighty button, maybe you have three different buttons depending on particular use cases of each one. Sometimes it's to do the exact opposite, sometimes you do have to pay the price of creating something that might account for all the edge cases, but then you have to be mindful of the fact that, hey, maybe for simple examples, this might be difficult to use, so maybe you emphasize a little bit more on the documentation side of that. So I think this is something that developers almost have to play with and you have to use it in the real world to find out what works for you and what works for your team and for that use case. Typically what I would recommend those, start simple, get something into the hands of your users as quickly as possible, solve the specific problem that you have and only once new challenges appear, start to think about how you can extend the functionality that you have.
Robert: While I've been reading about this issue of there's an idea in the community, people are saying production code, DRYness test code, DAMP. Could you explain what are people thinking? Why they say that and what's your view?
Ev: Yeah, I think there's probably some truth here. I think it makes sense for your test should not be DRY, so in the sense that tests should have some duplication inside of them. So why? What are unit tests typically used for, right? They're used to catch bugs and issues but when those issues arise, somebody has to determine what went wrong, right? So somebody is going to read that test and try to understand what was it testing and why did it fail? So the readability aspect of the test code is actually quite important because somebody needs to quickly jump to that line of code, understand what that test was doing and go, "Oh, okay. I understand why it failed." So if your test code has a lot of abstractions, if it's got like using utility methods, if you're trying to do clever things in your test code, you're wasting a lot of time potentially for developers that might want to quickly understand what went wrong. So I think that's probably where that concept came from.
Robert: Okay. Let's move on. The other alternative principle with a catchy acronym is WET. What is that about?
Ev: So I think WET is a bit of a silly concept, to be honest with you. I think probably somebody was looking for an acronym that was competing directly with DRY, but so WET stands for, Write Everything Twice. And I think this is essentially just a little bit of what we were talking about. It's the idea that, hey sometimes, duplication is actually better, like in the case of maybe unit tests.
Robert: So in your article, you said, you agree with this other writer who says, "We should stop obsessing about writing clean code." What do you mean by that?
Ev: So I think in general, a lot of people focus on this concept of clean code, which I would say means different things to different people, first of all. But I think generally people are looking for some core principles that they could drive their programming with, something that would lead to code that's highly performance, that's maintainable, that's reusable, that's easy to read, all of these good things that we tend to strive for. But I think that obsession about trying to almost over-engineer, over-think about your own code can tend to result in you writing bad code as a result of that. It's a bit of a catch-22, so to speak. So I do think that there are a few good principles that I would recommend. So first of all, write code that works first, so focus on just solving the problem above all else.
Ev: Once you've got that, then maybe focus on code that's easy to read and that's actually quite a hard thing to do, because sometimes when you write code, it's always going to be easier to read for you, you're the person who wrote it. And so this is where I think code reviews play a really important role where you get somebody else to read your code and they can really help you understand, maybe your naming isn't clear, maybe the algorithms that you wrote are too tricky or too confusing. Maybe your code is good but it maybe lacks a little bit of comments to understand what it's doing, or maybe if your code is too DRY, maybe jumps around too much, you have to kind of move from file to file. So those are probably some good principles to start with.
Robert: Great. Well, I think that wraps up what I wanted to talk about. Are there any final lessons or wisdom about when to apply these different principles that you'd like the listeners to take away?
Ev: One of the principles that I really like is to write code that's easy to delete, which is actually something quite difficult to think about, because, well, what does that mean? And I think some of these principles that we talked about today, DRY code, WET code, DAMP code, they can all be used as tools in your toolbox as a developer to help you in this effort. So if you have maybe too much repetition, apply a little bit of DRYness. If you have not enough repetition, apply a little bit of WETness. If your abstractions are too complex, you can simplify them with some principles from DAMP. So overall I think all of these kinds of lessons and all of these principles, once they come together, can play a really important role in making you a better developer.
Robert: Sure. And in the end, we're all trying to create business value for our organizations and that's a multifaceted thing, it includes not only products that work but have a reasonable cost to maintain the products over time.
Ev: Yeah. I think a lot of people just in general forget about the fact that programming is a collaborative task. It's very rare that you have one engineer that writes everything, right? You do it with a team and so the fact that other people are going to be reading your code is inevitable. And so, you should spend at least some of your efforts, some of your time trying to understand the importance of code readability. And this is where some of these tools can help you.
Robert: Ev, I'm going to link to your medium article in the show notes, where can people find you if they like to see what you're up to?
Ev: Yeah, probably the easiest way to get ahold of me is on Twitter. My handle is EvHaus and we can have a conversation there.
Robert: Ev, thank you for speaking to Code[ish].
Ev: Thank you very much. It was a pleasure.
A podcast brought to you by the developer advocate team at Heroku, exploring code, technology, tools, tips, and the life of the developer.
More episodes from Code[ish]
Karan Gupta and Marcus Blankenship
How can applying the right technology choices at the right time impact your coding and business choices? Karan Gupta explains how practicing “pragmatic engineering” can have an oversized impact on business and business efficiency. →
The episode focuses on managing a certificate authority (CA) within an enterprise. The internal CA is compared on many points to PKI on the public internet. →
James Dong and Chris Castle
How much can a day of coding help others? James Dong created a platform to help small businesses impacted by the COVID-19 pandemic sell gift cards online. Learn how this platform, built on Heroku, provided a way for residents to support... →