Pruebas y calidad de código

[Olor de código #3] Código ravioli: código cuyo flujo nadie conoce

El código ravioli tiene funciones de cinco líneas y una sola responsabilidad, pero nadie puede explicar qué ocurre al pulsar un botón. Aquí resumimos por qué se divide demasiado, el coste que revelan los saltos y cómo recuperar el flujo.

6 min de lectura
Imagen de portada de [Olor de código #3] Código ravioli: código cuyo flujo nadie conoce

En una revisión de código aparece esta situación: abres el archivo y todas las funciones tienen cinco líneas o menos.

Este artículo continúa el anterior Olor de código #2.

Los nombres son buenos y cada clase tiene una sola responsabilidad. No hay nada que señalar.

Pero nadie puede responder de inmediato a la pregunta: «¿Qué ocurre al pulsar este botón?».

Este código se denomina código ravioli (Ravioli Code) (Texto original de C2).

No siempre era un insulto

Curiosamente, el término no siempre se usa de forma negativa. Los raviolis son pasta formada por pequeñas piezas que envuelven bien el relleno.

Son pequeños objetos bien encapsulados, justo lo que perseguía la orientación a objetos. De hecho, a veces se presenta el ravioli como lo opuesto al código espagueti.

El problema es la cantidad.

Cada pieza es perfecta, pero hay 200 en el plato y en ningún sitio se indica en qué orden ni cómo se conectan.

Concretamente, tiene este aspecto.

final class CheckoutCoordinator {
    func start() { validator.validate(cart) }
}

final class CartValidator {
    func validate(_ cart: Cart) { stockChecker.check(cart.items) }
}

final class StockChecker {
    func check(_ items: [Item]) { priceCalculator.calculate(items) }
}

final class PriceCalculator {
    func calculate(_ items: [Item]) { paymentPreparer.prepare(items) }
}
// ... Hay dieciséis clases más como esta

Cada clase es impecable. Tiene un nombre preciso y hace una sola cosa.

Pero no existe ningún archivo que conozca todo el flujo de pago.

El flujo solo existe en las relaciones de llamada entre clases, y solo se descubre conectando el depurador, no leyendo el código.

Por qué se divide demasiado

Cuando se acepta «cuanto más corta sea una función, mejor» como regla.『Clean Code』llega a decir que una función debe tener dos o tres líneas, o cuatro como máximo.

El objetivo real de este consejo se acerca a hacer que una función trate un solo nivel de abstracción. Al convertirlo en un número de líneas, se pierde el propósito.

El resultado son treinta funciones de cinco líneas, de las que veintiocho solo se llaman desde un lugar.

Cuando el nombre no resume el contenido., handleUserAction, processData y updateState no explican qué hacen.

Si divides el código en funciones con esos nombres, el lector debe abrir el cuerpo y aumenta el número de lugares que debe consultar.

Una buena extracción evita mirar el cuerpo; aquí ocurre lo contrario.

Cuando se mezclan niveles de abstracción. En una función conviven una frase de política como «validar el pedido» y otra de detalle como «incrementar el índice en 1». La mirada sube y baja continuamente.

Si divides sin criterio en este estado, creas fragmentos con niveles desordenados.

Cuando extraes código usado una sola vez pensando en reutilizarlo. Es la misma mentalidad que crea código lasaña; solo cambia la dirección, de vertical a horizontal.

Diagrama comparativo de una estructura de clases ravioli encadenadas y una función orquestadora
Basta con un lugar donde escribir el flujo para resolver gran parte del problema

El coste aparece en el número de saltos

El coste del código ravioli se resume en una frase: ¿cuántas veces abres un archivo para responder una pregunta?

Al leer código, las personas mantienen el flujo en la memoria a corto plazo. Se difumina al superar tres o cuatro elementos.

Después de saltar seis veces entre archivos, se pierde qué buscabas al principio y vuelves al inicio.

Cuando se repite, resulta más rápido ejecutar el código para comprobarlo que leerlo.

También aparecen efectos secundarios.

  • Al añadir una función, no encuentras el fragmento existente y creas otro parecido. Los duplicados aumentan silenciosamente.
  • Como no encuentras dónde corregir el error, añades un arreglo temporal al final del flujo, es decir, en la pantalla.
  • Como el flujo completo no está escrito en el código, solo queda en la documentación o en la cabeza de alguien. Cuando esa persona se va del equipo, desaparece con ella.

Cómo recuperar el flujo

Crea un lugar donde escribir el flujo. Es la medida más eficaz.

Añade una función que muestre de un vistazo el orden completo y haz que solo describa ese orden.

func checkout(_ cart: Cart) async throws -> Receipt {
    try validate(cart)
    try await reserveStock(cart.items)
    let amount = calculateTotal(cart)
    let payment = try await charge(amount)
    return try await confirm(cart, payment)
}

Los detalles siguen en sus respectivos lugares. Lo que cambia es que el flujo queda escrito en una sola pantalla.

A veces estas funciones se llaman orquestadores. Eso es exactamente lo que le falta al código ravioli.

**Mantén un solo nivel por capa.**Las cinco líneas de la función superior son frases del mismo nivel. Si entra una condición detallada como items.count > 0, se rompe el nivel.

Acostumbrarse a comprobar si las frases están al mismo nivel al leer una función resulta más útil que usar un criterio de división.

**Decide si extraer por el nombre.**El criterio no es el número de líneas, sino esta pregunta: «¿El nombre de este fragmento comunica más que el cuerpo?».

Extraer if user.age >= 19 como isAdult(user) revela la intención y aporta valor. Extraer array.append(item) como addItem no aporta nada.

**Mantén cerca lo que está cerca.**El código que cambia junto debe estar en el mismo archivo y carpeta.

Si, por respetar la convención de un tipo por archivo, separas tipos que siempre se abren juntos, la proximidad importa más que la convención.

**El mismo criterio se aplica a los servicios.**Una división excesiva de microservicios se denomina nanoservicios.

Una arquitectura que atraviesa doce servicios para procesar una petición es código ravioli extendido más allá de la red. Aquí al coste de los saltos se suman la latencia y los puntos de fallo.

Imagen que compara lado a lado las estructuras de tres olores de código: espagueti, lasaña y ravioli
Los tres obtienen la máxima puntuación por fragmento

Lasaña, ravioli y espagueti

Al ponerlos juntos, se resumen así.

Forma Estado de los fragmentos Problema
Espagueti Grande y enredado No se puede predecir el flujo de ejecución
Lasaña Capas verticales Un cambio atraviesa varias capas
Ravioli Pequeño y limpio El flujo entre fragmentos no aparece en ningún sitio

Los tres tienen un alto coste para comprender el conjunto; solo cambia el método.

Si mides la calidad del código solo por la belleza de cada fragmento, no detectarás la lasaña ni el ravioli. Ambos sacan la máxima puntuación por fragmento.

Resumen

  • El código ravioli tiene tantos fragmentos pequeños y limpios que nadie puede explicar el flujo completo.
  • También se usó de forma positiva para referirse a código bien encapsulado. El problema es la cantidad.
  • Las causas principales son las reglas de líneas, los nombres ambiguos y los niveles de abstracción mezclados.
  • La clave de la solución es un lugar donde escribir el flujo.
  • El criterio de extracción no es la longitud, sino el nombre. Extrae solo cuando el nombre comunica más que el cuerpo.

La próxima entrega es el extremo opuesto: no hay demasiados fragmentos, sino uno solo.

Veremos el objeto Dios que conoce toda la aplicación desde una única clase.

Fuentes y criterios de verificación

  • Ravioli Code — C2 Wiki · texto original del autor · verificado 2026-08-17 · fundamento: metáfora del código ravioli como objetos pequeños y encapsulados excesivamente fragmentados

Seguir leyendo