ReviseAlgo Logo

Design Principles

DRY Principle

Don't Repeat Yourself

Last Updated: June 26, 2026 20 min read

The DRY (Don't Repeat Yourself) principle is a core philosophy in software engineering first coined in the book The Pragmatic Programmer. It states that "every piece of knowledge must have a single, unambiguous, authoritative representation within a system." DRY is not just about avoiding duplicate lines of code, but about ensuring that a single business rule or logic constraint is represented in exactly one place.

1. Learning Objectives

  • Understand the core philosophy of DRY ("Single Source of Truth").
  • Differentiate between "Duplication of Code" (syntax duplication) and "Duplication of Knowledge" (business logic duplication).
  • Apply refactoring techniques (utility classes, inheritance, composition, helper methods) to eliminate duplication.
  • Identify "Accidental Duplication" and know when *not* to refactor code.
  • Build modular validation components in Java, Python, and C++.

2. Problem Statement

Code duplication is one of the most common causes of bugs and maintenance headaches. When code is copy-pasted across multiple modules, changes to a single business requirement force developers to find and modify every copy.

For example, if the validation regex for an email address is copy-pasted in the UserRegistrationService, the ProfileUpdateService, and the AdminPortalService, a change in email formatting requirements forces updates in three separate files. If a developer misses even one instance, the system becomes inconsistent, leading to security vulnerabilities or data corruption.

3. Real-world Analogy

Think of a Restaurant Menu:

  • Imagine a restaurant has physical menus on tables, a website menu, and a mobile app menu.
  • If the prices of items are hardcoded independently on all three platforms, changing the price of a hamburger requires manual updates in three separate files. If the manager forgets to update the website, customers will see inconsistent pricing.
  • A DRY design would store prices in a single central database (Single Source of Truth). The physical menu system, website, and mobile app all query this database, ensuring price changes are automatically synchronized everywhere.

4. Theory

The DRY principle is often misunderstood as simply "don't copy-paste code lines." However, the principle targets the duplication of knowledge or business rules.

Duplication of Code vs. Duplication of Knowledge:

  • Code Duplication: Writing the same lines of code. This is a syntactic issue and is often easy to fix by extracting a helper method.
  • Knowledge Duplication: Representing the same business rule in multiple places. For example, validating credit card formats in both the frontend JavaScript client and the backend Java database controller. While this looks like duplication, it is often necessary for UX vs Security. However, within the backend codebase itself, the business rule representing credit card structure should reside in a single class.

Accidental Duplication:

A common mistake is merging two pieces of code that happen to look identical but represent different business concepts.

For example, if validating a PromoCode requires checking a length of 8 characters, and validating a WarehouseBinId also requires checking a length of 8 characters, merging these validations into a single helper method violates the Single Responsibility Principle (SRP). They look identical now, but they will change for entirely different reasons in the future. Merging them creates tight coupling between unrelated features.

5. Visual Diagrams (UML & Flow comparison)

Bad Design (WET - Write Everything Twice)

Both service components contain duplicated lines of code for validation, forcing updates in multiple files if validation rules change.

UserService
- createUser(email, pass)
if (!email.contains("@")) ...
if (pass.length < 8) ...
Duplicated Logic
AdminService
- createAdmin(email, pass)
if (!email.contains("@")) ...
if (pass.length < 8) ...

Good Design (DRY - Extracted Validation)

Duplication is removed by routing validation calls to a centralized helper class.

UserService
+ createUser()
AdminService
+ createAdmin()
calls static (dashed)
UserValidator
+ validateEmail(email)
+ validatePassword(password)

6. Syntax Explanation

To apply DRY, common syntax structures can be shared across classes:

  • Java: Extract methods to static helpers within utility classes (e.g. public static void validate()). Use private namespaces to keep helper functions encapsulated.
  • Python: Extract logic to helper functions, decorators (e.g., @validate_credentials), or module-level routines.
  • C++: Wrap shared functions inside a dedicated namespace (e.g., namespace ValidationUtils { ... }) or define static helper classes to prevent namespace pollution.

7. Step-by-Step Implementation

  • Step 1: Identify duplicate code segments across different classes (e.g. email and password validation rules).
  • Step 2: Create a helper utility class (e.g. Validator) to house the extracted code. Make sure this class is stateless.
  • Step 3: Move the duplicate validation logic into static helper methods inside the new class.
  • Step 4: Replace the duplicate validation logic in the service classes with calls to the helper methods.

8. Complete Code (Mini Project)

9. Code Walkthrough

In the refactored project above, duplication is eliminated:

  • We extracted validation constraints into a stateless helper utility (class or namespace Validator).
  • Both services now delegate validation to this class. If password requirements change (e.g. to at least 10 characters and requiring a digit), we only need to update the Validator.validatePassword method. The changes are automatically applied across the system.
  • Testing becomes easier: we can write comprehensive unit tests for the Validator class directly, rather than writing identical validation test cases for each service class.

10. Execution Flow

  1. Call: Client calls registerUser("user@example.com", "securepass123").
  2. Delegation: The service delegates verification to Validator.validateEmail and Validator.validatePassword.
  3. Check: Validator performs check logic. If criteria are met, control returns to the service.
  4. Execution: The service performs registration logic and returns control to the client.

11. Internal Working

At the compiler level, extracting duplicated code blocks into helper methods reduces the compiled bytecode size of the application.

Inside the JVM, when duplicate bytecode sequences are detected, the Just-In-Time (JIT) compiler compiles the extracted helper method into native machine instructions once. The calling services simply execute a invokestatic instruction pointing to the JIT-compiled validator method, rather than storing duplicated instruction arrays inside their own method layouts. This reduces cache misses and improves JIT inlining opportunities at runtime.

12. Complexity Analysis

  • Time Complexity: $O(1)$ to delegate the call. The execution time of the checks (e.g. regex email parsing) remains identical.
  • Space Complexity: $O(1)$ stack overhead for pushing method parameters onto the validator's stack frame. No heap allocations are performed since the validator is static and stateless.

13. Best Practices

  • Extract duplicate logic early: If you write the same logic twice, consider refactoring it. On the third time, extract it immediately (Rule of Three).
  • Store constants centrally: Avoid magic values. Use static final fields (e.g., public static final int MIN_PASSWORD_LENGTH = 8;) to store configuration parameters.
  • Prefer composition to share logic: When class behaviors overlap, inject helper components rather than using complex class inheritance.

14. Common Mistakes

  • Over-engineering DRY: Merging unrelated concepts because they look similar today. This creates tight coupling and violates SRP.
  • Creating deep inheritance hierarchies: Overusing subclass inheritance to share code. Prefer extracting utility methods or using component composition instead.
  • DRYing up test assertions: Sharing too much code between tests. Tests should be easy to read and self-contained. A small amount of duplication in test setup is acceptable if it improves readability.

15. Interview Questions

Q: How do you differentiate between code duplication and knowledge duplication?
Answer: Code duplication refers to identical lines of code. Knowledge duplication refers to representing a single business rule in multiple places. DRY primarily targets the duplication of knowledge, ensuring a business rule is defined in only one place.
Q: Explain "accidental duplication" and why merging it is dangerous.
Answer: Accidental duplication occurs when two different business entities happen to have identical validation logic initially. Merging them into a single method creates tight coupling. If the business rules for one entity change in the future, developers are forced to break the other entity or write complex conditional logic, violating the Single Responsibility Principle.
Q: What is the "Rule of Three" in software development?
Answer: The Rule of Three is a code refactoring guideline:
  1. First time: write the code.
  2. Second time: copy-paste it (and accept the duplication).
  3. Third time: refactor and extract the duplication into a reusable component.

16. Practice Exercises

  • Easy: Identify and extract duplicate discount tax formulas in a billing system into a single configuration class containing constants.
  • Medium: Refactor duplicate logging formats (e.g. formatting timestamps, service names, and levels) into a single centralized logging helper class.
  • Hard: Build a dynamic notification dispatcher that sends updates via SMS, Email, and Push Notifications. Extract common validation checks, throttling rules, and template rendering logic into shared helper components to keep the notification classes clean.

17. Challenge Problem

Design an e-commerce shipping rate calculator supporting multiple carriers (UPS, FedEx, DHL). Implement a DRY class hierarchy that shares the base distance-volume logic, while delegating specific weight rates to individual carrier subclasses using the Template Method Pattern.

18. Summary

  • DRY stands for "Don't Repeat Yourself" and targets duplication of knowledge/business rules.
  • Applying DRY makes code easier to maintain, test, and adapt to changing requirements.
  • Do not dry up code at the expense of readability or by coupling unrelated domain models.
  • Stateless helper utilities, namespaces, and composition are excellent ways to extract duplication.

19. Cheat Sheet

Principle Primary Focus Main Benefit Common Danger
DRY Unambiguous Source of Truth Easy maintenance, fewer rule bugs Over-abstraction, tight coupling
KISS Simplicity of Design High readability, quick refactoring Primitive obsession, lack of design patterns
YAGNI Avoiding premature feature coding Reduced development waste and complexity Under-design, lack of planning

20. Quiz

1. What is the central goal of the DRY principle?

A) Minimize the execution time of methods
B) Ensure every piece of system knowledge has a single, unambiguous representation (Correct)
C) Force all classes to use inheritance hierarchies

