Principios del código limpio

Cómo los principios del código limpio transforman su experiencia de programación

El código se escribe una vez y se lee cientos de veces. El desarrollador que escribe una función rara vez es quien la depura seis meses después, la amplía para un nuevo requisito o tiene que comprender sus casos límite durante un incidente en producción. Cada decisión tomada al escribir código —el nombre de una variable, la longitud de una función, la estructura de una clase— facilita o dificulta el trabajo del siguiente lector. El código limpio es la disciplina de simplificarlo sistemáticamente.

El concepto fue formalizado por Robert C. Martin, conocido como «Tío Bob», en Clean Code: A Handbook of Agile Software Craftsmanship (2008), que sigue siendo la obra de referencia sobre el tema. Refactoring: Improving the Design of Existing Code, de Martin Fowler, aborda el problema complementario: qué hacer cuando el código existente no está limpio y necesita estarlo. Juntas, estas dos obras definen los fundamentos intelectuales de la práctica del código limpio. Esta guía cubre sus principios clave con ejemplos de código prácticos en los lenguajes más utilizados, incluyendo RPG y PL/I, para desarrolladores que mantienen sistemas heredados empresariales.

Violaciones de código limpias que no puedes ver

SMART TS XL Detecta complejidad, duplicación y código muerto en COBOL, Java, Python, RPG y otros lenguajes de programación.

MÁS INFORMACIÓN

¿Qué es el código limpio?

El código limpio es aquel que es fácil de leer, comprender, probar y modificar. La palabra "fácil" implica un trabajo arduo en esta definición. El código que un compilador acepta no es necesariamente código limpio. El código que pasa todas las pruebas no es necesariamente código limpio. El código es limpio cuando otro desarrollador, alguien que no lo escribió, en un contexto no previsto al momento de su creación, puede comprender su propósito rápidamente, modificarlo con confianza y extenderlo sin consecuencias inesperadas.

La definición de Martin Fowler es la más citada: «Cualquier tonto puede escribir código que una computadora pueda entender. Los buenos programadores escriben código que los humanos puedan entender». La medida de un código limpio es la comprensión humana, no la ejecución de la máquina.

El código limpio no se trata de estética. No se trata de seguir una guía de estilo por el mero hecho de hacerlo. Se trata de las propiedades estructurales del código que determinan cuánto esfuerzo requerirá cada cambio futuro. Un código que viola los principios del código limpio acumula constantemente deuda técnica, el costo creciente de los atajos anteriores, hasta que cada nueva funcionalidad requiere comprender y gestionar cuidadosamente la complejidad existente antes de añadir cualquier novedad.

Principios del código limpio: Guía de referencia rápida

Los principios de código limpio de Robert C. Martin se pueden resumir en un conjunto de directrices prácticas. La siguiente tabla relaciona cada principio con su regla principal y el problema que previene:

PrincipioRegla básicaProblema que previene
Nombres significativosLos nombres deben revelar la intención, las variables, las funciones y las clases.Sobrecarga cognitiva decodificando qué x, tmp, o obj en realidad representa
Funciones pequeñasLas funciones hacen una sola cosa: caber en una pantalla.Monolitos imposibles de comprobar e ilegibles que mezclan responsabilidades
Responsabilidad únicaCada clase/módulo tiene una razón para cambiar.Clases divinas que se rompen cuando cambia cualquiera de una docena de cosas no relacionadas.
SECO (No te repitas)Cada pieza de conocimiento tiene una representaciónCorrecciones de errores aplicadas en una copia pero no en otras; desviación de la lógica
KISS (Mantenlo simple)Prefiero la solución más sencilla que funcione.Código excesivamente complejo que resuelve problemas que nadie tiene
YAGNI (No lo vas a necesitar)No desarrolles funcionalidades hasta que sean necesarias.Código especulativo muerto que añade complejidad sin ningún beneficio.
Principio Abierto/CerradoAbierto a prórroga, cerrado a modificaciónCódigo que rompe el comportamiento existente al agregar un nuevo comportamiento.
Separación de interesesDiferentes responsabilidades en diferentes lugaresCódigo enredado donde cambiar una cosa rompe cosas no relacionadas
Evite los comentarios sobre qué; úselos para por qué.El código explica qué; los comentarios explican por qué.Comentarios obsoletos que confunden más de lo que informan.
Regla de los Boy ScoutsDeja el código más limpio de lo que lo encontraste.Deterioro gradual de la calidad por negligencia incremental

