Los tests superan las instrucciones para agentes IA

15 min de lectura

Las mejores reglas para agentes IA son las que rompen el build

17.04.2026, Por Stephan Schwab

Todos quieren que los agentes de código IA sigan las reglas. Entonces escriben largos archivos Markdown llenos de instrucciones, convenciones y advertencias. Los agentes los leen, los entienden más o menos, y luego se desvían de todas formas. Hay un enfoque mejor, y existe desde hace más de seis décadas: el desarrollo guiado por tests. Los tests son restricciones ejecutables que fallan instantáneamente cuando un agente se sale del camino. Sin interpretación. Sin cumplimiento probabilístico. Rojo o verde.

Los tests superan las instrucciones para agentes IA

La carrera armamentista de las instrucciones Markdown

"No se puede salir del no-determinismo a base de instrucciones."

Cada herramienta de código IA tiene ahora su propio formato de instrucciones. Cursor tiene .cursorrules. GitHub Copilot tiene copilot-instructions.md. Windsurf tiene sus propias convenciones. La idea es la misma en todas partes: escribe las reglas de tu proyecto en prosa, y el agente las seguirá.

Los desarrolladores invierten horas en estos archivos. “Siempre usar funciones factory, nunca constructores.” “Preferir composición sobre herencia.” “Nunca modificar el esquema de base de datos sin una migración.” “Usar nuestro patrón personalizado de manejo de errores de src/errors/.” Los archivos crecen. Se convierten en guías de estilo internas, documentos de arquitectura y listas de deseos, todo en uno.

Y los agentes se desvían de todas formas.

No siempre. No inmediatamente. Pero eventualmente. La IA produce código que técnicamente satisface la letra de una instrucción mientras viola el espíritu de otras tres. Inventa una nueva función auxiliar en lugar de usar la existente. Refactoriza un módulo que funcionaba perfectamente porque “notó” una oportunidad de mejora. Cambia la firma de una interfaz porque el nuevo enfoque parecía más limpio.

Esto no es un error del modelo. Esto es lo que significa no-determinismo. Los LLM procesan instrucciones probabilísticamente. Cada token es una elección ponderada. “Siempre usar funciones factory” no se convierte en una restricción dura en el razonamiento del modelo. Se convierte en un empujón, una señal entre miles compitiendo por atención durante la generación. A veces el empujón gana. A veces pierde contra un patrón que el modelo vio con más frecuencia en los datos de entrenamiento.

Entonces los desarrolladores reaccionan agregando más instrucciones. Más específicas. “Al crear una nueva clase de servicio, SIEMPRE revisar src/services/ primero para patrones existentes.” “NUNCA renombrar métodos públicos existentes.” “Si no estás seguro de la arquitectura, PREGUNTA.” El archivo crece a 500 líneas. Luego 1.000. El agente ahora tiene tantas directivas competidoras que no puede satisfacerlas todas consistentemente. Las instrucciones se contradicen en los bordes, como toda prosa suficientemente detallada se contradice a sí misma.

Por qué la prosa no puede restringir una máquina de probabilidades

"El lenguaje natural es ambiguo por diseño. Es una ventaja para humanos y un desastre para restricciones."

El problema fundamental: se está usando el medio de comunicación más ambiguo (lenguaje natural) para restringir un sistema que opera con distribuciones de probabilidad. Cada oración tiene múltiples interpretaciones válidas. “Preferir composición sobre herencia” suena claro hasta que el agente encuentra un caso donde la herencia genuinamente simplifica el código. ¿Qué significa “preferir” entonces? ¿El 70 % de las veces? ¿90 %? ¿Siempre excepto cuando el agente decide lo contrario?

Las instrucciones en Markdown son esperanzas. Deseos expresados en lenguaje humano, alimentados a un sistema que no razona como un colega humano. Un colega humano lee “preferir composición” y construye un modelo mental de la arquitectura, la historia del equipo, las decisiones de diseño anteriores, el contexto específico del código. Un LLM lo lee y ajusta probabilidades de tokens.

