Hace unas semanas aprobé un pull request generado por un agente que arreglaba el cálculo de un descuento por antigüedad. Para verificar código generado por IA hice lo que hacemos casi todos: los tests pasaban en verde, el type checker no se quejó, el diff tenía 40 líneas legibles con nombres razonables.
Le di merge.
Dos días después, un usuario premium con 14 meses de antigüedad reportó un descuento del 10% en vez del 20%. El código funcionaba. Hacía exactamente lo que decía que hacía — solo que eso no era lo que yo había pedido. La condición antiguedad > 12 estaba invertida en un if, y ni el compilador ni los tests que el propio agente había escrito lo detectaron.
(Esta anécdota es ilustrativa del tipo de fallo que describo aquí — no es un incidente puntual verificable con fecha exacta, es el patrón que se repite.)
Esto no es una historia sobre un mal agente. Es sobre un mal proceso: mi "verificación" fue leer por encima y confiar en dos gates que no estaban hechos para atrapar ese error. Leer por encima no escala cuando el agente te entrega cinco PRs al día.
En corto: verificar código generado por IA sin leer cada línea es posible con cuatro filtros baratos: deja que el type checker haga de primer gate, pide los tests desde la especificación antes de enseñarle el código al agente, usa un segundo agente con un prompt distinto como revisor, y concentra tu lectura manual en las líneas que tocan auth, dinero o validación de input. Ninguno sustituye un sistema completo — son parches rápidos que aplicas hoy, sin instalar nada.
¿Qué es un gate de verificación (y por qué no es lo mismo que "revisar")?
Un gate de verificación es un chequeo binario que pasa o falla sin que tengas que interpretarlo — no depende de tu criterio ni de tu concentración a las 11 de la noche. "Revisar" es leer código y juzgar si parece correcto. Un gate ejecuta algo que responde sí o no, con evidencia. "Leí el diff y se veía bien" no es un gate, es una opinión — y las opiniones fallan justo cuando el código está bien escrito y hace lo contrario de lo que pide la spec.
Eso importa porque el código de un agente está optimizado para parecer correcto: nombres claros, formato limpio, estructura familiar. Tu ojo entrenado para detectar "código feo" falla exactamente cuando el código es bonito y está mal.
El problema real: el código pasa la vista y falla en producción
No soy el único con esta fricción. En un hilo de Hacker News con 298 comentarios titulado "When AI writes the software, who verifies it?", el usuario roadbuster lo describe así (traducido del inglés):
"El LLM genera felizmente tests que simplemente refuerzan el comportamiento existente del código. En ningún momento nadie se detiene a preguntar si el código generado implementa el comportamiento funcional deseado."
Es lo que me pasó con el descuento: el agente escribió el código y luego tests que confirmaban que ese código hacía lo que hacía — no lo que yo había pedido. El test nunca falla porque nació del mismo malentendido que la implementación.
En el mismo hilo, Karrot_Kream admite el punto incómodo: sigue auditando a mano los tests que genera la IA, aunque reconoce que es "la parte de programar que menos le gusta". Nadie quiere hacer esta parte. Por eso casi nadie la hace bien.
4 técnicas que puedes aplicar hoy, sin instalar nada nuevo
Ninguna requiere adoptar un framework, escribir un AGENTS.md o cambiar tu flujo. Son filtros que añades a lo que ya haces.
| Técnica | Qué detecta | Qué NO detecta | Esfuerzo |
|---|---|---|---|
| Type checker en modo estricto | Tipos incompatibles, propiedades inexistentes, APIs alucinadas, nulls sin manejar | Lógica de negocio incorrecta que tipa perfectamente bien | Ninguno extra — actívalo en strict |
| Tests desde la spec, antes de ver el código | Que el comportamiento coincida con lo pedido, no con lo que el agente decidió escribir | Casos que la spec no contempló; spec vaga produce tests vagos | Bajo — un prompt aparte, antes de la implementación |
| Segunda pasada con otro agente/prompt como revisor | Inconsistencias entre spec y código, casos límite obvios, errores sin manejar | Puede repetir el sesgo del primero si comparte el mismo chat | Medio — exige prompt adversarial, en sesión nueva |
| Diffing dirigido a zonas críticas (auth, dinero, validación) | Regresiones graves justo donde más caro sale que fallen | Todo lo que quede fuera del filtro — no es lectura completa | Bajo — un comando de git, pero exige definir bien qué es "crítico" |
1. Deja que el type checker sea tu primer filtro
TypeScript, mypy o el compilador que uses no mienten ni se cansan. Si el agente alucina una propiedad, el gate lo para antes de tu revisión:
function getDiscount(user: User): number {
if (user.subscription.tier === 'premium') {
return user.subscription.discountRate; // no existe en el tipo
}
return 0;
}
error TS2339: Property 'discountRate' does not exist on type 'Subscription'.
Esto no habría parado mi bug del descuento invertido — el tipo estaba bien, la lógica no. Pero sí para buena parte de las alucinaciones típicas: APIs inventadas, campos que no existen, nulls sin manejar. Es gratis y ya lo tienes. Actívalo en modo estricto si no lo has hecho.
2. Pide los tests desde la spec, antes de enseñarle el código
Esta es la técnica que me habría salvado del bug del descuento. En vez de pedir código y luego tests, invierte el orden:
Esta es la especificación (no hay código todavía):
"calcularDescuento(usuario) devuelve 20% si el usuario es premium
y lleva más de 12 meses activo, 10% si es premium con menos de
12 meses, y 0% en cualquier otro caso."
Escribe los tests de aceptación en Vitest. Todavía no has visto
ninguna implementación.
Cuando el agente escribe primero el código y luego "sus" tests, el test hereda cualquier malentendido de la spec. Cuando nace de la spec en una pasada separada, se convierte en un juez independiente que detecta el error porque no lo comparte.
3. Usa un segundo agente con un prompt distinto como revisor
No el mismo chat. Una sesión nueva, sin el contexto de cómo se escribió el código, con un prompt deliberadamente escéptico:
Eres un revisor senior, escéptico por defecto. No escribiste este código
y no asumes que está bien solo porque compila y pasa los tests actuales.
Busca: casos límite no cubiertos, lógica que no coincide con la
especificación, manejo de errores ausente, datos sensibles sin validar.
Especificación: [pegar]
Código: [pegar diff]
No digas "se ve bien". Señala línea y motivo, o di qué revisaste
y por qué no encontraste problema ahí.
Este patrón de usar un agente distinto para tareas que exigen otro punto de vista es parte de lo que enseño paso a paso en Construye con IA: montar flujos con varios agentes que se corrigen entre sí, no uno solo que se audita a sí mismo.
4. Diffing dirigido: lee solo lo que puede doler de verdad
No leas las 340 líneas del PR. Filtra por lo que toca zonas donde un error sale caro:
git diff --stat main...feature/discount-calc
git diff main...feature/discount-calc -- '**/auth/**' '**/payment/**' '**/*valida*' '**/*schema*'
El criterio de qué es "crítico" no es intuición — es cuánto cuesta que falle y qué tan rápido te enteras.
Mi criterio, sin "depende"
El type checker en estricto y el diffing dirigido van siempre, en cualquier PR — son gratis. Los tests desde la spec los reservo para lógica de negocio real, no para un CRUD trivial. El segundo agente como revisor solo cuando ya tengo un flujo agéntico corriendo — si no, revisarlo yo mismo es más rápido.
Si solo añades una cosa hoy, que sea el type checker en strict más el diffing dirigido: eliminan la categoría de bugs más tonta sin coste de configuración extra.
Lo que estas técnicas no te van a coger
Sé honesto contigo mismo, porque yo no lo fui con el descuento: estos cuatro filtros no son un sistema, son parches.
No dejan rastro. Dentro de tres meses no hay ningún documento que diga qué se verificó, con qué criterio, y quién lo aprobó — repites el proceso de memoria, y la memoria falla en el PR número 40 de la semana.
Dependen de que definas bien qué es "crítico" o qué pide realmente la spec. Si tu filtro de diffing no incluye la carpeta correcta, o tu spec es ambigua, el agujero sigue ahí y nadie te avisa — el chequeo "pasó" porque nunca miró donde tenía que mirar.
Y el segundo agente como revisor puede convertirse en un espejo del primero si comparte contexto o sesgo. Un revisor que piensa igual que quien escribió el código no es un revisor — es una segunda opinión de la misma persona.
Si tu proyecto mueve dinero real, datos de usuarios o decisiones irreversibles, estas cuatro técnicas son el piso mínimo, no el techo. El post Revisar código generado por IA: el método Revisión por Contrato explica el sistema completo que sí deja rastro: qué se construye, por dónde no puede salirse el agente, y quién dice que está bien, con un AGENTS.md real. Si además quieres el sistema montado con harness de verificación end-to-end, el workshop SDD + Agentic Engineering cubre eso paso a paso.
Qué puedes hacer hoy
Abre tu tsconfig.json o tu mypy.ini ahora mismo y confirma que estás en modo estricto. Es la línea más barata que vas a escribir esta semana.
Después, la próxima vez que le pidas código a un agente, invierte el orden: pide primero los tests desde la especificación, en una pasada separada, antes de pedir la implementación. Esa sola inversión habría parado mi bug del descuento.
Estas cuatro técnicas son la entrada. Cuando el proyecto crezca lo suficiente como para que un parche ya no alcance, el siguiente paso es un sistema real de verificación — contrato, carril y veredicto — que dejo completo, con ejemplos y el AGENTS.md entero, en Revisar código generado por IA: el método Revisión por Contrato. Y si quieres ver cómo aplico esto semana a semana en proyectos reales, en Dominicode Labs comparto el detalle con la comunidad.
Preguntas frecuentes
¿El type checker es suficiente para confiar en código generado por IA?
No. Detecta que las piezas encajan en forma — tipos correctos, propiedades que existen, nulls manejados. No detecta que la lógica de negocio sea la que pediste. Mi bug del descuento invertido tipaba perfectamente bien: es un filtro necesario y gratuito, no una prueba de corrección.
¿Qué diferencia hay entre pedir tests antes o después de ver la implementación?
Cuando el mismo agente escribe primero el código y luego los tests, estos heredan cualquier malentendido de la spec, porque nacen de la misma lectura equivocada. Generados en una pasada separada, se convierten en un juez independiente que detecta el error porque no lo comparte.
¿Puedo usar el mismo agente que escribió el código para revisarlo después?
Puedes, pero pierdes gran parte del valor: en el mismo chat, con el mismo contexto, el agente tiende a justificar sus decisiones en vez de cuestionarlas. Usa una sesión nueva y un prompt explícitamente escéptico para que la segunda pasada aporte un punto de vista distinto, no un eco del primero.
¿Estas técnicas sirven si mi proyecto no tiene tests todavía?
El type checker y el diffing dirigido sí, sin cambios — no dependen de una suite existente. Los tests desde la spec además te dan una forma barata de empezar a construirla: cada vez que le pides código a un agente, generas primero el test de aceptación, y en unos meses tienes cobertura real sin haber dedicado un sprint a escribirla.
¿Cuándo necesito algo más que estas 4 técnicas?
Cuando el error de un agente puede costarte dinero real, datos de usuarios, o algo que no se deshace con un revert. Ahí un parche puntual no basta — necesitas un sistema que deje rastro de qué se verificó y con qué criterio, que es justo lo que cubre el método de Revisión por Contrato.
Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

Leave a Reply