I like my code like I like my martini, DRY.

Dr. Davis

dry martini

In the world of software development, there’s a principle that stands out as both simple and powerful: Don’t Repeat Yourself, often abbreviated as DRY. This principle encourages developers to avoid duplicating code, logic, or ideas. The ultimate goal? Increase clarity, reduce bugs, and make code more adaptable when requirements change. In this post, I will discuss the concept of DRY and provide practical Python examples that you can use in your own projects.

What Does “Don’t Repeat Yourself” Mean?

The DRY principle states that every piece of knowledge or logic should have a single, authoritative representation within a codebase. This means that if you find the same function, snippet, or logic structure repeated across your project, it’s usually a sign that there’s a better way to express it:

  • Less code duplication: When the same logic appears in multiple places, it’s easy to introduce inconsistencies or typos.
  • Easier maintenance: Changing one function or class is simpler than tracking down and updating identical code in many places.
  • Improved readability: A single, well-named function or class is easier to understand than code repeated across the codebase.

Examples of Violating and Fixing DRY

Repetitive Code Blocks

Problem: Imagine a scenario where you need to apply a discount to multiple products. You might find yourself writing the same logic multiple times:

# Not DRY
product_a_price = 100
discount_a = 0.1
product_a_price_after_discount = product_a_price - (product_a_price * discount_a)

product_b_price = 200
discount_b = 0.1
product_b_price_after_discount = product_b_price - (product_b_price * discount_b)

product_c_price = 300
discount_c = 0.1
product_c_price_after_discount = product_c_price - (product_c_price * discount_c)

print(product_a_price_after_discount, product_b_price_after_discount, product_c_price_after_discount)

Above, the logic to apply a discount is repeated three times. If you later discover a mistake in your discount calculation, you’ll have to fix it in three different places, which is a sign you’re violating the DRY principle.

Solution: Encapsulate the discount logic in a function. This removes the repeated logic and centralizes it in a single place.

def apply_discount(price, discount):
    return price - (price * discount)

product_a_price = 100
product_b_price = 200
product_c_price = 300

discount = 0.1  # 10% discount

product_a_price_after_discount = apply_discount(product_a_price, discount)
product_b_price_after_discount = apply_discount(product_b_price, discount)
product_c_price_after_discount = apply_discount(product_c_price, discount)

print(product_a_price_after_discount, product_b_price_after_discount, product_c_price_after_discount)

Now, if the logic for applying the discount changes, you’ll only need to update it once in the apply_discount function.

Using Classes and Inheritance

Problem: If you have two classes that share similar attributes and methods, you might have a lot of repeated code across both. This is a prime example of duplication that can be reduced by taking advantage of inheritance or composition.

# Not DRY
class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

    def make_sound(self):
        return "Woof!"

class Cat:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

    def make_sound(self):
        return "Meow!"

Here, both classes have name, breed, and a make_sound method, which indicates the same conceptual structure.

Solution: Use inheritance to create a shared base class, which keeps shared attributes and methods in one place. Each subclass can override or customize the behavior when needed.

class Animal:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed
    
    def make_sound(self):
        return "I am an animal!"

class Dog(Animal):
    def make_sound(self):
        return "Woof!"

class Cat(Animal):
    def make_sound(self):
        return "Meow!"
dog = Dog("Rex", "Labrador")
cat = Cat("Whiskers", "Siamese")
print(dog.make_sound())  # "Woof!"
print(cat.make_sound())  # "Meow!"

By introducing a single Animal base class, you only define name and breed initialization once. If you add new features or logic to Animal, both Dog and Cat will inherit those changes without requiring manual updates.

Conclusion

The DRY principle is an indispensable guideline for creating robust codebase. As your project grows, DRY practices make it easier to maintain, test, and refactor code, preventing it from turning into “spaghetti code.” Python’s design philosophy, emphasizing readability and simplicity, naturally pairs well with DRY best practices.

Next time you notice yourself copying and pasting the same block of code, ask whether you can refactor that code into a function, class, or other module. You will end up with a codebase that is not only more concise but also easier for you and your team to work with in the long run.