Cuantas más instrucciones se agregan, más se juega un juego que no se puede ganar. Se intenta enumerar cada posible situación que un agente podría encontrar y preespecificar el comportamiento correcto. Es la misma trampa en la que cayó BDUF (Big Design Up Front) hace décadas. El mundo es demasiado complejo para especificarlo completamente por adelantado. Cada nueva instrucción crea nuevos casos límite donde las instrucciones se contradicen.

Los tests no piden cumplimiento. Lo exigen.

"A un test fallido no le importa el razonamiento del agente. Le importa el resultado."

Ahora consideremos qué pasa cuando existe una suite de tests completa.

El agente IA escribe código. Los tests se ejecutan. Pasan o fallan. No hay interpretación. No hay cumplimiento probabilístico. No hay “seguí el espíritu de la instrucción.” El build está rojo o verde.

Cuando el agente inventa un nuevo patrón en lugar de usar el existente, los tests de integración detectan la inconsistencia. Cuando refactoriza una interfaz pública, los tests de contrato se rompen. Cuando cambia el comportamiento de la base de datos, los tests de integridad de datos fallan. Cuando altera la lógica de negocio, los tests de especificación gritan.

Los tests son especificaciones que se ejecutan. Definen lo que el sistema hace, y lo verifican continuamente. Un agente IA no puede convencer a una aserción fallida con palabras bonitas.

Por eso el desarrollo guiado por tests resulta ser la herramienta de gobernanza más efectiva para agentes de código IA en 2026. La práctica se remonta a 1957, cuando Daniel D. McCracken la describió en Digital Computer Programming: preparar primero la salida esperada, luego escribir código hasta que la salida real coincida. Kent Beck la redescubrió y formalizó en 2003. Ninguno de los dos pensaba en LLMs. Pero TDD produce exactamente el artefacto que restringe generadores de código no deterministas: una especificación ejecutable, densa, del comportamiento previsto.

La ventaja de las especificaciones ejecutables

"Tu suite de tests es el único archivo de instrucciones que la IA no puede malinterpretar."

Pensemos en qué representa realmente una suite de tests bien mantenida. Es una descripción precisa, inequívoca y verificable por máquina de lo que el software debe hacer. No cómo debe verse. No qué patrones debe seguir. Qué debe hacer.

Un test unitario que dice expect(calculateTax(100, 'DE')).toBe(19) es una especificación. Dice: para un monto de 100 en Alemania, el impuesto es 19. Ningún LLM puede malinterpretar eso. Ninguna selección probabilística de tokens puede hacer que 19 sea igual a 21. El test pasa o falla.

Multiplica eso por cientos o miles de tests. Se obtiene una especificación tan densa y precisa que el agente IA opera dentro de un corredor. Puede ser creativo en cómo implementa algo. Puede elegir diferentes nombres de variables, diferentes estructuras de control de flujo, diferentes patrones internos. Pero no puede cambiar qué hace el código sin disparar fallos inmediatamente.

La mayoría pasa por alto este punto: los tests no solo detectan errores después del hecho. Los agentes de código modernos leen los tests antes de escribir una sola línea de implementación. La suite de tests es contexto. Cuando un agente ve esto:

expect(createUser({role: 'admin'})).toHavePermission('delete')

No solo sabe qué validar. Sabe qué construir. Los tests se convierten en la especificación más clara e inequívoca que el agente puede encontrar en la base de código. Las instrucciones en prosa compiten con datos de entrenamiento y ruido de la ventana de contexto. Un test con entradas concretas y salidas esperadas corta a través de todo eso. El agente lo lee, entiende el contrato, y genera código para satisfacerlo. Los tests son simultáneamente el plano y el inspector.

Esa es la verdadera gobernanza que se necesita. No importa si el agente usa una función factory o un constructor, siempre que el comportamiento sea correcto. No importa si reestructura módulos internos, siempre que todos los contratos se mantengan. Los tests gobiernan resultados, no estilo. Y los resultados son lo que importa.

