Code Conservatory
The DRY Principle in Programming: Why It's Not Always the Best Choice
Photo by Cottonbro from Pexels

The DRY Principle in Programming: Why It's Not Always the Best Choice

As a developer, you might be familiar with the DRY (Don’t Repeat Yourself) principle in programming. It encourages the reduction of code duplication, which often leads to more maintainable, efficient, and less error-prone programs. Although the DRY principle is widely applicable, there are certain situations where it may not be the optimal approach.

Here’s an example to illustrate how applying the DRY principle can sometimes lead to overengineered code. Let’s say you’re working on a TypeScript program that calculates the price of products with and without discounts. The discounts are either a percentage or a fixed amount off the original price.

Without using the DRY principle, you might have two separate functions like this:

function applyPercentageDiscount(price: number, discount: number): number {
  return price - (price * (discount / 100));
}

function applyFixedDiscount(price: number, discount: number): number {
  return price - discount;
}

While trying to apply the DRY principle, you might decide to create a single function that handles both percentage and fixed discounts using an additional parameter:

type DiscountType = 'percentage' | 'fixed';

function applyDiscount(price: number, discount: number, type: DiscountType): number {
  if (type === 'percentage') {
    return price - (price * (discount / 100));
  } else {
    return price - discount;
  }
}

Although you’ve removed code duplication, you may have overengineered the solution. By combining the two functions, you’ve introduced an additional parameter type, which can lead to confusion and potential errors when using the function. The original functions were simple, straightforward, and easy to understand, while the combined function is more complex and harder to use correctly.

In this case, the benefits of applying the DRY principle are minimal, and the increased complexity may outweigh the advantages of code reuse. It’s essential to balance the DRY principle with other factors like readability, simplicity, and ease of use.

Now, imagine you’re writing another program that calculates the age of a user based on their birth year. Without using the DRY principle, you might write separate functions for calculating the age of users with different roles, like students and teachers:

interface Student {
  name: string;
  birthYear: number;
}

interface Teacher {
  name: string;
  birthYear: number;
}

function calculateStudentAge(student: Student): number {
  const currentYear = new Date().getFullYear();
  return currentYear - student.birthYear;
}

function calculateTeacherAge(teacher: Teacher): number {
  const currentYear = new Date().getFullYear();
  return currentYear - teacher.birthYear;
}

To apply the DRY principle, you can create a single function that calculates the age of a user, regardless of their role:

interface User {
  name: string;
  birthYear: number;
}

function calculateAge(user: User): number {
  const currentYear = new Date().getFullYear();
  return currentYear - user.birthYear;
}

// Usage:
const student: User = { name: 'Alice', birthYear: 2000 };
const teacher: User = { name: 'Bob', birthYear: 1985 };

console.log(calculateAge(student)); // Student's age
console.log(calculateAge(teacher)); // Teacher's age

By applying the DRY principle, you’ve simplified the code, making it easier to read and maintain. This single function can now be used to calculate the age of any user type, avoiding code duplication.

In this blog post, we will examine some scenarios where the DRY principle could prove to be counterproductive, and explore ways to find a balance between DRY and other programming principles.

Readability and Simplicity

Using the DRY principle can sometimes make your code too complicated and difficult to understand. You might try so hard to avoid repeating code that you end up with a big mess that’s hard to maintain. Sometimes, it’s easier to just repeat code rather than trying to come up with a fancy solution. In those cases, you have to think carefully about whether the benefits of DRY outweigh the downsides of making your code harder to read and simpler.

Abstraction?

Premature abstraction is a problem you might face if you stick to the DRY principle too strictly. It happens when you try to make your code reusable and general before you really get the whole picture of what you’re working on or what it might need in the future. This can lead to confusing and overly complicated code that makes things harder to figure out, fix, and keep up with. It’s important to strike a balance between reusing code and keeping things abstract.

Coupling?

When you stick to the DRY principle, sometimes you might accidentally link different parts of the system together(coupling) in ways you didn’t mean to. By getting rid of repeated code and making shared parts, you could end up making unrelated sections of the app depend on each other. This can make it harder to change or add to the system, since tweaking one part might mess up other parts. In situations like this, it’s actually better to let some code be duplicated, so that unrelated pieces stay separate and easier to manage.

Optimizing?

One more issue with sticking to the DRY principle is focusing too much on making the code smaller or thinking it’s more efficient, while ignoring important stuff like how easy it is to read, keep up with, and how well it performs. Having less code might look like a step up, but just looking at the size or number of lines doesn’t always mean the code is actually better. Keep the big picture in mind and aim for a good balance between reusing code, making it readable, and easy to maintain.

Flexibility?

Finally, following the DRY principle might sometimes make your code less flexible. When you make a shared part to avoid repeating code, you could keep it from being adaptable. If what you need changes, you might realize that the shared part is too rigid and tough to change, which could make it even harder to maintain than if you just let some code be duplicated in the first place.

Conclusion

The DRY principle is definitely useful, but it’s important to know its limits. Aim for a good mix of reusing code, keeping things simple, easy to read, and maintain. Remember that repeating some code is okay and can even be helpful in some cases.

Check out the DRY quiz to test yourself