DRY Principle
DRY Principle: Don’t Repeat Yourself
Section titled “DRY Principle: Don’t Repeat Yourself”The DRY (Don’t Repeat Yourself) principle is one of the most fundamental principles in software development. It states that every piece of knowledge must have a single, unambiguous representation within a system.
Why DRY Principle?
Section titled “Why DRY Principle?”DRY Principle helps you:
- Reduce duplication - Write code once, use it everywhere
- Easier maintenance - Change code in one place
- Consistency - Same logic behaves the same everywhere
- Less bugs - Fix bugs once, not in multiple places
- Better readability - Less code to read and understand
Visual: DRY Principle Concept
Section titled “Visual: DRY Principle Concept”What Happens If We Don’t Follow DRY?
Section titled “What Happens If We Don’t Follow DRY?”Without DRY Principle, you might:
- Duplicate code - Same logic repeated in multiple places
- Inconsistent behavior - Same logic implemented differently
- Maintenance nightmare - Need to update code in many places
- More bugs - Fix bugs in multiple places, easy to miss some
- Harder to test - Test same logic multiple times
Visual: The Duplication Problem
Section titled “Visual: The Duplication Problem”Simple Example: User Validation
Section titled “Simple Example: User Validation”Let’s see a simple example showing the problem and solution:
The Problem: Code Duplication
Section titled “The Problem: Code Duplication”Visual: Code Duplication Flow
Section titled “Visual: Code Duplication Flow”# ❌ Without DRY - Code duplication everywhere!
def register_user(email: str, password: str): # Validation logic duplicated if not email or "@" not in email: raise ValueError("Invalid email") if not password or len(password) < 8: raise ValueError("Password must be at least 8 characters") # ... registration logic
def login_user(email: str, password: str): # Same validation logic duplicated! if not email or "@" not in email: raise ValueError("Invalid email") if not password or len(password) < 8: raise ValueError("Password must be at least 8 characters") # ... login logic
def reset_password(email: str, new_password: str): # Same validation logic duplicated again! if not email or "@" not in email: raise ValueError("Invalid email") if not new_password or len(new_password) < 8: raise ValueError("Password must be at least 8 characters") # ... reset logic
# Problems:# - Validation logic repeated 3 times# - If validation rules change, need to update 3 places# - Easy to make mistakes or forget to update all places// ❌ Without DRY - Code duplication everywhere!
public class UserService { public void registerUser(String email, String password) { // Validation logic duplicated if (email == null || !email.contains("@")) { throw new IllegalArgumentException("Invalid email"); } if (password == null || password.length() < 8) { throw new IllegalArgumentException("Password must be at least 8 characters"); } // ... registration logic }
public void loginUser(String email, String password) { // Same validation logic duplicated! if (email == null || !email.contains("@")) { throw new IllegalArgumentException("Invalid email"); } if (password == null || password.length() < 8) { throw new IllegalArgumentException("Password must be at least 8 characters"); } // ... login logic }
public void resetPassword(String email, String newPassword) { // Same validation logic duplicated again! if (email == null || !email.contains("@")) { throw new IllegalArgumentException("Invalid email"); } if (newPassword == null || newPassword.length() < 8) { throw new IllegalArgumentException("Password must be at least 8 characters"); } // ... reset logic }}
// Problems:// - Validation logic repeated 3 times// - If validation rules change, need to update 3 places// - Easy to make mistakes or forget to update all placesThe Solution: DRY Principle
Section titled “The Solution: DRY Principle”Visual: DRY Solution Flow
Section titled “Visual: DRY Solution Flow”# ✅ With DRY - Validation logic in one place!
class Validator: """Single source of truth for validation logic"""
@staticmethod def validate_email(email: str) -> None: """Validate email - defined once, used everywhere""" if not email or "@" not in email: raise ValueError("Invalid email")
@staticmethod def validate_password(password: str) -> None: """Validate password - defined once, used everywhere""" if not password or len(password) < 8: raise ValueError("Password must be at least 8 characters")
def register_user(email: str, password: str): # Use validation from single source Validator.validate_email(email) Validator.validate_password(password) # ... registration logic
def login_user(email: str, password: str): # Use same validation - no duplication! Validator.validate_email(email) Validator.validate_password(password) # ... login logic
def reset_password(email: str, new_password: str): # Use same validation - no duplication! Validator.validate_email(email) Validator.validate_password(new_password) # ... reset logic
# Benefits:# - Validation logic in one place# - Change validation rules once, affects all uses# - Consistent behavior everywhere# - Easier to test - test validation once// ✅ With DRY - Validation logic in one place!
class Validator { // Single source of truth for validation logic public static void validateEmail(String email) { // Validate email - defined once, used everywhere if (email == null || !email.contains("@")) { throw new IllegalArgumentException("Invalid email"); } }
public static void validatePassword(String password) { // Validate password - defined once, used everywhere if (password == null || password.length() < 8) { throw new IllegalArgumentException("Password must be at least 8 characters"); } }}
public class UserService { public void registerUser(String email, String password) { // Use validation from single source Validator.validateEmail(email); Validator.validatePassword(password); // ... registration logic }
public void loginUser(String email, String password) { // Use same validation - no duplication! Validator.validateEmail(email); Validator.validatePassword(password); // ... login logic }
public void resetPassword(String email, String newPassword) { // Use same validation - no duplication! Validator.validateEmail(email); Validator.validatePassword(newPassword); // ... reset logic }}
// Benefits:// - Validation logic in one place// - Change validation rules once, affects all uses// - Consistent behavior everywhere// - Easier to test - test validation onceReal-World Example: Database Connection
Section titled “Real-World Example: Database Connection”Here’s a more realistic example showing DRY in action:
# ❌ Without DRY - Database connection logic duplicated
class UserService: def get_user(self, user_id: int): # Connection logic duplicated connection = create_connection() try: # ... query logic return user finally: connection.close()
def create_user(self, user_data: dict): # Same connection logic duplicated! connection = create_connection() try: # ... insert logic return user finally: connection.close()
# ✅ With DRY - Database connection logic in one place
class DatabaseManager: """Single source of truth for database operations"""
@staticmethod def execute_query(query: str, params: tuple = None): """Execute query - handles connection lifecycle""" connection = create_connection() try: cursor = connection.cursor() cursor.execute(query, params) return cursor.fetchall() finally: connection.close()
class UserService: def get_user(self, user_id: int): # Use database manager - no duplication! results = DatabaseManager.execute_query( "SELECT * FROM users WHERE id = %s", (user_id,) ) return results[0] if results else None
def create_user(self, user_data: dict): # Use same database manager - no duplication! DatabaseManager.execute_query( "INSERT INTO users (name, email) VALUES (%s, %s)", (user_data['name'], user_data['email']) )// ❌ Without DRY - Database connection logic duplicated
public class UserService { public User getUser(int userId) { // Connection logic duplicated Connection connection = createConnection(); try { // ... query logic return user; } finally { connection.close(); } }
public User createUser(UserData userData) { // Same connection logic duplicated! Connection connection = createConnection(); try { // ... insert logic return user; } finally { connection.close(); } }}
// ✅ With DRY - Database connection logic in one place
class DatabaseManager { // Single source of truth for database operations public static List<Map<String, Object>> executeQuery(String query, Object... params) { // Execute query - handles connection lifecycle Connection connection = createConnection(); try { PreparedStatement stmt = connection.prepareStatement(query); // Set parameters... ResultSet rs = stmt.executeQuery(); // Process results... return results; } finally { connection.close(); } }}
public class UserService { public User getUser(int userId) { // Use database manager - no duplication! List<Map<String, Object>> results = DatabaseManager.executeQuery( "SELECT * FROM users WHERE id = ?", userId ); return results.isEmpty() ? null : mapToUser(results.get(0)); }
public User createUser(UserData userData) { // Use same database manager - no duplication! DatabaseManager.executeQuery( "INSERT INTO users (name, email) VALUES (?, ?)", userData.getName(), userData.getEmail() ); return user; }}When to Apply DRY?
Section titled “When to Apply DRY?”Apply DRY Principle when:
✅ Same logic appears multiple times - Extract to function/class
✅ Business rules are repeated - Centralize in one place
✅ Configuration values duplicated - Use constants/config
✅ Similar code patterns - Create reusable abstractions
✅ Data structures repeated - Define once, reuse
Visual: When to Apply DRY
Section titled “Visual: When to Apply DRY”When NOT to Over-Apply DRY?
Section titled “When NOT to Over-Apply DRY?”Don’t over-apply DRY when:
❌ Code is similar but different - Don’t force abstraction
❌ Premature abstraction - Wait until you see actual duplication
❌ Over-engineering - Simple duplication might be fine
❌ Performance critical - Sometimes duplication is faster
Key Takeaways
Section titled “Key Takeaways”- DRY Principle = Don’t Repeat Yourself
- Single source of truth - Every piece of knowledge in one place
- Easier maintenance - Change once, affects all uses
- Consistency - Same behavior everywhere
- Balance - Don’t over-abstract, eliminate real duplication
Remember: DRY is about eliminating real duplication, not creating unnecessary abstractions. Use it wisely! 🎯