Compárese con un archivo de instrucciones Markdown. “Siempre usar funciones factory” gobierna estilo, no resultados. Restringe lo equivocado. La IA podría seguirlo perfectamente mientras produce código que destruye la lógica de negocio, y no se sabrá hasta producción.

La persecución interminable de los vibe coders

"Refinan instrucciones sin parar porque no tienen tests. Y no tienen tests porque nunca aprendieron TDD."

Así se ve en la práctica. Alguien sin experiencia en TDD empieza a usar un agente de código IA. El agente produce código que funciona para los primeros prompts. Luego empieza a desviarse. Cambia patrones. Renombra cosas. Inventa abstracciones. El desarrollador nota la desviación y hace lo único que conoce: escribir más instrucciones.

“No renombrar funciones existentes.” Agregado. El agente deja de renombrar pero empieza a envolver funciones en adaptadores innecesarios. “No agregar capas de adaptadores sin preguntar.” Agregado. El agente ahora pregunta antes de cada cambio, lo que ralentiza todo hasta ser inservible. “Solo preguntar para cambios arquitectónicos.” Agregado. El agente interpreta “arquitectónico” de manera diferente al desarrollador. Más desviación.

Esta es la rueda de hámster del vibe coder. Cada instrucción es un parche para el fallo de la instrucción anterior. El archivo de instrucciones se convierte en una pila creciente de casos especiales, contradicciones y advertencias en MAYÚSCULAS cada vez más desesperadas. Algunos desarrolladores terminan con archivos que se leen como contratos legales, llenos de “DEBE”, “NO DEBE”, “BAJO NINGUNA CIRCUNSTANCIA”, como si amenazar a un LLM con lenguaje contractual lo hiciera cumplir.

No lo hará. El modelo no entiende amenaza ni obligación. Procesa texto y predice tokens. Las mayúsculas podrían aumentar ligeramente la probabilidad de cumplimiento, del mismo modo que escribir “IMPORTANTE” en un correo aumenta ligeramente la probabilidad de que alguien lo lea. Ligeramente.

Mientras tanto, un desarrollador que practica TDD trabaja de otra manera. En la práctica, el agente escribe test e implementación juntos. Eso está bien. La velocidad importa. Pero aquí está la diferencia crítica: esos tests existen antes del siguiente cambio. Cuando el agente toca ese código de nuevo, los tests de la ronda anterior actúan como barandillas. Definen lo que no debe romperse. ¿El agente se desvía? El test falla. Inmediatamente. Sin archivo de instrucciones. Sin negociación con una máquina de probabilidades.

El rol del humano cambia. Ya no se escribe cada test a mano. Se revisan los tests que el agente escribió, se verifica que capturen el comportamiento correcto, y se ajustan donde están demasiado flojos. Luego se sigue adelante. La próxima vez que el agente modifique ese módulo, chocará contra un muro de especificaciones que no puede ignorar. Cada ronda de desarrollo deja restricciones para la siguiente. La suite de tests crece hasta convertirse en una red de seguridad cada vez más densa, construida colaborativamente entre humano y máquina.

Y aquí hay algo que hace este flujo de trabajo aún más poderoso: no hay que leer cada test y cada línea de código personalmente. Se puede preguntar al agente. “¿Algún test verifica que los tokens expirados son rechazados?” “¿Qué pasa si el monto del pago es cero?” “Muéstrame qué tests cubren el flujo de eliminación de usuarios.” Se interroga la base de código a través de conversación, verificando supuestos sin rastrear manualmente los archivos. Es como tener un testigo en el estrado que ha leído cada línea y puede responder al instante. Los tests se convierten en documentación consultable, y el agente se convierte en la interfaz hacia ella.

Pero hacer las preguntas correctas es donde la experiencia real importa. Alguien que ha construido sistemas, los ha puesto en producción y los ha visto fallar a las 3 de la mañana, que ha lidiado con condiciones de carrera, corrupción de datos y timeouts en cascada, sabe qué investigar. Pregunta por casos límite porque ha sido quemado por casos límite. Pregunta por limpieza en caso de fallo porque ha depurado recursos huérfanos. Pregunta por concurrencia porque ha visto qué pasa cuando dos solicitudes golpean la misma fila. El agente puede responder cualquier pregunta que se le lance. No puede decir qué preguntas se están olvidando. Esa es la brecha entre un desarrollador y alguien que acaba de aprender a escribir prompts.

