DRY (Don't Repeat Yourself) in Programming ⚡️
Ali Samir
Posted on November 30, 2024
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;
}
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;
}
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()}`;
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}`;
}
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>;
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>;
};
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!
Posted on November 30, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.