La velocidad de la AI sin una pila de seguridad termina en fragilidad

Lo peligroso del código generado con AI no es que siempre esté mal.

Lo peligroso es que a menudo parece lo bastante correcto como para hacer merge.

Ahí está el riesgo. El código obviamente roto se detecta. El código que parece plausible, pasa un par de tests felices y al mismo tiempo debilita una boundary importante es el que llega a producción.

Si tu workflow es solo prompt, paste, review y merge, cada aumento de velocidad en la generación agranda la distancia entre lo rápido que cambias el sistema y lo bien que puedes confiar en él.

La solución no es más heroicidad en code review. La solución es una pila de seguridad por capas que capture distintos fallos en distintos momentos del ciclo.

Capa 1: Prevention con types, schemas y contracts

El defecto más barato es el que el programa ni siquiera puede representar.

Por eso la primera capa es prevention.

Aquí estrechas la surface area antes de que el comportamiento llegue al runtime:

  • Branded types y phantom types evitan mezclar valores estructuralmente parecidos pero semánticamente distintos.
  • Runtime schemas como Zod protegen las fronteras donde entran datos no tipados.
  • Contracts definen preconditions, postconditions e invariants alrededor de los caminos críticos.

Esto importa todavía más con AI-generated code, porque los modelos son buenos produciendo implementaciones que parecen coherentes aunque cometan category mistakes. Si tus types y schemas son débiles, el modelo tiene demasiado espacio para quedar “más o menos bien”.

Capa 2: Verification con property-based tests

Los example tests sirven, pero son demasiado fáciles de overfittear tanto para humanos como para modelos.

Un modelo puede generar una función y también su happy-path test correspondiente. En el pull request eso parece productividad. En realidad, el comportamiento sigue infraespecificado.

Property-based testing cambia la pregunta. En vez de preguntar si la función funciona para tres ejemplos, preguntas qué debe seguir siendo cierto para clases completas de inputs.

Los mejores puntos de entrada suelen ser:

  • round trips
  • idempotencia
  • invariantes de orden
  • monotonicidad
  • comportamiento correcto ante invalid input

Aquí la AI ayuda bastante. Los modelos suelen proponer un primer borrador razonable de properties a partir de una firma o un doc comment. El humano sigue revisando, pero el problema de la página en blanco se reduce mucho.

Capa 3: Assessment con mutation testing

Coverage no es una métrica de calidad. Es una métrica de ejecución.

Mutation testing hace la pregunta que de verdad importa: si el código cambiara de una forma defectuosa pero plausible, ¿tus tests lo notarían?

Por eso mutation testing va por encima de contracts y property tests. No los reemplaza. Mide si realmente están haciendo trabajo útil.

Esto es especialmente importante con suites de tests generadas por AI. Los modelos pueden generar tests que parecen sólidos, ejecutan muchas líneas y sin embargo validan muy poco. Mutation testing expone esa falsa confianza con rapidez.

La forma práctica no es correr full mutation analysis en todo, todo el tiempo. La forma práctica es:

  • empezar por módulos críticos
  • usar incremental mutation testing sobre código cambiado
  • hacer triage agresivo de survivors
  • subir thresholds a medida que madura la suite

En la era de la AI, mutation testing es el antídoto contra la confianza falsa.

Capa 4: Runtime containment y recovery

Aun con una buena pila de verificación, algo se escapará.

Por eso la capa externa es runtime containment.

Aquí importan crash-only design, deadlines, circuit breakers, leases y capability-based boundaries. Cuando algo se escapa, el sistema debería fallar de forma controlada en lugar de convertir un camino malo en un incidente en cascada.

Para muchos equipos, esta capa empieza pequeña:

  • timeouts explícitos en llamadas externas
  • idempotency keys en endpoints de mutación
  • circuit breakers delante de dependencias inestables
  • capability surfaces estrechas para operaciones sensibles

La meta no es perfección. La meta es blast radius acotado.

Por qué las capas funcionan mejor juntas

Cada capa captura una clase distinta de fallo.

Types y contracts previenen estados inválidos obvios. Property-based tests verifican semántica. Mutation testing comprueba si los tests tienen dientes. Runtime containment maneja lo que logra escapar.

Esa es la idea central: no necesitas una técnica perfecta. Necesitas varias técnicas imperfectas que fallen de forma independiente.

Cómo sería un rollout práctico

La mayoría de los equipos no debería activarlo todo de golpe.

La secuencia más práctica suele ser:

  1. endurecer boundaries en TypeScript o Rust
  2. añadir runtime schemas en inputs externos
  3. introducir contracts en funciones críticas
  4. escribir property tests para serializers, reducers y validators
  5. activar incremental mutation testing en módulos de alto riesgo
  6. añadir runtime containment donde más fallan las dependencias

Esa secuencia funciona porque cada capa fortalece a la siguiente. Mejores schemas permiten mejores properties. Mejores properties mejoran mutation scores. El feedback de mutation testing muestra dónde siguen flojos los contracts o la profundidad de test.

El verdadero cambio en la ingeniería con AI

No ganarán los equipos que generen más código. Ganarán los equipos que puedan absorber más código generado sin bajar el nivel de confianza.

Eso es exactamente lo que resuelve esta pila de seguridad.

Convierte la AI de speed amplifier en reliability amplifier. El modelo ayuda a generar implementaciones, tests, contracts y rules. La pila se asegura de que esos artefactos sean comprobados por sistemas deterministas, en vez de ser aceptados solo porque suenan bien.

Si vas en serio con usar AI en producción, ese es el estándar. No otra checklist de review. No otro prompt diciendo “be careful”.

Sino un sistema por capas donde cada cambio generado tenga que sobrevivir prevention, verification, assessment y containment.

Así es como el código rápido se convierte en código confiable.