DRY (Don't Repeat Yourself) in Programming ⚡️

alisamir

Ali Samir

Posted on November 30, 2024

DRY (Don't Repeat Yourself) in Programming ⚡️

The DRY principle, short for "Don't Repeat Yourself," is one of the cornerstone principles in software development.

Andy Hunt and Dave Thomas coined the principle in their book The Pragmatic Programmer.

It emphasizes reducing repetition in code to improve maintainability, reduce errors, and save time.

This article will explore the DRY principle, its significance, and how to apply it effectively with TypeScript.


📌 What is DRY?

The DRY principle advocates reusing logic, abstractions, or patterns instead of duplicating them.

Code duplication often leads to issues such as:

  • Difficult maintenance: A bug in duplicated code must be fixed in every instance.

  • Inconsistent updates: Forgetting to update all duplicates can cause inconsistencies.

  • Bloated codebases: Repeated logic increases the size of the codebase unnecessarily.

Instead of writing the same logic multiple times, developers are encouraged to refactor code into reusable modules, functions, or abstractions.


📌 The Cost of Not Following DRY

Let's start with an example that violates DRY:

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

function calculateFinalPriceWithTax(price: number, discount: number, taxRate: number): number {
  const discountedPrice = price - price * discount;
  return discountedPrice + discountedPrice * taxRate;
}
Enter fullscreen mode Exit fullscreen mode

Here, the logic for calculating the discounted price is repeated in both functions.

This duplication increases the chances of bugs and inconsistency.

What if the formula changes? You’ll need to hunt down every instance manually.


📌 Applying DRY: Refactor and Reuse

We can refactor the above example to follow the DRY principle:

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

function calculateFinalPriceWithTax(price: number, discount: number, taxRate: number): number {
  const discountedPrice = calculateDiscount(price, discount);
  return discountedPrice + discountedPrice * taxRate;
}
Enter fullscreen mode Exit fullscreen mode

By extracting the discount logic into a reusable function, we've avoided duplication.

Now, any changes to the discount formula require updating only one place.


📌 Practical Applications of DRY

1. Shared Utility Functions

Many projects include utility functions that handle repetitive tasks like formatting dates, validation, or API requests.

Non-DRY Example:

const formatDateYYYYMMDD = (date: Date): string =>
  `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;

const formatDateMMDDYYYY = (date: Date): string =>
  `${date.getMonth() + 1}-${date.getDate()}-${date.getFullYear()}`;
Enter fullscreen mode Exit fullscreen mode

DRY Example:

function formatDate(date: Date, format: "YYYY-MM-DD" | "MM-DD-YYYY"): string {
  const year = date.getFullYear();
  const month = date.getMonth() + 1;
  const day = date.getDate();

  return format === "YYYY-MM-DD"
    ? `${year}-${month}-${day}`
    : `${month}-${day}-${year}`;
}
Enter fullscreen mode Exit fullscreen mode

2. Abstracting Repeated Components in Frontend Development

In a TypeScript-based React application, developers often repeat UI components unnecessarily.

Non-DRY Example:

const ButtonPrimary = () => <button style={{ color: 'white', backgroundColor: 'blue' }}>Primary</button>;
const ButtonSecondary = () => <button style={{ color: 'blue', backgroundColor: 'white' }}>Secondary</button>;
Enter fullscreen mode Exit fullscreen mode

DRY Example:

type ButtonProps = {
  label: string;
  variant: 'primary' | 'secondary';
};

const Button: React.FC<ButtonProps> = ({ label, variant }) => {
  const styles =
    variant === 'primary'
      ? { color: 'white', backgroundColor: 'blue' }
      : { color: 'blue', backgroundColor: 'white' };

  return <button style={styles}>{label}</button>;
};
Enter fullscreen mode Exit fullscreen mode

Now you have a reusable button component that supports multiple variants.


📌 Tips for Staying DRY

  • Modular Design: Split your codebase into reusable modules or packages.

  • Shared Configurations: Use shared configuration files for tools like ESLint, Prettier, or Webpack.

  • Reusable Abstractions: Identify patterns in your code and abstract them into functions, classes, or components.

  • Code Reviews: Encourage team members to review code for unnecessary repetition.


📌 Balancing DRY with Practicality

While DRY is a powerful principle, over-applying it can lead to premature abstraction, making your code harder to understand.

For example, merging two unrelated functions into one "general" function might reduce repetition but increase complexity.

Rule of Thumb:

  • If duplication exists for unrelated concepts, it’s often fine.

  • If duplication arises from related logic, consider refactoring.


Conclusion ✅

The DRY principle is essential for creating clean, maintainable, and scalable codebases.

By identifying repetitive patterns and consolidating them into reusable structures, developers can reduce bugs, save time, and enhance collaboration.

Use TypeScript’s robust type system to create abstractions that are not only reusable but also safe and predictable.

Keep DRY in mind as you write and review your code—but balance it with practicality for the best results!

Happy Coding!

💖 💪 🙅 🚩
alisamir
Ali Samir

Posted on November 30, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

DRY (Don't Repeat Yourself) in Programming ⚡️
softwaredevelopment DRY (Don't Repeat Yourself) in Programming ⚡️

November 30, 2024

Out of the Tar Pit, another approach
softwaredevelopment Out of the Tar Pit, another approach

July 23, 2019