Skip to content

DRY - Don't Repeat Yourself


Definition

DRY Definition


Code Duplication

// BAD: Copy-paste code
public class OrderService {

    public double calculateOrderTotal(Order order) {
        double subtotal = 0;
        for (OrderItem item : order.getItems()) {
            subtotal += item.getPrice() * item.getQuantity();
        }
        double tax = subtotal * 0.08;  // 8% tax
        double shipping = subtotal > 100 ? 0 : 10;  // Free shipping over $100
        return subtotal + tax + shipping;
    }

    public double calculateQuoteTotal(Quote quote) {
        double subtotal = 0;
        for (QuoteItem item : quote.getItems()) {
            subtotal += item.getPrice() * item.getQuantity();  // Same logic!
        }
        double tax = subtotal * 0.08;  // Same tax!
        double shipping = subtotal > 100 ? 0 : 10;  // Same shipping!
        return subtotal + tax + shipping;
    }
}

// GOOD: Extract common logic
public class PricingService {

    public Money calculateSubtotal(List<LineItem> items) {
        return items.stream()
            .map(item -> item.getPrice().multiply(item.getQuantity()))
            .reduce(Money.ZERO, Money::add);
    }

    public Money calculateTax(Money subtotal) {
        return subtotal.multiply(TaxConfig.RATE);  // Single source of truth
    }

    public Money calculateShipping(Money subtotal) {
        return subtotal.isGreaterThan(ShippingConfig.FREE_THRESHOLD)
            ? Money.ZERO
            : ShippingConfig.STANDARD_RATE;
    }

    public Money calculateTotal(List<LineItem> items) {
        Money subtotal = calculateSubtotal(items);
        return subtotal
            .add(calculateTax(subtotal))
            .add(calculateShipping(subtotal));
    }
}

Knowledge Duplication

// BAD: Validation rules duplicated
public class UserController {
    public void createUser(UserRequest request) {
        if (request.getEmail() == null ||
            !request.getEmail().matches("^[\\w.-]+@[\\w.-]+\\.\\w+$")) {
            throw new ValidationException("Invalid email");
        }
        if (request.getPassword().length() < 8) {
            throw new ValidationException("Password too short");
        }
        // ...
    }
}

public class UserService {
    public void updateEmail(String userId, String newEmail) {
        // Same validation repeated!
        if (newEmail == null ||
            !newEmail.matches("^[\\w.-]+@[\\w.-]+\\.\\w+$")) {
            throw new ValidationException("Invalid email");
        }
        // ...
    }
}

// GOOD: Centralized validation knowledge
public class Email {  // Value object
    private static final Pattern PATTERN =
        Pattern.compile("^[\\w.-]+@[\\w.-]+\\.\\w+$");

    private final String value;

    public Email(String value) {
        if (value == null || !PATTERN.matcher(value).matches()) {
            throw new InvalidEmailException(value);
        }
        this.value = value.toLowerCase();
    }
}

public class Password {
    private static final int MIN_LENGTH = 8;

    public Password(String value) {
        if (value == null || value.length() < MIN_LENGTH) {
            throw new WeakPasswordException();
        }
        // ...
    }
}

// Now validation is automatic
public void createUser(Email email, Password password) { }
public void updateEmail(UserId userId, Email newEmail) { }

Data Duplication

DRY Data Duplication


When DRY Doesn't Apply

// NOT duplication - Different knowledge!
// These look similar but serve different purposes

// Business rule: Cart discount
public Money calculateCartDiscount(Cart cart) {
    if (cart.getTotal().isGreaterThan(Money.of(100))) {
        return cart.getTotal().multiply(0.10);  // 10% off
    }
    return Money.ZERO;
}

// Business rule: Loyalty reward
public Money calculateLoyaltyBonus(Customer customer) {
    if (customer.getTotalPurchases().isGreaterThan(Money.of(100))) {
        return Money.of(10);  // $10 reward
    }
    return Money.ZERO;
}

// These both check "$100 threshold" but:
// - They represent DIFFERENT business rules
// - They might change independently
// - Forcing them together creates wrong coupling

// RULE: If two pieces of code might change for
// DIFFERENT REASONS, they're not duplicates!

DRY at Different Levels

DRY Levels


Tips & Tricks

DRY Tips