by Leon Rosenshein

Don't Be Too DRY

DRY, or Don’t Repeat Yourself is a good rule of thumb. Like all other rules of thumb though, if you were to ask me if you should remove some specific bit of duplication, my answer would be “It depends”.

On the other hand, one of the Go Proverbs is, as Rob Pike tells us,

A little copying is better than a little dependency

So, when should you stay dry, and when should repeat yourself? It depends. It depends on whether or not you’re really repeating yourself or just have the same sequence of characters.

At one extreme you have something like this

numberOfSidesOfSquare := 4
numberOfSeasons := 4
defaultLegsOnDog := 4

There’s three things all set to the same value. Strict DRY might have you believe that you should define a constant called FOUR and then assign that to each of the variables, or write a function, setVariableTo4() that takes a variable and changes it’s value. But that doesn’t make sense. First, the integer 4 is already constant, so defining another constant is redundant. Second, while the action being taken is the same, semantically each of those lines does something different. There is no logical, domain relationship between the number of seasons, the number of sides of a square, and the default number of legs on a dog. The only thing they have in common is the value, 4.

At the other extreme you have bunch of classes that need to send out notifications. They all have functions with a signature something like

function sendNotification(smtpHost string, fromEmailAddress string, toEmailAddress string, subject string, body string) err {

Some have the arguments in a different order. Some don’t default smtpHost and subject if you pass in nil or an empty string, One doesn’t accept a from address at all. Inside the function some validate the inputs, others don’t. The error messages are different. They use different internal flow. If you look at the sequence of characters in the function bodies, they’re all different. No textual repeating at all. But logically, semantically, they do exactly the same thing. They validate the inputs. They transform the inputs into the right format and structures needed to talk to an SMTP host and send an email. The return an error if there’s some kind of problem sending the email. That’s something you should write once. For multiple reasons.

First, it makes future maintenance easier. If the default SMTP host changes you only need to change it in once place, and you can make sure no-one else even knows about it. If you want to change from email to Slack notification (or SMS or anything else) you can do that in one place. Or do them all. Again, only in one place. Second, from a Domain Driven Design standpoint, notification doesn’t belong in those classes. Yes, the classes need to do the notification, and they probably care who’s notified, but that’s it. They shouldn’t know or care how. It’s an external capability they should be told about.

Of course, those two are the extremes. For other cases you need to think more deeply about it. Are the things the same semantically/logically? Are they parts of multiple domains? Are they really parts of those domains or are they used by those domains? Should the functionality be extracted and put somewhere else entirely. Would not repeating yourself add some coupling you don’t want? If you have some coupled things, can you break the coupling by repeating yourself.

TL/DR; being DRY is good, but don’t be too dry. Think about the costs and benefits first.