The DRY principle

DRY is an acronym for "Don't Repeat Yourself" (or sometimes do not repeat yourself). The DRY principle has been developed by Andy Hunt and Dave Thomas in their book The Pragmatic Programmer.

The DRY principle is stated as "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system". In other words, the DRY principle is focused on avoiding the repetition of information.

When you write code that performs the same tasks over and over again, any change of one task requires the same change to be made to all instances of that task. A lot of work and wasted time could go into editing every instance of that task.

The DRY coding approach will make the code easier to follow and read (for others, but very important, for your future self). This will support reproducibility of your code.

Another advantage of using the DRY coding approach, is that your code will be easier to update because you only have to update your code once, rather than updating it in every code block where it is used.

A practical example

We want to calculate the average grade of five students. Here is how we could do it in Python:

# Grades = [grade1, grade2]
student1 = [80, 95]
student2 = [69, 85]
student3 = [95, 90]
student4 = [80, 79]
student5 = [72, 60]

avg_student1 = int((student1[0] + student1[1]) / 2)
print("avg {} = {}".format('student1', avg_student1))

avg_student2 = int((student2[0] + student2[1]) / 2)
print("avg {} = {}".format('student2', avg_student2))

avg_student3 = int((student3[0] + student3[1]) / 2)
print("avg {} = {}".format('student3', avg_student3))

avg_student4 = int((student4[0] + student4[1]) / 2)
print("avg {} = {}".format('student4', avg_student4))

avg_student5 = int((student5[0] + student5[1]) / 2)
print("avg {} = {}".format('student5', avg_student5))

While the above code works, it required a lot copy-and-pasting and editing (typing and retyping).

If you copy and paste code to reuse it in various places, if your code contains an error, or if, for example, you have to change the formula you are using, you will have to change your code in all the places you've pasted and used that code. You will get very confused if you forget to fix one of the pasted lines of code and your code does not work.

Use functions to go along with DRY

Here is an improved version of the code, using a function to calculate the average grade:

def avg_calc(grade1, grade2):
"""Calculate avg from two grades"""
avg = int(grade1 + grade2) / 2)
return avg

# Grades = [grade1, grade2]
student1 = [80, 95]
student2 = [69, 85]
student3 = [95, 90]
student4 = [80, 79]
student5 = [72, 60]

avg_student1 = avg_calc(student1[0], student1[1])
print("avg {} = {}".format('student1', avg_student1))

avg_student2 = avg_calc(student2[0], student2[1])
print("avg {} = {}".format('student2', avg_student2))

avg_student3 = avg_calc(student3[0], student3[1])
print("avg {} = {}".format('student3', avg_student3))

avg_student4 = avg_calc(student4[0], student4[1])
print("avg {} = {}".format('student4', avg_student4))

avg_student5 = avg_calc(student5[0], student5[1])
print("avg {} = {}".format('student5', avg_student5))

Take it a step further

The above code clearly produces the same output as the first version of the code. Anyway, it still required lots of copy and pasting and changing student numbers. You can see that the print statement is identical for each student. We are going to improve our code further by including the print statement in our function.

def avg_calc(stud_num, grade1, grade2):
"""Calculate avg from two grades"""
avg = int(grade1 + grade2) / 2)
student = 'student' + str(stud_num)
print("avg {} = {}".format(student, avg))

# Grades = [grade1, grade2]
student1 = [80, 95]
student2 = [69, 85]
student3 = [95, 90]
student4 = [80, 79]
student5 = [72, 60]

avg_student1 = avg_calc(1, student1[0], student1[1])
avg_student2 = avg_calc(2, student2[0], student2[1])
avg_student3 = avg_calc(3, student3[0], student3[1])
avg_student4 = avg_calc(4, student4[0], student4[1])
avg_student5 = avg_calc(5, student5[0], student5[1])

The final touch

The above code works as expected. But even for this version, the call to our function for each student has been copy-pasted, and we had to remember to change the student number.

Take a look at the final version of the code, below. To hold our students’ data, we now use a list of lists. Each internal list holds the data for a student, and we have added the student number along with their grades. The code is using a for loop to call our avg_calc() function for each student.

def avg_calc(stud_num, grade1, grade2):
"""Calculate avg from two grades"""
avg = int(grade1 + grade2) / 2)
student = 'student' + str(stud_num)
print("avg {} = {}".format(student, avg))

# students = [[stud_num, grade1, grade2]]
students =[[1, 80, 95], # student1
[2, 69, 85], # student2
[3, 95, 90], # student3
[4, 80, 79], # student4
[5, 72, 60]] # student5

for stud in students:
avg_calc(stud[0], stud[1], stud[2]) 

As a result, the code is shorter, easier to read, and easier to maintain. And this is what DRY is all about.

Other articles:

The SOLID principle

Single responsibility principle

Open/closed principle

Liskov's substitution principle

Interface segregation principle