DRY Principle

Don't Repeat Yourself - a fundamental principle for reducing code duplication and improving maintainability.

best-practices
drybest-practicesrefactoringclean-codearchitecture

What is DRY?

"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."

DRY = Don't Repeat Yourself

✓ Single source of truth
✓ Reduce duplication
✓ Centralize logic
✓ Easier maintenance

Extract Functions

// ❌ Bad - repeated validation logic
function createUser(email: string) {
  if (!email.includes('@') || email.length < 5) {
    throw new Error('Invalid email');
  }
  // create user...
}

function updateEmail(email: string) {
  if (!email.includes('@') || email.length < 5) {
    throw new Error('Invalid email');
  }
  // update email...
}

// ✅ Good - extracted reusable function
function validateEmail(email: string): boolean {
  return email.includes('@') && email.length >= 5;
}

function createUser(email: string) {
  if (!validateEmail(email)) throw new Error('Invalid email');
  // create user...
}

function updateEmail(email: string) {
  if (!validateEmail(email)) throw new Error('Invalid email');
  // update email...
}

Use Constants

// ❌ Bad - magic numbers repeated
function calculateShipping(weight: number) {
  return weight * 5.99;
}

function calculateTax(price: number) {
  return price * 0.08; // 8% tax
}

// Tax rate 0.08 and shipping rate 5.99 appear in multiple files...

// ✅ Good - centralized constants
const SHIPPING_RATE_PER_KG = 5.99;
const TAX_RATE = 0.08;

function calculateShipping(weight: number) {
  return weight * SHIPPING_RATE_PER_KG;
}

function calculateTax(price: number) {
  return price * TAX_RATE;
}

Create Base Classes

// ❌ Bad - repeated code in similar classes
class Dog {
  name: string;
  eat() { console.log(`${this.name} is eating`); }
  sleep() { console.log(`${this.name} is sleeping`); }
  bark() { console.log('Woof!'); }
}

class Cat {
  name: string;
  eat() { console.log(`${this.name} is eating`); }
  sleep() { console.log(`${this.name} is sleeping`); }
  meow() { console.log('Meow!'); }
}

// ✅ Good - shared behavior in base class
abstract class Animal {
  constructor(public name: string) {}
  eat() { console.log(`${this.name} is eating`); }
  sleep() { console.log(`${this.name} is sleeping`); }
}

class Dog extends Animal {
  bark() { console.log('Woof!'); }
}

class Cat extends Animal {
  meow() { console.log('Meow!'); }
}

Use Utility Functions

// ❌ Bad - repeated formatting logic
function displayUserDate(user: User) {
  const date = user.createdAt;
  return `${date.getMonth()+1}/${date.getDate()}/${date.getFullYear()}`;
}

function displayOrderDate(order: Order) {
  const date = order.placedAt;
  return `${date.getMonth()+1}/${date.getDate()}/${date.getFullYear()}`;
}

// ✅ Good - reusable utility
function formatDate(date: Date): string {
  return `${date.getMonth()+1}/${date.getDate()}/${date.getFullYear()}`;
}

function displayUserDate(user: User) {
  return formatDate(user.createdAt);
}

function displayOrderDate(order: Order) {
  return formatDate(order.placedAt);
}

Configuration Files

// ❌ Bad - hardcoded values everywhere
// file1.ts
const apiUrl = 'https://api.example.com/v1';

// file2.ts
const apiUrl = 'https://api.example.com/v1';

// ✅ Good - single configuration source
// config.ts
export const config = {
  apiUrl: 'https://api.example.com/v1',
  timeout: 5000,
  retries: 3,
};

// file1.ts & file2.ts
import { config } from './config';
fetch(config.apiUrl);

Template/Component Reuse

// ❌ Bad - repeated UI structure
const UserCard = () => (
  <div className="card shadow rounded p-4">
    <h3>{user.name}</h3>
  </div>
);

const ProductCard = () => (
  <div className="card shadow rounded p-4">
    <h3>{product.name}</h3>
  </div>
);

// ✅ Good - reusable card component
const Card = ({ children }) => (
  <div className="card shadow rounded p-4">
    {children}
  </div>
);

const UserCard = () => <Card><h3>{user.name}</h3></Card>;
const ProductCard = () => <Card><h3>{product.name}</h3></Card>;

When NOT to Apply DRY

⚠️ Avoid premature abstraction:

• Code that looks similar but serves different purposes
• Two pieces of code that might evolve differently
• When abstraction adds more complexity than duplication
• "Rule of Three" - wait for 3 occurrences before abstracting

Remember: Some duplication is better than wrong abstraction

Quick Reference

TechniqueUse Case
Extract functionsRepeated logic blocks
ConstantsMagic numbers/strings
Base classesShared behavior
UtilitiesCommon operations
Config filesEnvironment values
ComponentsRepeated UI patterns