davit.tech

DRY code vs. WET code. Why you should not repeat yourself

24, March 2020 - 6 min read

Intro ☜

DRY - Don't Rrepeat Yourself is a software development principle which states that duplication of code should be close to zero. And the abbreviation WET - Write Every Time or We Enjoy Typing or Write Everything Twice or Waste Everyone's Time is the opposite of DRY principle.

We Enjoy Typing

DRY principle has been formulated by Andy Hunt and Dave Thomas in their book The Pragmatic Programmer first time back in 1999. They say when the DRY principle is applied successfully, a modification of any single element of a system does not require a change in other logically unrelated elements. To understand the principles better let's take a look at some code examples.


Code Example ☜

We will go through code examples written in Python progamming language but this priciple is acceptable also in other programming languages.

Assume that we have a restaurant menu program like below:

def main_dishes():
    print('Restaurant menu')    
    print('For delivery cal to 777 555 999.')    
    print('--------------------------------------------------')    
    print('spicy pasta - 12$')
    print('fried rice - 11$')
    print('fish pasta - 18$')
    print('pasta carbonara - 15$')
    print('--------------------------------------------------')    
    print('Service fees will be added')    
    print('Fee - 10%')

def salads():
    print('Restaurant menu')    
    print('For delivery cal to 777 555 999.')    
    print('--------------------------------------------------')    
    print('cesar - 9$')
    print('greek - 7$')
    print('--------------------------------------------------')    
    print('Service fees will be added')    
    print('Fee - 10%')

def drinks(x, y):
    print('Restaurant menu')    
    print('For delivery cal to 777 555 999.')    
    print('--------------------------------------------------')    
    print('soda - 3$')
    print('water - 2$')
    print('--------------------------------------------------')    
    print('Service fees will be added')    
    print('Fee - 10%')

main_dishes()
salads()
drinks()

Each function(main_dishes etc) outputs a page of menu. On top of each page, we have a piece of information about the delivery and at the bottom of each page we have the information about fees.

Now imagine that we were asked to change the delivery phone number or the percent of the fee for all pages. And if we look at the code we will see those texts are being repeated on each page of the menu. Which is against of DRY principle and it will be hard to complete the new request. As you can see, If you duplicate code, it's not the only effort that you duplicate.

To reduce repeated parts in the code we can have two new functions which will output the header(top) and the bottom of each page. Our code will look like below.


def header():
    print('Restaurant menu')    
    print('For delivery cal to 777 555 999.')    
    print('--------------------------------------------------') 

def bottom():
    print('--------------------------------------------------')    
    print('Service fees will be added')    
    print('Fee - 10%')

def main_dishes():
    header()
    print('spicy pasta - 12$')
    print('fried rice - 11$')
    print('fish pasta - 18$')
    print('pasta carbonara - 15$')
    bottom()

def salads():
    header()    
    print('cesar - 9$')
    print('greek - 7$')
    bottom()

def drinks(x, y):
    header()
    print('soda - 3$')
    print('water - 2$')
    bottom()

main_dishes()
salads()
drinks()

Now if we want to change the delivery number or the fee percent, we will do that only in one place. And our code is now much better in terms of readability and maintainability.


Another Example ☜

So, we have some basic functions which do simple math operations. See the code below:

def add(x, y):
    return x + y

def sub(x, y):
    return x - y

def mul(x, y):
    return x * y

def div(x, y):
    return x / y

And now we received a request to output the execution time for each function that we have. If We Enjoy Typing we will implement this new feature the following way:

import time

def add(x, y):
    start_time = time.time()
    result = x + y
    end_time = time.time()
    print('add execution took {} seconds'.format(end_time - start_time))
    return result

def sub(x, y):
    start_time = time.time()
    result = x - y
    end_time = time.time()
    print('sub execution took {} seconds'.format(end_time - start_time))
    return result

def mul(x, y):
    start_time = time.time()
    result = x * y
    end_time = time.time()
    print('mul execution took {} seconds'.format(end_time - start_time))
    return result

def div(x, y):
    start_time = time.time()
    result = x / y
    end_time = time.time()
    print('div execution took {} seconds'.format(end_time - start_time))
    return result

What we have done is we created a variable start_time and assigned the current time in seconds since the Epoch. Then we assigned the result of our function the a new variable called result. Then, we created another variable end_time and again assigned the current time in seconds since the Epoch. Before returning the result we printed the difference of end_time and start_time which is the execution duration in seconds. As you can see, we wrote/modified 20 lines of code to support this new feature in our program. Not a big deal. But then we got another request to output the execution duration only if it's bigger than a second. We could add if statements to each function in order to complete the new request, but in that case, our code will be even uglier and it will be hard to maintain it. Instead we can make it "DRY compatible". Here is how:

import time

def add(x, y):
    return x + y

def sub(x, y):
    return x - y

def mul(x, y):
    return x * y

def div(x, y):
    return x / y

def time_it(func, x, y):
    start_time = time.time()
    result = func(x, y)
    end_time = time.time()
    duration = end_time - start_time
    if duration > 1:
        print('{} execution took {} seconds'.format(func.__name__, duration))
    return result

print(time_it(add, 3, 4))
print(time_it(mul, 5, 5))

Looks much better, isn't it? And now if want to have another function which also should output the execution duration, we will just reuse the time_it function and to make it work.


Summary ☜

Let's summarize things we have learned. Here are the main advantages of DRY principle.

  • Maintainability

    It's the biggest benefit. No need to copy-paste the new logic all over the place. Or ,if you fixed a bug you can forget fixing it in other places as well.

  • Readability

    DRY code is more readable is most of the cases.

  • Reusability

    Possibility to reuse already written code in other places. In the long run, it will definitely speed up the development process and save you some time.

  • Testing

    Last but not least advantage is to have a testing-ready code. It's proved that testing isolated and small part of the code is much easier. The more big your functions are, the more assertions you have to write in a single unit test to make sure your code works as it should. Which is not considered as a good practice.

That's it. Be Lazy and Don't Repeat Yourself. Let me know your thoughts about DRY and WET in the comments down below. 👇

This might interest you.