Don’t Repeat Yourself (DRY)

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

Overview

DRY is fundamentally about knowledge, not code. When the same knowledge is expressed in multiple places, changing that knowledge requires finding and updating all the copies—and missing one creates inconsistency. DRY applies to code, documentation, database schemas, test fixtures, and anywhere else knowledge lives.

The opposite of DRY is WET: “Write Everything Twice” (or “We Enjoy Typing”). WET code creates maintenance nightmares where fixes in one place don’t propagate to duplicated logic elsewhere.

Key Principles

Knowledge vs. Code

Duplication of knowledge is always a problem. Duplication of code is only a problem when the code represents the same underlying knowledge. Two functions that happen to have similar implementations but represent different domain concepts are not DRY violations—they just happen to be similar today.

// Not a DRY violation - different knowledge, similar code
function validateEmail(email: string): boolean {
  return email.includes('@') && email.includes('.');
}
 
function containsAtSymbol(text: string): boolean {
  return text.includes('@');
}

The Rule of Three

Don’t abstract prematurely. The first time you write something, just write it. The second time, note the duplication but consider leaving it. The third time, extract the commonality. This prevents creating abstractions before you understand the actual pattern.

Types of Duplication

Imposed duplication - Development practices require multiple representations (e.g., documentation that restates code). Combat with automation and generation.

Inadvertent duplication - Developers don’t realize they’re duplicating. Combat with code reviews and knowledge sharing.

Impatient duplication - Copying is faster than thinking. Combat with discipline and tooling.

Inter-developer duplication - Multiple developers solve the same problem. Combat with communication and shared libraries.

Example

Consider validation logic spread across an application:

// Before: Knowledge duplicated in multiple places
 
// In the API handler
function createUser(req: Request): Response {
  if (!req.body.email || !req.body.email.includes('@')) {
    return { error: 'Invalid email' };
  }
  if (!req.body.password || req.body.password.length < 8) {
    return { error: 'Password too short' };
  }
  // ...
}
 
// In the frontend
function validateForm(formData: FormData): string[] {
  const errors = [];
  if (!formData.email || !formData.email.includes('@')) {
    errors.push('Invalid email');
  }
  if (!formData.password || formData.password.length < 8) {
    errors.push('Password too short');
  }
  return errors;
}
 
// In the database model
class User {
  email: string; // Must contain @
  password: string; // Must be >= 8 chars
}

The knowledge “emails must contain @” and “passwords must be 8+ characters” exists in three places.

// After: Single authoritative representation
 
// Shared validation rules
const validationRules = {
  email: {
    validate: (value: string) => value?.includes('@'),
    message: 'Invalid email',
  },
  password: {
    validate: (value: string) => value?.length >= 8,
    message: 'Password must be at least 8 characters',
  },
};
 
// Used everywhere
function validate(field: string, value: string): string | null {
  const rule = validationRules[field];
  if (!rule.validate(value)) {
    return rule.message;
  }
  return null;
}

Common Mistakes

Confusing coincidental similarity with duplication - Code that looks the same but represents different concepts isn’t a DRY violation. Forcing it into a shared abstraction creates coupling between unrelated things.

Extracting too early - Premature abstraction based on two instances can lead to awkward generalizations that don’t fit future needs.

Ignoring non-code duplication - Comments that restate code, documentation that duplicates code behavior, test data that duplicates production data—all are DRY violations.

Creating coupled abstractions - A shared function used by unrelated modules creates coupling. Sometimes duplication is better than the wrong abstraction.

References

  • Hunt, A., & Thomas, D. (1999). The Pragmatic Programmer. Chapter 2: The Evils of Duplication.
  • Martin, R. C. (2008). Clean Code: A Handbook of Agile Software Craftsmanship.