Cláusulas de guarda

15-06-2026

Debemos tener en cuenta que escribir código sostenible es la forma más barata de añadir, cambiar o quitar funcionalidad; algo que agradecerán tanto nuestros compañeros como nuestro “yo del futuro”.

En esta ocasión, quiero hablar de las cláusulas de Guarda. Se trata de comprobaciones booleanas situadas al inicio de un método que evalúan las condiciones necesarias para que el resto del proceso tenga sentido. Si una condición no se cumple, la ejecución se interrumpe de inmediato (usualmente con un return o lanzando una excepción), evitando así el anidamiento innecesario de bloques if-else.

Me gusta compararlo con el “derecho de admisión” de un local: es como poner a un portero en la puerta. Si alguien no tiene entrada, se va a casa antes de poner un pie dentro. Así, quienes logran entrar (el cuerpo del método) saben que todos allí cumplen los requisitos y pueden disfrutar de la fiesta sin interrupciones.

En los proyectos en los que colaboramos, nos solemos encontrar métodos con muchos condicionales anidados que se van desplazando a la derecha. Esto se conoce como Pyramid of Doom, como muestra el siguiente ejemplo:

 

export class Account { public constructor(public totalBalance: number, public isActive: boolean) {} public decrease(amount: number): void { if (amount > 0) { if (this.isActive) { if (this.totalBalance >= amount) { this.totalBalance = this.totalBalance - amount; } else { throw new Error("The account balance is not enough"); } } else { throw new Error("The account is not active"); } } else { throw new Error("The amount has to be above zero"); } } }

Como podemos apreciar, es una lógica bastante complicada de entender y mucho más de cambiar, sin cometer un error.

Antes de nada, hacemos las pruebas unitarias para asegurarnos de que no hemos cambiado el comportamiento observable y hemos cubierto todos los caminos posibles.

 

import { describe, it, expect } from "vitest"; import { Account } from "./account"; describe("Account", () => { it("should decrease the balance when account is active and has enough funds", () => { const initialBalance = 100; const account = new Account(initialBalance, true); const amountToDecrease = 40; account.decrease(amountToDecrease); expect(account.totalBalance).toBe(60); }); it("should allow balance to reach exactly zero", () => { const account = new Account(50, true); account.decrease(50); expect(account.totalBalance).toBe(0); }); it("should throw error when amount is zero", () => { const account = new Account(100, true); expect(() => account.decrease(0)).toThrow("The amount has to be above zero"); }); it("should throw error when amount is negative", () => { const account = new Account(100, true); expect(() => account.decrease(-10)).toThrow("The amount has to be above zero"); }); it("should throw error when the account is not active", () => { const account = new Account(100, false); expect(() => account.decrease(50)).toThrow("The account is not active"); }); it("should throw error when balance is insufficient", () => { const account = new Account(50, true); expect(() => account.decrease(100)).toThrow("The account balance is not enough"); }); it("should NOT modify balance if an error is thrown", () => { const account = new Account(50, true); try { account.decrease(100); } catch (e) { // Ignore error for this specific check } expect(account.totalBalance).toBe(50); }); });

En el libro de Kent Beck, Implementation Patterns, podemos leer las cláusulas de guarda como una retirada a tiempo[1] para evitar toda la complejidad de las sentencias if-then-else. Martin Fowler, en su famoso libro Refactoring, Improving the design of the existing code, podemos encontrar la receta Replace Nested Conditional with Guard Clauses[2], que es básicamente la misma idea. Incluso Fowler habla de su amigo Kent al describir este truco a la hora de mejorar el código. Por otro lado, si los libros en la lengua inglesa nos cuentan un poco, el libro Código Sostenible, de Carlos Blé Jurado[3] se hace también eco de ello.

Por lo tanto, el código nos quedaría de la siguiente manera:

 

export class Account { public constructor(public totalBalance: number, public isActive: boolean) {} public decrease(amount: number): void { if (amount <= 0) throw new Error("The amount has to be above zero"); if (!this.isActive) throw new Error("The account is not active"); if (this.totalBalance < amount) throw new Error("The account balance is not enough"); this.totalBalance -= amount; } }

Como podemos observar, se mejora la legibilidad, porque el flujo principal de la función es lineal y también se reduce la carga mental, porque los if anidados obligan al cerebro a actuar como un compilador, manteniendo una “pila” (stack) de condiciones. Con las guardas, el cerebro “libera” memoria en cada línea.

Conclusión

Como dice Martin Fowler en Refactoring, las cláusulas de guarda permiten que el lector no tenga que mantener en su cabeza un estado mental complejo. Separar claramente el “chequeo de errores” de la “lógica real” es un paso de gigante hacia un código más limpio y sostenible.


Referencias [1] Implementation Patterns por Kent Beck pag. 97 [2] Refactoring, improving the design of the existing code por Martin Fowler pag. 201 [3] Código Sostenible por Carlos Blé Jurado pag. 121