DRY Refactoring
Process
-
Identify - Exact copies, similar patterns, parallel hierarchies, naming patterns (data1 /data2 , handleXClick )
-
Analyze - Coupling, cohesion, frequency (Rule of Three: wait for 3+ occurrences), volatility
-
Refactor - Choose technique below, extract incrementally, test after each step
Techniques
Extract Function - Same logic in multiple places
getFullName(user: User) => ${user.firstName} ${user.lastName}
Extract Variable - Repeated expression
const isWorkingAge = user.age >= 18 && user.age < 65;
Parameterize - Code differs only in values
validateField(value: string, pattern: RegExp) // Use: validateField(email, EMAIL_REGEX)
Extract Class - Related functions scattered
class UserRewards { calculateDiscount(user, amount) { } getLoyaltyPoints(user) { } }
Polymorphism - Repeated switch/if-else
interface PaymentProcessor { process(amount: number): void } class CreditProcessor implements PaymentProcessor { }
Strategy Pattern - Duplicated algorithm selection
const strategies = { date: byDate, name: byName }; items.sort(strategies[sortType] ?? byPriority);
Pull Up Method - Identical methods in subclasses
class BaseUser { getDisplayName() { } } class AdminUser extends BaseUser { }
Detection
Code Smells: Look for numbered variables (data1 , data2 ), parallel function names (handleXClick ), near-identical code differing only in constants, repeated validation/error handling, parallel class structures, large switches in multiple places, repeated null checks, magic numbers
Rule of Three: Wait for 3+ occurrences before abstracting
When NOT to DRY
-
Coincidental similarity - Avoid abstracting different domains/business rules that happen to look alike (will diverge)
-
Premature abstraction - Wait until pattern is clear; early abstraction often guesses wrong
-
Single use - Skip abstraction when code appears 1-2 times and is unlikely to grow
-
Test clarity - Prefer readable test setup over DRY
-
Over-engineering - Avoid abstracting every 2-3 line similarity
Patterns
-
Configuration over code - Use data structures to eliminate conditionals
-
Template Method - Define skeleton in base, vary steps in subclasses
-
Dependency Injection - Parameterize dependencies to reduce coupling
-
Builder - Construct complex objects incrementally
Best Practices
-
Refactor only after tests pass (green)
-
Apply one refactoring at a time
-
Commit changes frequently
-
Name abstractions for intent, not implementation
-
Consider performance impact of abstractions
-
Review abstractions with team before finalizing