Clean Code en C#: cómo escribir código que apetece leer
Cada desarrollador .NET se ha enfrentado a la situación en la que, después de un mes o dos, el código propio se vuelve ilegible. Para evitar esto, existe la filosofía Clean Code, popularizada por Robert Martin. En este artículo analizaremos los principios fundamentales del código limpio en C# con ejemplos concretos que te ayudarán a escribir aplicaciones mantenibles y escalables.
¿Por qué es importante Clean Code para un desarrollador C#?
Clean Code no es solo un formato bonito. Es un conjunto de prácticas que hacen que el código sea:
- Legible — cualquier desarrollador entenderá rápidamente la lógica;
- Mantenible — es fácil realizar cambios sin riesgo de romper el sistema;
- Comprobable — el código se cubre fácilmente con pruebas unitarias;
- Eficiente — ausencia de abstracciones innecesarias y duplicación.
En C#, con su tipado estricto y las potentes capacidades de .NET (LINQ, async/await, Records), seguir los principios de Clean Code es especialmente importante. Un código deficiente en C# puede provocar errores no evidentes y problemas de rendimiento.
Principio 1: Nombrado de variables y métodos
El nombre de una variable o método debe responder a la pregunta "¿qué es esto?" o "¿qué hace?". Evite las abreviaturas de una sola letra (excepto los índices de bucles).
Ejemplo incorrecto
public class Data{ public int a; // ¿qué es esto? public string b; // no está claro
public void Do(int x, int y) // ¿qué hace el método? { var r = x + y; Console.WriteLine(r); }}Ejemplo correcto (Clean Code)
public class Customer{ public int Id { get; set; } public string FullName { get; set; }
public void PrintCustomerSummary(int customerId, int orderCount) { var totalOrders = customerId + orderCount; Console.WriteLine($"Customer {customerId} has {totalOrders} orders"); }}Regla: Los nombres deben ser expresivos. Si una variable se llama data, casi siempre es malo. Use customerData, orderList, invoiceTotal.
Principio 2: Métodos pequeños con una sola responsabilidad
Cada método debe hacer exactamente una cosa. Si un método contiene más de 10-15 líneas, probablemente deba dividirse. Esta es la base del principio Single Responsibility (de SOLID).
Ejemplo incorrecto (el método lo hace todo)
public void ProcessOrder(int orderId){ // 1. Validación if (orderId <= 0) throw new ArgumentException("Invalid ID"); // 2. Obtención de datos de la BD var order = _db.Orders.Find(orderId); // 3. Cálculo del descuento decimal discount = 0; if (order.Total > 1000) discount = order.Total * 0.1m; // 4. Envío de email var email = new MailMessage(); email.To.Add(order.CustomerEmail); email.Subject = "Order processed"; // 5. Registro en log _logger.LogInformation("Order {Id} processed", orderId);}Ejemplo correcto (refactorización)
public void ProcessOrder(int orderId){ ValidateOrderId(orderId); var order = GetOrderFromDatabase(orderId); var discount = CalculateDiscount(order); SendConfirmationEmail(order, discount); LogOrderProcessing(orderId);}
private void ValidateOrderId(int orderId){ if (orderId <= 0) throw new ArgumentException("Order ID must be positive", nameof(orderId));}
private Order GetOrderFromDatabase(int orderId){ return _db.Orders.Find(orderId) ?? throw new InvalidOperationException("Order not found");}
private decimal CalculateDiscount(Order order){ return order.Total > 1000 ? order.Total * 0.1m : 0;}
private void SendConfir