Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

DRY, KISS, YAGNI

Ba nguyên tắc này là nền tảng trong việc viết code clean và maintainable.

DRY - Don’t Repeat Yourself

Concept

Mỗi piece of knowledge trong hệ thống nên có một single, unambiguous representation. Không nên duplicate code hay logic.

Examples

Bad - Code Duplication:

// Duplicate validation logic
public class UserController : ControllerBase
{
    public IActionResult CreateUser(CreateUserRequest request)
    {
        if (string.IsNullOrEmpty(request.Name))
            return BadRequest("Name is required");
            
        if (request.Name.Length < 2)
            return BadRequest("Name must be at least 2 characters");
            
        if (request.Name.Length > 100)
            return BadRequest("Name must not exceed 100 characters");
            
        // Save user
    }
    
    public IActionResult UpdateUser(UpdateUserRequest request)
    {
        // Same validation repeated!
        if (string.IsNullOrEmpty(request.Name))
            return BadRequest("Name is required");
            
        if (request.Name.Length < 2)
            return BadRequest("Name must be at least 2 characters");
            
        if (request.Name.Length > 100)
            return BadRequest("Name must not exceed 100 characters");
            
        // Update user
    }
}

Good - Extract to Shared Method:

public static class ValidationHelper
{
    public static (bool IsValid, string Error) ValidateName(string name)
    {
        if (string.IsNullOrEmpty(name))
            return (false, "Name is required");
            
        if (name.Length < 2)
            return (false, "Name must be at least 2 characters");
            
        if (name.Length > 100)
            return (false, "Name must not exceed 100 characters");
            
        return (true, null);
    }
}

public class UserController : ControllerBase
{
    public IActionResult CreateUser(CreateUserRequest request)
    {
        var (isValid, error) = ValidationHelper.ValidateName(request.Name);
        if (!isValid)
            return BadRequest(error);
            
        // Save user
    }
    
    public IActionResult UpdateUser(UpdateUserRequest request)
    {
        var (isValid, error) = ValidationHelper.ValidateName(request.Name);
        if (!isValid)
            return BadRequest(error);
            
        // Update user
    }
}

Good - Use Inheritance or Composition:

// Base class for shared behavior
public abstract class BaseController : ControllerBase
{
    protected IActionResult ValidateRequest<T>(T request)
    {
        // Common validation
    }
}

public class UserController : BaseController
{
    // Inherits validation
}

Benefits

  • Maintainability: Thay đổi một nơi
  • Reduced bugs: Logic tập trung
  • Better testing: Test một lần
  • Readability: Clearer code

KISS - Keep It Simple, Stupid

Concept

Giải pháp đơn giản nhất thường là tốt nhất. Tránh over-engineering và unnecessary complexity.

Examples

Bad - Over-complicated:

// Too many abstractions
public interface IUserProcessor
{
    Task<IResult<UserProcessingResult>> ProcessAsync(UserProcessingRequest request);
}

public class UserProcessingContext
{
    private readonly IUserProcessor _processor;
    private readonly IUserRepository _repository;
    private readonly ICacheService _cache;
    
    public async Task<IResult<UserProcessingResult>> ProcessUser(
        UserProcessingRequest request,
        ProcessingContext context)
    {
        var strategy = GetProcessingStrategy(context);
        return await strategy.ProcessAsync(request);
    }
}

Good - Simple and Direct:

// Direct and simple
public class UserService
{
    public async Task<User> CreateUser(CreateUserRequest request)
    {
        if (request == null)
            throw new ArgumentNullException(nameof(request));
            
        var user = new User
        {
            Name = request.Name,
            Email = request.Email
        };
        
        await _userRepository.AddAsync(user);
        return user;
    }
}

When to Avoid Over-Simplification

// Don't oversimplify complex domain logic
// Still maintain proper structure for complex scenarios
public class OrderProcessor
{
    private readonly IEnumerable<IOrderValidationStrategy> _strategies;
    