¿Y el estilo? ¿Y la arquitectura?

"Los linters imponen estilo. Los tests imponen comportamiento. Las instrucciones no imponen nada."

La objeción razonable es: los tests no gobiernan todo. No imponen estilo de código. No impiden que el agente use tabulaciones en lugar de espacios, que escriba one-liners demasiado ingeniosos o que elija nombres de variables desafortunados.

Cierto. Pero hay herramientas que sí lo hacen. Linters, formateadores, análisis estático. Ruff, ESLint, Prettier, Checkstyle, lo que corresponda al stack. Estas herramientas imponen estilo de manera determinista. Se ejecutan, marcan violaciones, el build falla. Igual que los tests.

¿Arquitectura? Si la arquitectura importa, hay que expresarla en tests. Escribir tests que verifiquen límites de módulos. Tests que comprueben direcciones de dependencia. Tests que aseguren que ciertos paquetes no importen de ciertos otros. ArchUnit para Java hace exactamente esto. Otros lenguajes tienen equivalentes. Si una regla arquitectónica solo existe en un archivo Markdown, es una sugerencia. Si existe en un test, es una restricción.

El patrón es siempre el mismo: las cosas que se ejecutan y fallan son restricciones. Las cosas que están en un archivo esperando ser leídas son sugerencias. Los agentes IA son malos siguiendo sugerencias consistentemente. Son muy buenos haciendo que los tests pasen.

La paradoja: prácticas antiguas como herramientas modernas

"TDD no fue diseñado para IA. Simplemente resulta que resuelve exactamente el problema correcto."

Hay una ironía que vale la pena considerar. La tecnología más promocionada de la década, la programación asistida por IA, se gobierna mejor con una práctica más antigua que la mayoría de los lenguajes de programación. McCracken describió la programación guiada por tests el mismo año en que apareció Fortran. Kent Beck la redescubrió cuatro décadas después. La idea central nunca cambió: definir cómo se ve “correcto” antes de escribir el código. Esa disciplina da pasos pequeños, retroalimentación rápida y comportamiento verificado. Resulta que esas propiedades exactas son lo que se necesita cuando un agente no determinista escribe el código.

Pasos pequeños significan que el agente no puede desviarse mucho antes de chocar con un límite de test. Retroalimentación rápida significa que la desviación se detecta en segundos, no en días. Comportamiento verificado significa que hay un juez objetivo y automatizado sobre si la salida del agente es aceptable.

Vibe coding omite todo esto. El vibe coder envía un prompt, el agente genera, el desarrollador revisa la salida a ojo y la sube a producción si “se ve bien.” Funciona para prototipos desechables. Falla catastróficamente en cualquier cosa que necesite funcionar de manera confiable, mantenerse en el tiempo o ser modificada por alguien diferente al autor del prompt original.

Por eso la IA no está reemplazando desarrolladores. Está reemplazando la ilusión de que se podía salir adelante sin entender realmente lo que se construye. Quienes temían el reemplazo eran a menudo los que no podían articular qué debía hacer su código más allá de “funciona en mi máquina.” La IA no creó ese problema. Lo expuso. Un desarrollador que sabe escribir tests, hacer las preguntas correctas y detectar debilidades estructurales usará la IA para moverse tres veces más rápido. Un desarrollador que se apoyaba en copiar-pegar y Stack Overflow descubrirá que la IA puede hacer esa parte también, y más barato. La amenaza no es la inteligencia artificial. La amenaza es haber entrado a la profesión por el salario en vez del oficio. Buenos desarrolladores con IA superarán a equipos mediocres sin ella. No es una predicción. Ya está sucediendo.