2. Who coined the DRY principle?

A) Robert C. Martin
B) Erich Gamma
C) The Pragmatic Programmer authors (Correct)

3. How does knowledge duplication differ from code duplication?

A) Knowledge duplication refers to duplicating business logic rules in multiple places (Correct)
B) Code duplication is always necessary for compiler performance
C) Knowledge duplication cannot be solved using utilities

4. What is WET code?

A) Code containing memory leak references
B) "Write Everything Twice" - duplicate-heavy code (Correct)
C) Highly optimized multithreading code

5. Why is merging "accidental duplication" a bad practice?

A) It decreases compilation speed
B) It couples unrelated business concepts, violating the Single Responsibility Principle (Correct)
C) It causes static initializer errors

6. What is the "Rule of Three"?

A) Do not pass more than three parameters to a method
B) Restructure/refactor duplicate code the third time you write it (Correct)
C) Limit class inheritances to three levels

7. How does JIT optimization benefit from DRY?

A) JIT compiler compiles extracted methods once, saving native instruction cache space (Correct)
B) JIT compiler completely deletes local stack variables
C) JIT compiler runs garbage collection dynamically

8. What is a key risk of over-extracting duplication?

A) Creating complex, unreadable abstractions that are hard to change (Correct)
B) Speeding up runtime executions too much
C) Causing stack overflows

9. How should constants (like password length limits) be handled to maintain DRY?

A) Hardcoded in every validation method
B) Declared in a single, central place (e.g. static final variables) and reused (Correct)
C) Passed as arguments in every user interaction

10. Where is a small amount of code duplication generally acceptable?

A) Backend database write services
B) Unit test files, to keep individual tests readable and self-contained (Correct)
C) Security credential validators

21. Next Lesson Preview

In the next lesson, we will explore the KISS (Keep It Simple, Stupid) principle to learn why simple code design is always superior to complex, over-engineered architectures!