    public OrderProcessor(IEnumerable<IOrderValidationStrategy> strategies)
    {
        _strategies = strategies;
    }
    
    public ValidationResult Validate(Order order)
    {
        var result = new ValidationResult();
        
        foreach (var strategy in _strategies)
        {
            var strategyResult = strategy.Validate(order);
            result.Merge(strategyResult);
        }
        
        return result;
    }
}

Benefits

  • Easier to understand
  • Faster to develop
  • Less bugs
  • Easier to maintain

YAGNI - You Aren’t Gonna Need It

Concept

Không implement tính năng cho đến khi nó thực sự cần thiết. Tránh speculative design.

Examples

Bad - Over-Engineering:

// Building infrastructure for future needs that may never come

public interface IUserRepository
{
    Task<User> GetByIdAsync(Guid id);
    Task<User> GetByEmailAsync(string email);  // Not used yet!
    Task<List<User>> GetByStatusAsync(UserStatus status);  // Not used yet!
    Task<User> GetByPhoneAsync(string phone);  // Not used yet!
    Task<List<User>> SearchAsync(string query);  // Not used yet!
}

public class UserRepository : IUserRepository
{
    // Implementing all these methods before they're needed
}

Good - Just Enough:

public interface IUserRepository
{
    Task<User> GetByIdAsync(Guid id);
    // Add more as needed
}

public class UserRepository : IUserRepository
{
    public async Task<User> GetByIdAsync(Guid id)
    {
        return await _context.Users.FindAsync(id);
    }
}

When You Actually Need Extensibility

// If you genuinely anticipate change, use interface properly
public interface IUserRepository
{
    Task<User> GetByIdAsync(Guid id);
}

// Extensibility built in through interface, not through extra methods
public interface IUserRepository
{
    // Base operations
}

// Future: Easy to extend with decorator pattern
public class CachedUserRepository : IUserRepository
{
    private readonly IUserRepository _inner;
    private readonly ICache _cache;
    
    public async Task<User> GetByIdAsync(Guid id)
    {
        return await _cache.GetOrSetAsync(
            $"user:{id}",
            () => _inner.GetByIdAsync(id));
    }
}

Don’t Confuse YAGNI with Bad Design

// YAGNI doesn't mean "write bad code"
public class PaymentService
{
    // Good: Proper abstraction exists
    private readonly IPaymentGateway _paymentGateway;
    
    // Bad: Hardcoded, not testable
    private readonly SqlConnection _connection;
}

Benefits

  • Faster delivery: Focus on what matters now
  • Less waste: No unused code
  • Simpler codebase: Easier to navigate
  • Adaptability: Change direction easily

How They Work Together

DRY    → Avoid duplication
KISS   → Keep solution simple
YAGNI  → Only build what's needed

Together: Build simple, non-repeated solutions for current needs

Anti-Patterns to Avoid

Anti-PatternDescription
Premature AbstractionCreating abstractions before needed
Speculative GeneralityBuilding for “future” features
Golden HammerUsing one solution for everything
Not Invented HereAvoiding existing solutions

Real-World Application

// Before: DRY violation
public class OrderConfirmationEmail
{
    public void SendConfirmation(Order order)
    {
        var body = $"Order #{order.Id}\n";
        body += $"Customer: {order.CustomerName}\n";
        
        // Duplicate email formatting logic
    }
}

public class ShippingNotificationEmail
{
    public void SendNotification(Order order)
    {
        var body = $"Order #{order.Id}\n";
        body += $"Customer: {order.CustomerName}\n";
        
        // Same logic again!
    }
}

// After: DRY + KISS + YAGNI
public class EmailService
{
    // Shared, simple, just what's needed
    public void Send(string template, object data)
    {
        var body = RenderTemplate(template, data);
        _emailGateway.Send(body);
    }
}

Summary

PrincipleFocusAction
DRYDuplicationExtract shared code
KISSComplexitySimplify solution
YAGNIFutureBuild only what’s needed

References