Explicación de los principios básicos del código limpio

SECO, no te repitas

El principio DRY (Don't Repeat Yourself) establece que cada fragmento de conocimiento debe tener una única representación, inequívoca y autorizada, dentro de un sistema. Cuando la misma lógica aparece en varios lugares, esas representaciones inevitablemente divergen. Un cambio en una regla de negocio se convierte en una búsqueda y reemplazo en múltiples archivos, y la falta de una copia genera un error.

El principio DRY (Don't Repeat Yourself) se aplica a más que solo código copiado y pegado. Se aplica a la configuración, la documentación y los esquemas de datos. Si la misma información debe mantenerse en dos lugares, se infringe el principio DRY, independientemente de si se copió o no código literalmente.

KISS, Mantenlo simple

KISS sostiene que los sistemas funcionan mejor cuando son simples, y que la simplicidad debe ser un objetivo primordial en el diseño. La complejidad no es señal de sofisticación, sino de que algo podría haberse expresado con mayor claridad.

La implicación práctica es la siguiente: cuando dos soluciones resuelven el mismo problema, es preferible la más sencilla. La solución más compleja podría manejar casos límite que aún no se han presentado. Sin duda, dificultará la comprensión del código para quienes lo lean.

YAGNI, no lo vas a necesitar

El principio YAGNI desaconseja añadir funcionalidades hasta que sean necesarias. Es una respuesta a la tendencia común a construir sistemas flexibles y extensibles para prever necesidades futuras que nunca se materializan. Cada abstracción, interfaz y opción de configuración que existe para un hipotético caso de uso futuro supone una carga cognitiva adicional para cualquier desarrollador que lea el código hoy en día.

Nombres significativos

Los nombres son el principal mecanismo de comunicación en el código fuente. Una función llamada process() No comunica nada sobre lo que procesa, cuándo o qué devuelve. Una función llamada calculateMonthlyInterest() Se comunica con precisión.

Criterios clave para un nombre: ¿Se puede determinar qué representa sin leer su implementación? ¿El nombre revela su propósito, parámetros y valor de retorno? ¿Sería necesario un comentario para explicar su función?

Funciones pequeñas

La regla general de Robert C. Martin: las funciones deben ser pequeñas, más pequeñas de lo que uno cree necesario. Una función que realiza una sola tarea puede nombrarse claramente, probarse de forma aislada y comprenderse sin leer su implementación. Una función que realiza varias tareas requiere comprenderlas todas simultáneamente.

El principio de responsabilidad única aplicado a las funciones: una función debe hacer una sola cosa, hacerla bien y hacer solo eso. Si necesitas la conjunción "y" para describir lo que hace una función, probablemente hace demasiado.

Código limpio en Java

La verbosidad de Java hace que la disciplina del código limpio sea especialmente importante. El código repetitivo que requiere el lenguaje puede ocultar la intención del código si no se gestiona con cuidado.

Java

// Before: unclear names, mixed responsibilities, magic numbers
public double calc(int x, int y) {
    double r = 0;
    if (y > 1000) {
        r = x * y * 0.1;
    } else {
        r = x * y * 0.05;
    }
    return r;
}

// After: meaningful names, single responsibility, named constants
private static final double PREMIUM_DISCOUNT_RATE = 0.10;
private static final double STANDARD_DISCOUNT_RATE = 0.05;
private static final int PREMIUM_THRESHOLD = 1000;

public double calculateDiscount(int quantity, int unitPrice) {
    double subtotal = quantity * unitPrice;
    return isPremiumOrder(unitPrice) 
        ? subtotal * PREMIUM_DISCOUNT_RATE 
        : subtotal * STANDARD_DISCOUNT_RATE;
}

private boolean isPremiumOrder(int unitPrice) {
    return unitPrice > PREMIUM_THRESHOLD;
}

Las prácticas clave para un código limpio en Java incluyen: priorizar la composición sobre la herencia, usar flujos para el procesamiento de datos en lugar de bucles extensos, convertir los números mágicos en constantes con nombre y mantener las clases enfocadas en una única responsabilidad. Los principios de código limpio de Robert C. Martin para Java incluyen la regla de que las clases deben ser pequeñas, medidas no en líneas sino en responsabilidades.

Java

// Clean Java: streams over imperative loops
List<String> activeUserEmails = users.stream()
    .filter(User::isActive)
    .map(User::getEmail)
    .collect(Collectors.toList());

Código limpio en Python

La filosofía de diseño de Python, que prioriza lo explícito sobre lo implícito y lo simple sobre lo complejo, se alinea naturalmente con los principios del código limpio. PEP 8 es la guía de estilo oficial de Python y la base para un código Python limpio.

pitón

# Before: vague names, long function, no separation
def do_stuff(d):
    res = []
    for i in d:
        if i['a'] > 18:
            res.append(i['n'].upper())
    return res

# After: meaningful names, separated concerns, Pythonic style
def get_adult_names_uppercase(users: list[dict]) -> list[str]:
    return [
        user["name"].upper()
        for user in users
        if user["age"] > 18
    ]

pitón

# Clean Python: context managers for resource handling
# Before: manual, error-prone
f = open("data.txt")
data = f.read()
f.close()

# After: guaranteed cleanup, self-documenting intent
with open("data.txt") as f:
    data = f.read()

El código limpio propio de Python utiliza comprensiones de listas en lugar de bucles manuales para transformaciones sencillas, sugerencias de tipo para firmas de funciones autodocumentadas, gestores de contexto para la gestión de recursos y clases de datos o tuplas con nombre en lugar de diccionarios simples para datos estructurados.

Código limpio en JavaScript y TypeScript

La flexibilidad de JavaScript es a la vez su mayor fortaleza y su principal desafío en cuanto a código limpio. Sin disciplina, las bases de código JavaScript acumulan patrones inconsistentes, conversiones de tipo implícitas y cadenas de devolución de llamada enredadas.

javascript

// Before: var, callback hell, no error handling
function getUser(id, cb) {
    db.query('SELECT * FROM users WHERE id = ' + id, function(err, rows) {
        if (err) cb(err);
        cb(null, rows[0]);
    });
}

// After: async/await, parameterized query, proper error handling
async function getUserById(userId: number): Promise<User | null> {
    const [rows] = await db.execute(
        'SELECT * FROM users WHERE id = ?',
        [userId]
    );
    return rows[0] ?? null;
}

mecanografiado

// Clean TypeScript: explicit types replace implicit any
// Before
function process(data) {
    return data.map(x => x.v * 2);
}

// After
interface DataPoint {
    value: number;
    label: string;
}

function doubleValues(dataPoints: DataPoint[]): number[] {
    return dataPoints.map(point => point.value * 2);
}

El JavaScript limpio utiliza const por defecto, let cuando sea necesario volver a encuadernar, y nunca varLas funciones puras, donde la misma entrada siempre produce la misma salida y no hay efectos secundarios, son el componente básico más limpio para la lógica de JavaScript.

Código limpio en C#

C# ofrece potentes funciones para escribir código limpio y expresivo. La evolución del lenguaje —LINQ, registros, coincidencia de patrones, tipos de referencia anulables— apunta constantemente hacia una sintaxis más declarativa y legible.

CSharp

// Before: magic numbers, verbose loop, mutable state
public double CalculateTotal(List<OrderItem> items)
{
    double total = 0;
    foreach (var item in items)
    {
        if (item.Quantity > 10)
            total += item.UnitPrice * item.Quantity * 0.9;
        else
            total += item.UnitPrice * item.Quantity;
    }
    return total;
}

// After: named constant, LINQ, single expression
private const double BulkDiscountRate = 0.9;
private const int BulkDiscountThreshold = 10;

public double CalculateTotal(IEnumerable<OrderItem> items) =>
    items.Sum(item => item.Quantity > BulkDiscountThreshold
        ? item.UnitPrice * item.Quantity * BulkDiscountRate
        : item.UnitPrice * item.Quantity);

Principios de código limpio de C#: usar propiedades en lugar de campos públicos para la encapsulación, aprovechar LINQ para operaciones de datos declarativas, usar registros para estructuras de datos inmutables, preferir interfaces sobre tipos concretos en firmas de métodos y usar tipos de referencia anulables (string? vs string) para hacer explícita la seguridad nula.

Código limpio en Kotlin

El diseño de Kotlin reduce específicamente el código repetitivo que dificulta mantener limpio Java, al tiempo que añade características, clases de datos, funciones de extensión y seguridad contra valores nulos, que permiten escribir código limpio de forma natural.

kotlin

// Before: verbose Java-style Kotlin
class User {
    var name: String = ""
    var email: String = ""
    var age: Int = 0
}

fun processUsers(users: List<User>): List<String> {
    val result = mutableListOf<String>()
    for (user in users) {
        if (user.age >= 18) {
            result.add(user.email)
        }
    }
    return result
}

// After: idiomatic clean Kotlin
data class User(val name: String, val email: String, val age: Int)

fun getAdultEmails(users: List<User>): List<String> =
    users.filter { it.age >= 18 }.map { it.email }

Kotlin data class Proporciona automáticamente los métodos equals, hashCode, copy y toString, eliminando el código repetitivo que hace que las clases de datos de Java sean propensas a errores y generen verbosidad. Las funciones de extensión permiten añadir métodos de utilidad sencillos a las clases existentes sin necesidad de herencia.

Código limpio en RPG y PL/I: lenguajes heredados, principios modernos

Los principios de código limpio se aplican a todos los lenguajes, incluidos los lenguajes empresariales que gestionan sistemas financieros, plataformas de seguros y aplicaciones gubernamentales en todo el mundo. RPG (Report Program Generator) y PL/I aún reciben mantenimiento activo en muchas organizaciones, y la misma disciplina que garantiza la limpieza de Java o Python hace que RPG y PL/I sean sostenibles.

Código limpio en RPG (ILE RPG) :

rpg

     // Before: cryptic two-character names, magic numbers
     C                   EVAL      D = Q * P * 1.05
     C                   IF        Q > 100
     C                   EVAL      D = Q * P * 0.95
     C                   ENDIF

     // After: meaningful names, named constants, clear intent
      /free
       dcl-c BULK_DISCOUNT_THRESHOLD 100;
       dcl-c BULK_DISCOUNT_RATE      0.95;
       dcl-c STANDARD_RATE          1.05;

       dcl-proc CalculateOrderTotal;
         dcl-pi *N packed(15:2);
           quantity  packed(7:0) value;
           unitPrice packed(9:2) value;
         end-pi;

         if quantity > BULK_DISCOUNT_THRESHOLD;
           return quantity * unitPrice * BULK_DISCOUNT_RATE;
         else;
           return quantity * unitPrice * STANDARD_RATE;
         endif;
       end-proc;
      /end-free

Prácticas clave para un RPG limpio: utilice el formato libre de ILE RPG (/free) sintaxis en lugar de formato fijo para mayor legibilidad, nombre los procedimientos de forma descriptiva, reemplace los valores codificados con constantes con nombre usando dcl-cy descomponer programas largos en procedimientos específicos utilizando dcl-proc.

Código limpio en PL/I :

recodo

/* Before: single-letter names, no structure */
CALC: PROC(X, Y) RETURNS(FLOAT);
  DCL (X, Y, R) FLOAT;
  IF Y > 1000 THEN R = X * Y * 0.1;
  ELSE R = X * Y * 0.05;
  RETURN(R);
END CALC;

/* After: meaningful names, named constants, clear intent */
DCL PREMIUM_THRESHOLD  FIXED DECIMAL(7) INIT(1000);
DCL PREMIUM_RATE       FLOAT           INIT(0.10);
DCL STANDARD_RATE      FLOAT           INIT(0.05);

CALCULATE_DISCOUNT: PROC(QUANTITY, UNIT_PRICE) RETURNS(FLOAT);
  DCL (QUANTITY, UNIT_PRICE) FLOAT;
  DCL SUBTOTAL              FLOAT;

  SUBTOTAL = QUANTITY * UNIT_PRICE;

  IF UNIT_PRICE > PREMIUM_THRESHOLD
    THEN RETURN(SUBTOTAL * PREMIUM_RATE);
    ELSE RETURN(SUBTOTAL * STANDARD_RATE);

END CALCULATE_DISCOUNT;

Los principios de código limpio de PL/I reflejan los de los lenguajes modernos: identificadores significativos (el límite de 31 caracteres de PL/I es suficiente para nombres descriptivos), constantes con nombre en lugar de números mágicos, procedimientos centrados en una sola tarea y manejo explícito de errores mediante el sistema de condiciones ON.

Herramientas de código limpio y análisis estático

Aplicar manualmente los principios de código limpio mediante la revisión del código es necesario, pero no suficiente a gran escala. Las herramientas de análisis estático aplican automáticamente las métricas de código limpio, señalando las infracciones antes de que se fusionen.

IdiomasLo que impone
SonarQube / SonarCloud30+ idiomasComplejidad, duplicación, malos olores en el código, seguridad
estilo de cuadrosJavaConvenciones de nomenclatura, formato, estructura
PMDJava, ApexCódigo duplicado, variables no utilizadas, complejidad
ESLint + typescript-eslintJavaScript, mecanografiadoEstilo, complejidad, código no utilizado, patrones asíncronos
Pylint + RadónPythonPEP 8, complejidad, índice de mantenibilidad
ReafiladoC#Estilo de código, código redundante, sugerencias de refactorización
ClippyHerrumbrePatrones idiomáticos, errores comunes
SMART TS XLCOBOL, RPG, PL/I, Java, Python y másComplejidad entre lenguajes, duplicación, código muerto

Las métricas de código limpio más comunes que miden las herramientas son: complejidad ciclomática (número de ramificaciones de decisión; si supera las 10, es una advertencia; si supera las 20, es crítica), complejidad cognitiva (dificultad para entender el código; una métrica específica de SonarQube que mejora la complejidad ciclomática para medir la legibilidad) y tasa de duplicación (porcentaje de código duplicado; si supera el 3%, requiere atención).

Cómo SMART TS XL Garantiza la aplicación de código limpio en todas las bases de código de la empresa.

Las herramientas de código limpio mencionadas anteriormente operan dentro de un solo lenguaje. En entornos empresariales donde coexisten servicios Java, canalizaciones Python, programas por lotes COBOL, módulos RPG y flujos de trabajo JCL, es necesario medir simultáneamente las infracciones de código limpio de cada lenguaje, y las relaciones entre componentes de diferentes lenguajes son tan importantes como la calidad dentro de cualquier archivo individual.

SMART TS XL, análisis de código estático Aplica métricas de código limpio a todos los lenguajes del entorno simultáneamente. La complejidad ciclomática, las tasas de duplicación, la identificación de código muerto y las métricas de acoplamiento estructural se calculan para los programas COBOL utilizando la misma metodología que para las clases Java, lo que produce mediciones de calidad comparables y unificadas en todo el portafolio de aplicaciones.

La capacidad de análisis de impacto permite abordar las infracciones de código limpio a nivel arquitectónico: cuando un párrafo COBOL tiene un alto acoplamiento con docenas de otros programas, el análisis de impacto muestra con precisión qué componentes se ven afectados antes de comenzar cualquier refactorización. Esto transforma la parálisis que experimentan los equipos en grandes bases de código en un programa de remediación estructurado con un alcance definido.

El mapeo de dependencias de la aplicación identifica las violaciones arquitectónicas del código limpio a nivel de sistema: qué componentes se han convertido en clases dominantes a escala empresarial, dónde las dependencias circulares violan la separación de responsabilidades entre lenguajes y dónde la misma lógica de negocio se ha implementado de forma independiente en múltiples sistemas sin que ninguna copia tenga conocimiento de la otra. Esta violación del principio DRY (Don't Repeat Yourself) entre lenguajes, donde la misma regla de negocio se mantiene por separado en un programa COBOL, un servicio Java y una canalización Python, es la categoría más costosa de violación del código limpio en los sistemas empresariales y resulta invisible para cualquier herramienta de un solo lenguaje.

Para los equipos que aplican principios de código limpio durante modernización heredada programas, SMART TS XL Proporciona el análisis previo a la refactorización que hace que el trabajo sea manejable: el código muerto se excluye del alcance antes de que comience la conversión, se identifican los componentes de mayor complejidad para darles prioridad y se dispone del gráfico de dependencias completo antes de que se realice cualquier cambio.

El código limpio es una disciplina de equipo, no una práctica individual.

Los principios de esta guía son más fáciles de enunciar que de mantener. Cualquier desarrollador puede escribir una función limpia de forma aislada. El reto reside en mantener un código limpio en todo un equipo, a lo largo del tiempo y en una base de código que crece a medida que cambian sus colaboradores. Esto requiere tres cosas: estándares compartidos que todos conozcan y hayan aceptado, herramientas que apliquen esos estándares automáticamente y una cultura de mejora incremental, la regla del Boy Scout aplicada de forma consistente, dejando cada parte del código ligeramente más limpia de lo que se encontró.

La formulación de Robert C. Martin sigue siendo la más útil: «Un código limpio siempre da la impresión de haber sido escrito por alguien que se preocupa por su trabajo». La prueba de que alguien se preocupó no reside en la ausencia de errores, sino en la presencia de nombres legibles, funciones bien definidas, una estructura clara y la ausencia de sorpresas. Esa evidencia es lo que hace que valga la pena trabajar en un código, contribuir a él y mantenerlo a lo largo de los años y los cambios de equipo que cualquier sistema exitoso acumulará.