DRY Principle
DRY Principle: Don’t Repeat Yourself
Section titled “DRY Principle: Don’t Repeat Yourself”The DRY principle (Don’t Repeat Yourself) is one of the most fundamental principles in software development. The DRY principle states that every piece of knowledge must have a single, unambiguous representation within a system. Understanding the DRY principle is essential for writing maintainable, clean code.
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 places// ❌ Without DRY - Code duplication everywhere!
function registerUser(email: string, password: string): void { // Validation logic duplicated if (!email || !email.includes("@")) { throw new Error("Invalid email"); } if (!password || password.length < 8) { throw new Error("Password must be at least 8 characters"); } // ... registration logic}
function loginUser(email: string, password: string): void { // Same validation logic duplicated! if (!email || !email.includes("@")) { throw new Error("Invalid email"); } if (!password || password.length < 8) { throw new Error("Password must be at least 8 characters"); } // ... login logic}
function resetPassword(email: string, newPassword: string): void { // Same validation logic duplicated again! if (!email || !email.includes("@")) { throw new Error("Invalid email"); } if (!newPassword || newPassword.length < 8) { throw new Error("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!
#include <string>#include <stdexcept>
void registerUser(const std::string& email, const std::string& password) { // Validation logic duplicated if (email.empty() || email.find("@") == std::string::npos) { throw std::invalid_argument("Invalid email"); } if (password.empty() || password.length() < 8) { throw std::invalid_argument("Password must be at least 8 characters"); } // ... registration logic}
void loginUser(const std::string& email, const std::string& password) { // Same validation logic duplicated! if (email.empty() || email.find("@") == std::string::npos) { throw std::invalid_argument("Invalid email"); } if (password.empty() || password.length() < 8) { throw std::invalid_argument("Password must be at least 8 characters"); } // ... login logic}
void resetPassword(const std::string& email, const std::string& newPassword) { // Same validation logic duplicated again! if (email.empty() || email.find("@") == std::string::npos) { throw std::invalid_argument("Invalid email"); } if (newPassword.empty() || newPassword.length() < 8) { throw std::invalid_argument("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 (string.IsNullOrEmpty(email) || !email.Contains("@")) { throw new ArgumentException("Invalid email"); } if (string.IsNullOrEmpty(password) || password.Length < 8) { throw new ArgumentException("Password must be at least 8 characters"); } // ... registration logic }
public void LoginUser(string email, string password) { // Same validation logic duplicated! if (string.IsNullOrEmpty(email) || !email.Contains("@")) { throw new ArgumentException("Invalid email"); } if (string.IsNullOrEmpty(password) || password.Length < 8) { throw new ArgumentException("Password must be at least 8 characters"); } // ... login logic }
public void ResetPassword(string email, string newPassword) { // Same validation logic duplicated again! if (string.IsNullOrEmpty(email) || !email.Contains("@")) { throw new ArgumentException("Invalid email"); } if (string.IsNullOrEmpty(newPassword) || newPassword.Length < 8) { throw new ArgumentException("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 — duplicated validation everywhere
package main
import ( "errors" "strings")
type UserService struct{}
func (*UserService) RegisterUser(email, password string) error { if strings.TrimSpace(email) == "" || !strings.Contains(email, "@") { return errors.New("Invalid email") } if strings.TrimSpace(password) == "" || len(password) < 8 { return errors.New("Password must be at least 8 characters") } return nil}
func (*UserService) LoginUser(email, password string) error { if strings.TrimSpace(email) == "" || !strings.Contains(email, "@") { return errors.New("Invalid email") } if strings.TrimSpace(password) == "" || len(password) < 8 { return errors.New("Password must be at least 8 characters") } return nil}
func (*UserService) ResetPassword(email, newPassword string) error { if strings.TrimSpace(email) == "" || !strings.Contains(email, "@") { return errors.New("Invalid email") } if strings.TrimSpace(newPassword) == "" || len(newPassword) < 8 { return errors.New("Password must be at least 8 characters") } return nil}
// Problems:// - Validation logic repeated 3 times// - Rules change ⇒ update multiple sites// ❌ Without DRY — duplicated validation everywhere
struct UserService;
impl UserService { fn register_user(&self, email: &str, password: &str) -> Result<(), String> { if email.trim().is_empty() || !email.contains('@') { return Err("Invalid email".to_string()); } if password.trim().is_empty() || password.len() < 8 { return Err("Password must be at least 8 characters".to_string()); } Ok(()) }
fn login_user(&self, email: &str, password: &str) -> Result<(), String> { if email.trim().is_empty() || !email.contains('@') { return Err("Invalid email".to_string()); } if password.trim().is_empty() || password.len() < 8 { return Err("Password must be at least 8 characters".to_string()); } Ok(()) }
fn reset_password(&self, email: &str, new_password: &str) -> Result<(), String> { if email.trim().is_empty() || !email.contains('@') { return Err("Invalid email".to_string()); } if new_password.trim().is_empty() || new_password.len() < 8 { return Err("Password must be at least 8 characters".to_string()); } Ok(()) }}
// Problems:// - Validation logic repeated 3 times// - Rules change => update multiple sitesThe 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 once// ✅ With DRY - Validation logic in one place!
class Validator { /** Single source of truth for validation logic */
static validateEmail(email: string): void { /** Validate email - defined once, used everywhere */ if (!email || !email.includes("@")) { throw new Error("Invalid email"); } }
static validatePassword(password: string): void { /** Validate password - defined once, used everywhere */ if (!password || password.length < 8) { throw new Error("Password must be at least 8 characters"); } }}
function registerUser(email: string, password: string): void { // Use validation from single source Validator.validateEmail(email); Validator.validatePassword(password); // ... registration logic}
function loginUser(email: string, password: string): void { // Use same validation - no duplication! Validator.validateEmail(email); Validator.validatePassword(password); // ... login logic}
function resetPassword(email: string, newPassword: string): void { // 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 once// ✅ With DRY - Validation logic in one place!
#include <string>#include <stdexcept>
class Validator {public: // Single source of truth for validation logic static void validateEmail(const std::string& email) { // Validate email - defined once, used everywhere if (email.empty() || email.find("@") == std::string::npos) { throw std::invalid_argument("Invalid email"); } }
static void validatePassword(const std::string& password) { // Validate password - defined once, used everywhere if (password.empty() || password.length() < 8) { throw std::invalid_argument("Password must be at least 8 characters"); } }};
void registerUser(const std::string& email, const std::string& password) { // Use validation from single source Validator::validateEmail(email); Validator::validatePassword(password); // ... registration logic}
void loginUser(const std::string& email, const std::string& password) { // Use same validation - no duplication! Validator::validateEmail(email); Validator::validatePassword(password); // ... login logic}
void resetPassword(const std::string& email, const std::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 once// ✅ With DRY - Validation logic in one place!
public class Validator{ // Single source of truth for validation logic public static void ValidateEmail(string email) { // Validate email - defined once, used everywhere if (string.IsNullOrEmpty(email) || !email.Contains("@")) { throw new ArgumentException("Invalid email"); } }
public static void ValidatePassword(string password) { // Validate password - defined once, used everywhere if (string.IsNullOrEmpty(password) || password.Length < 8) { throw new ArgumentException("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 once// ✅ With DRY — validation centralized
package main
import ( "errors" "strings")
type Validator struct{}
func (Validator) ValidateEmail(email string) error { if strings.TrimSpace(email) == "" || !strings.Contains(email, "@") { return errors.New("Invalid email") } return nil}
func (Validator) ValidatePassword(password string) error { if strings.TrimSpace(password) == "" || len(password) < 8 { return errors.New("Password must be at least 8 characters") } return nil}
type UserService struct { v Validator}
func (s *UserService) RegisterUser(email, password string) error { if err := s.v.ValidateEmail(email); err != nil { return err } if err := s.v.ValidatePassword(password); err != nil { return err } return nil}
func (s *UserService) LoginUser(email, password string) error { if err := s.v.ValidateEmail(email); err != nil { return err } if err := s.v.ValidatePassword(password); err != nil { return err } return nil}
func (s *UserService) ResetPassword(email, newPassword string) error { if err := s.v.ValidateEmail(email); err != nil { return err } if err := s.v.ValidatePassword(newPassword); err != nil { return err } return nil}// ✅ With DRY — validation centralized
struct Validator;
impl Validator { fn validate_email(email: &str) -> Result<(), String> { if email.trim().is_empty() || !email.contains('@') { return Err("Invalid email".to_string()); } Ok(()) }
fn validate_password(password: &str) -> Result<(), String> { if password.trim().is_empty() || password.len() < 8 { return Err("Password must be at least 8 characters".to_string()); } Ok(()) }}
struct UserService;
impl UserService { fn register_user(&self, email: &str, password: &str) -> Result<(), String> { Validator::validate_email(email)?; Validator::validate_password(password)?; Ok(()) }
fn login_user(&self, email: &str, password: &str) -> Result<(), String> { Validator::validate_email(email)?; Validator::validate_password(password)?; Ok(()) }
fn reset_password(&self, email: &str, new_password: &str) -> Result<(), String> { Validator::validate_email(email)?; Validator::validate_password(new_password)?; Ok(()) }}Real-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; }}// ❌ Without DRY - Database connection logic duplicated
class UserService { getUser(userId: number): User | null { // Connection logic duplicated const connection = createConnection(); try { // ... query logic return user; } finally { connection.close(); } }
createUser(userData: UserData): User { // Same connection logic duplicated! const 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 */
static executeQuery<T>(query: string, params?: any[]): T[] { /** Execute query - handles connection lifecycle */ const connection = createConnection(); try { const results = connection.execute(query, params); return results as T[]; } finally { connection.close(); } }}
class UserService { getUser(userId: number): User | null { // Use database manager - no duplication! const results = DatabaseManager.executeQuery<User>( "SELECT * FROM users WHERE id = ?", [userId] ); return results.length > 0 ? results[0] : null; }
createUser(userData: UserData): void { // Use same database manager - no duplication! DatabaseManager.executeQuery( "INSERT INTO users (name, email) VALUES (?, ?)", [userData.name, userData.email] ); }}// ❌ Without DRY - Database connection logic duplicated
#include <memory>#include <vector>
class UserService {public: User* getUser(int userId) { // Connection logic duplicated auto connection = createConnection(); try { // ... query logic return user; } catch (...) { connection->close(); throw; } connection->close(); }
User* createUser(const UserData& userData) { // Same connection logic duplicated! auto connection = createConnection(); try { // ... insert logic return user; } catch (...) { connection->close(); throw; } connection->close(); }};
// ✅ With DRY - Database connection logic in one place
class DatabaseManager {public: // Single source of truth for database operations template<typename T> static std::vector<T> executeQuery(const std::string& query, const std::vector<std::string>& params = {}) { // Execute query - handles connection lifecycle auto connection = createConnection(); try { auto stmt = connection->prepareStatement(query); // Set parameters... auto results = stmt->executeQuery(); // Process results... return processResults<T>(results); } catch (...) { connection->close(); throw; } connection->close(); }};
class UserService {public: User* getUser(int userId) { // Use database manager - no duplication! auto results = DatabaseManager::executeQuery<User>( "SELECT * FROM users WHERE id = ?", {std::to_string(userId)} ); return results.empty() ? nullptr : new User(results[0]); }
void createUser(const UserData& userData) { // Use same database manager - no duplication! DatabaseManager::executeQuery<void>( "INSERT INTO users (name, email) VALUES (?, ?)", {userData.name, userData.email} ); }};// ❌ Without DRY - Database connection logic duplicated
using System;using System.Data;
public class UserService{ public User GetUser(int userId) { // Connection logic duplicated var connection = CreateConnection(); try { // ... query logic return user; } finally { connection.Close(); } }
public User CreateUser(UserData userData) { // Same connection logic duplicated! var connection = CreateConnection(); try { // ... insert logic return user; } finally { connection.Close(); } }}
// ✅ With DRY - Database connection logic in one place
public class DatabaseManager{ // Single source of truth for database operations public static List<Dictionary<string, object>> ExecuteQuery( string query, params object[] parameters) { // Execute query - handles connection lifecycle var connection = CreateConnection(); try { var cmd = connection.CreateCommand(); cmd.CommandText = query; // Set parameters... var reader = cmd.ExecuteReader(); // Process results... return results; } finally { connection.Close(); } }}
public class UserService{ public User GetUser(int userId) { // Use database manager - no duplication! var results = DatabaseManager.ExecuteQuery( "SELECT * FROM users WHERE id = @userId", userId ); return results.Count > 0 ? MapToUser(results[0]) : null; }
public void CreateUser(UserData userData) { // Use same database manager - no duplication! DatabaseManager.ExecuteQuery( "INSERT INTO users (name, email) VALUES (@name, @email)", userData.Name, userData.Email ); }}// ❌ Without DRY — repeated open/close
package main
type Connection struct{}
func CreateConnection() *Connection { return &Connection{} }func (*Connection) Close() {}
type User struct{}type UserData struct{ Name, Email string }
func MapToUser(_ map[string]any) *User { return &User{} }
type UserServiceBad struct{}
func (*UserServiceBad) GetUser(userID int) *User { conn := CreateConnection() defer conn.Close() // ... query return nil}
func (*UserServiceBad) CreateUser(_ UserData) *User { conn := CreateConnection() defer conn.Close() // ... insert return nil}
// ✅ With DRY — connection lifecycle in one helper
type DatabaseManager struct{}
func (DatabaseManager) ExecuteQuery(query string, args ...any) []map[string]any { conn := CreateConnection() defer conn.Close() _ = query _ = args // run command, read rows → results return nil}
type UserService struct{}
func (*UserService) GetUser(userID int) *User { results := DatabaseManager{}.ExecuteQuery( "SELECT * FROM users WHERE id = @userId", userID, ) if len(results) == 0 { return nil } return MapToUser(results[0])}
func (*UserService) CreateUser(data UserData) { DatabaseManager{}.ExecuteQuery( "INSERT INTO users (name, email) VALUES (@name, @email)", data.Name, data.Email, )}// ❌ Without DRY — repeated open/close
struct Connection;
impl Connection { fn open() -> Self { Self }
fn close(self) {}}
struct User;struct UserData { name: String, email: String,}
struct UserServiceBad;
impl UserServiceBad { fn get_user(&self, _user_id: i32) -> Option<User> { let conn = Connection::open(); // ... query conn.close(); None }
fn create_user(&self, _data: UserData) { let conn = Connection::open(); // ... insert conn.close(); }}
// ✅ With DRY — connection lifecycle in one helper
struct DatabaseManager;
impl DatabaseManager { fn execute_query<F, T>(&self, operation: F) -> T where F: FnOnce(&Connection) -> T, { let conn = Connection::open(); let result = operation(&conn); conn.close(); result }}
struct UserService { db: DatabaseManager,}
impl UserService { fn get_user(&self, user_id: i32) -> Option<User> { self.db.execute_query(|_conn| { println!("SELECT * FROM users WHERE id = {}", user_id); None }) }
fn create_user(&self, data: UserData) { self.db.execute_query(|_conn| { println!("INSERT user: {} <{}>", data.name, data.email); }); }}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! 🎯