//Calidad de código a escala industrial con IA
CRAP, mutation testing y quality gates cuando el agente genera más de lo que puedes leer
La revisión humana no escala cuando un agente de IA genera más código del que nadie puede leer. Cómo un circuito de quality gates, CRAP y mutation testing impone la calidad antes de ejecutar el código.|Es irónico que, cuanto más código escribimos, menos podemos leerlo línea a línea. Durante décadas la calidad se garantizaba con la revisión humana: un compañero leía el diff, fruncía el ceño y te sugería amablemente que mejorases el maldito código. Con los agentes generando volúmenes que ningún equipo puede masticar, ese modelo clásico ya no escala.
Y si insistes en que un humano sea el último filtro de cada cambio, te quedan dos cartas: o reduces la velocidad del agente a ritmo de junior, o aceptas que la mayor parte del código entre en producción sin que nadie lo haya mirado de verdad.
La calidad no puede depender de la revisión humana. Debe imponerse en el sistema de desarrollo.
La calidad está en los constraints
Cada vez más verificaciones tienen que ocurrir fuera de la cabeza del programador: en el entorno, en el harness del agente y en la tubería de CI. Los constraints dicen qué está permitido y qué no. Tests que fallan en rojo. Reglas deterministas. Umbrales que no se negocian con un prompt persuasivo.
Ese conjunto forma los quality gates: barreras que el código tiene que cruzar antes de considerarse entregable. Con miles de cambios diarios, la fiabilidad depende de un circuito continuo de verificación automática.
Esto añade burocracia, pero es el precio a pagar para aguantar el ritmo infernal de generación agéntica. Y no tiene por qué ser tediosa ni la tienes que hacer tú.
Quality gates que el agente no puede negociar
Un quality gate es una barrera objetiva: el código la cruza o no. No se salta con un prompt.
Un gate útil debe ser barato de ejecutar, difícil de engañar y fácil de convertir en criterio de éxito: pasa o falla, sin debate. Si el agente no sabe cuándo ha terminado, seguirá generando. Si el criterio es “parece correcto”, generará algo que “parece funcionar”.
En la práctica, el circuito suele incluir varios de estos quality gates:
- Compilador: el código debe compilar, arrancar y estar bien tipado (esencial en TypeScript).
- Tests: unitarios, de integración, e2e, de propiedades y de aceptación.
- Métricas estructurales: complejidad ciclomática, tamaños, cobertura de pruebas.
- Reglas de arquitectura y linting: dependencias, duplicación de código.
Todo esto para verificar que el código se comporta como se espera hoy, y que es fácil de cambiar para adoptar un nuevo comportamiento mañana.
Y para hacerlo a escala industrial, dos herramientas están en auge: el análisis CRAP y el mutation testing.
CRAP: el riesgo de cambiar código complejo y no cubierto
CRAP, además de cagada, significa Change Risk Anti-Patterns. El nombre es de broma, pero la métrica no. Combina dos señales que, por separado, no nos dicen tanto como cuando se juntan:
- la complejidad ciclomática de una función (cuántos caminos independientes tiene),
- y la cobertura de tests de esa función (porcentaje de instrucciones que se ejercitan).
Para obtener un único valor hay una fórmula. Te la dejo aquí como referencia. Puedes olvidarla en cuanto la leas.
CRAP(f) = CC² × (1 − coverage)³ + CC
CC es la complejidad. coverage es la fracción cubierta, de 0 a 1. Complejidad alta sube el riesgo. Cobertura baja lo eleva. Complejidad alta y cobertura baja lo disparan.
Una función pequeña y bien testada se queda cerca de su propia complejidad. Una función de veinte ramas sin tests se convierte en un artefacto tóxico que nadie quiere manipular.
El índice de mierda se compara entonces con un umbral. Por encima de 30, el riesgo es inasumible. Uncle Bob recomienda gates con umbral CRAP de 8, lo cual es muy agresivo. Para llegar ahí, él empieza midiendo todo el código, ataca las peores funciones (>36) y va endureciendo poco a poco: 16, luego 8.
Lo importante es que, para el agente, CRAP es un criterio accionable: o reduce ramas, o escribe más tests que cubran las que quedan. Las dos cosas bajan el bullshit score. Las dos mejoran el día siguiente.
Mutation testing: cuando el test miente
El coverage responde a una pregunta simple: ¿se ejecutó esta línea? Un agente es extraordinariamente bueno generando tests que recorren código y no afirman casi nada. Suite en verde, 92% de cobertura, palmada en la espalda y a casa. Pero en una línea ya cubierta había un comparador > que debería ser >=. ¿Cómo es que no se detectó?
El mutation testing hace una pregunta incómoda: si el código estuviera mal, ¿lo pillaría la suite? Para ello, el motor introduce mutants: cambia un + por un -, invierte un if, relaja o endurece un comparador, elimina una guarda. Luego ejecuta los tests de nuevo.
- Si fallan, el mutante muere. La suite detectó el defecto de comportamiento. Todo bien.
- Si pasan, el mutante sobrevive. La suite solo probaba el código, no el comportamiento. Mal.
Esta idea es vieja, pero era poco práctica por tediosa y costosa. Ahora es más fácil (hay muchas herramientas) y rentable (los agentes matan a los mutantes escribiendo más tests, y rápido).
Herramientas que ya puedes enchufar:
npx stryker run # JS/TS
dotnet stryker # .NET
pitest # Java
Back-pressure: el sistema también frena
Todos estos obstáculos suponen un coste en tiempo y recursos en el ciclo de desarrollo.
Cuando el volumen de generación supera la capacidad de verificación, el sistema tiene que frenar. Eso se llama contrapresión ( en inglés, back-pressure): tomado de la ingeniería de fluidos, como la resistencia que se opone al flujo.
Compilador, tests, políticas de seguridad, CI, CRAP, mutación: cada uno es un punto de fricción. Si quieres mantener o aumentar el flujo, tus opciones son pocas:
- Escala el sistema de verificación (más workers, mutación diferencial solo sobre el diff, gates en paralelo).
- Reduce la velocidad del agente (tareas más pequeñas, menos ficheros por ciclo, menos autonomía).
- Baja el nivel de calidad exigido (prototipo, MVP, demo… pero con fecha fin o riesgo asumible).
Lo que no escala es generar un pull request de mil líneas y decir… “ya lo miraré luego”.
Conclusión
Ahora, la atención humana es el recurso más escaso. Gastarla en el diff de cada función es desperdiciarla. El revisor del futuro se reserva lo que los gates todavía no cuantifican bien: la seguridad de negocio, la claridad de la especificación, las decisiones de producto.
La calidad a escala industrial depende de un plan de constraints bien diseñado. Los agentes pueden generar código a una velocidad que ningún revisor humano aguanta. Solo un circuito con vallas —tests, estructura, CRAP, mutación— garantiza que el código que las supere sea correcto, seguro y mantenible antes de ejecutarlo.
No estamos pidiendo más disciplina al modelo ni saturándolo con reglas y prompts gigantes. Se trata de incrustar la disciplina en el sistema de desarrollo, con principios de ingeniería de software.
El oficio que gana valor es el de ingeniero, no el de programador.
Estoy incorporando todas estas ideas en AIDDbot, un arnés de skills para agentes. Es open source, así que puedes ver cómo controlo a mis agentes y, mejor aún, ayudarme a incrementar la calidad de su desarrollo.