The goal of the DRY principle is to avoid duplication of knowledge across code, documentation, database schemas, tests, builds, etc.

Why duplication of knowledge is bad

  • More work - When changes are needed, they need to made in multiple places
  • Complexity - When changes are needed, it is harder to know/find all points of change
  • Fragility - When changes are needed, it is easy to miss duplicated points, creating multiple conflicting sources

To explain what DRY is and is not, lets look at some examples. All examples are over simplified for the sake of easy understanding. There are many other design principles which should be applied but we are only going to consider DRY so that we can isolate it as a principle.

public class Traveller
{
    public void GoHome()
    {
          Car car = new Car();
          car.Start();
          car.Gear();
          car.Accelerate();
          car.Goto("Home");
    }

    public void GotoWork()
    {
          Car car = new Car();
          car.Start();
          car.Gear();
          car.Accelerate();
          car.Goto("Office");
    }
}

In the above example GoHome and GotoWork do the same fundamental logic, if a change was required it would mean making the change in multiple places. To make this code DRY we can extract the duplicated code out.


public class Traveller
{
    public void GoHome()
    {
        GoTo("Home");
    }
    
    public void GotoWork()
    {
        GoTo("Office");
    }
    
    private void GoTo(string location)
    {
        Car car = new Car();
        car.Start();
        car.Gear();
        car.Accelerate();
        car.Goto(location);
    }
}

Now that the code is shared, changes can be made in one place and are guaranteed to be applied to both GoHome and GotoWork.

The DRY principle was first introduced in the “The Pragmatic Programmer” by Dave Thomas and Andrew Hunt. Dave Thomas later said:

"Don't Repeat Yourself (or DRY) is probably one of the most misunderstood parts of the book. Most people take DRY to mean you shouldn't duplicate code. That's not its intention. The idea behind DRY is far grander than that. DRY says that every piece of system knowledge should have one authoritative, unambiguous representation. Every piece of knowledge in the development of something should have a single representation."

-The Pragmatic Programmer (2000) by Andy Hunt & Dave Thomas

DRY (Don’t Repeat Yourself) was the first principle I learnt as a developer, and for most developers it is in-grained as a must do and to never question it. While the DRY principle is important I would like to point a caveat and how to get around it. Throughout my early career every time I saw two lines of code which were exactly the same I would refactor it so that the code was shared. Eventually I found that I was scared to make changes to code because any change would affect multiple areas due to the coupling caused by removing all duplication.

For example:

public class Cat
{
    public void Hunt()
    {
        Console.WriteLine("Kill prey");
    }
}
    
public class Owl
{
    public void Hunt()
    {
        Console.WriteLine("Kill prey");
    }
}

In this example the Hunt method is duplicated, so a developer may share this code to make it DRY:

public class Hunter
{
    public void Hunt()
    {
        Console.WriteLine("Kill prey");
    }
}

public class Cat: Hunter
{
}

public class Owl : Hunter
{
}

The idea is that when you need to make changes to the Hunt method you only have to change it in one place. The problem comes in when the change is: “cat’s must stalk their prey”.

public class Hunter
{
    public void Hunt()
    {
        Console.WriteLine("Stalk prey");
        Console.WriteLine("Kill prey");
    }
}

If the developer does not know that the Cat class is sharing that logic with other classes then all other classes will be incorrect. This is a simple example and is exacerbated when there are many classes sharing this code and there are many lines of shared code. When code is highly coupled and shared, then developers often feel like they are dealing with spaghetti where a change can break or affect unrelated areas.

"When following DRY, it is quite common that people start building coupling and complexity into their software."

-Gregg Young

If the developer is aware of the shared code they will have to create some further solution to work around the requirements. For example, “if” statements can be added to the Hunt method so that the behaviour changes per animal class, but this will greatly increase complexity with every new requirement. So the best solution is to go back to each class implementing its own Hunt method.

If code is duplicated but is expected to diverge and change independently then it should NOT be shared. On the other side, if you expect a change would need to be applied everywhere because it is inherently the same knowledge then it should be shared. Remember that DRY is not about avoiding code duplication but rather about avoiding knowledge duplication.

If you are unsure of how the codebase will evolve and don’t know what changes to expect then remember YAGNI (You Ain’t Gonna Need It), and simply duplicate the code. When change occurs and you find that the code really should be shared then you can refactor towards DRY. This is the better solution because it is generally easier to refactor towards DRY then it is to move away from it, plus if DRY is not needed then you avoid the work of refactoring towards DRY and then away from it.

Check out these links for more info:

My design and architecture repo