El vibe coder que descubre que su agente sigue desviándose tiene dos opciones. Aprender TDD. O seguir escribiendo instrucciones que nunca serán suficientes. La mayoría elige la segunda opción porque se siente como progreso. El archivo se hace más largo. El agente sigue desviándose. Pero al menos están haciendo algo.

Cómo empezar

Si se usan agentes de código IA y no se tiene una suite de tests, hay que empezar a construir una. No como algo secundario. Antes de enviar el primer prompt al agente.

Escribe un test que falle y que describa lo que quieres. Deja que el agente proponga una implementación. Ejecuta el test. Si pasa, escribe el siguiente test. Si falla, la propuesta del agente estaba equivocada. Díselo. Deja que lo intente de nuevo, con el test fallido como especificación.

Este flujo de trabajo es más rápido que escribir instrucciones detalladas, porque se le da al agente un criterio de éxito inequívoco en lugar de esperar que interprete la prosa correctamente. Y cada test que se escribe permanece. Se convierte en parte de la especificación permanente. La próxima vez que el agente toque ese código, el test previene la regresión. ¿El archivo de instrucciones? El agente lo olvida en el momento en que la ventana de contexto se llena con otra cosa.

Para quienes ya practican TDD, ya tienen la respuesta. Sigan haciendo lo que hacen. Su suite de tests es la mejor herramienta de gobernanza IA que el dinero no puede comprar. Los agentes mejorarán. Los modelos se volverán más inteligentes. Pero seguirán siendo máquinas de probabilidad no deterministas, y siempre necesitarán restricciones duras. Los tests son esas restricciones.

Un punto de partida para el archivo de instrucciones

"Las instrucciones, cortas. Que los tests hablen."

Nada de esto significa que los archivos de instrucciones sean inútiles. Solo no deberían intentar hacer el trabajo que tests y herramientas hacen mejor. Un archivo de instrucciones corto que establezca cómo debe trabajar el agente supera a uno largo que intente especificar cada decisión de código. Aquí un punto de partida. Ponerlo en copilot-instructions.md, claude.md, o lo que el editor espere.

## Flujo de trabajo

Seguir desarrollo guiado por tests estrictamente:
1. Escribir primero un test que falle
2. Implementar el código mínimo para que pase
3. Refactorizar manteniendo todos los tests en verde
4. Nunca escribir código de producción sin test correspondiente

Ejecutar la suite completa de tests después de cada cambio.
Una tarea no está completa hasta que todos los tests pasen.

## Principios de código

- No repetirse. Extraer lógica compartida en funciones
  o módulos. Si hay duplicación, eliminarla.
- Funciones cortas. Una responsabilidad por función.
- Preferir composición sobre herencia.
- Sin código muerto. Si no tiene test y no se llama, borrarlo.

## Lo que no se debe hacer

- Nunca modificar o eliminar un test existente para que
  la implementación funcione. Si un test falla, el código
  está mal.
- Nunca saltarse tests para "arreglar después."
- Nunca agregar dependencias sin verificar que ya se usan
  en el proyecto.

## Estilo y formato

Seguir la configuración del linter y formateador del proyecto.
No sobreescribirla.

Son aproximadamente 30 líneas. Le dicen al agente cómo trabajar, no qué construir. El “qué” vive en los tests. Y a diferencia de un archivo de 500 líneas lleno de edictos de arquitectura contradictorios, este es lo suficientemente corto para que el agente lo mantenga en contexto.

¿Son suficientes 30 líneas? Probarlo. Agregar reglas solo cuando el agente haga repetidamente algo que los tests y linters no puedan detectar. La mayoría de las veces, no hará falta.

El resto son vibes.

Contacto

Hablemos de tu situación real. ¿Quieres acelerar la entrega, quitar bloqueos técnicos o validar si una idea merece más inversión? Escucho tu contexto y te doy 1-2 recomendaciones prácticas. Sin compromiso ni discurso comercial. Confidencial y directo.

Empecemos a trabajar juntos

Newsletter: Sin teatro metodológico. Sin relleno.
Ideas reales sobre entrega de software y liderazgo.

×