Cómo audito código heredado con SDD y subagentes
En iadev conté cómo programo con IA con método: la spec primero, los tests en rojo después y el código al final. Todo el tutorial parte de una carpeta vacía. Pero la mayor parte del código que toco no empieza en una carpeta vacía: lo heredo. Son módulos de PrestaShop escritos por otras personas, con años de parches encima, y alguno escrito en parte con otra IA que ya no tiene a nadie detrás para explicar por qué hizo lo que hizo.
Este post cuenta cómo audito ese código y lo convierto en specs antes de tocar una línea. Es el mismo método de iadev con el orden cambiado: allí la spec describe lo que quiero construir; aquí, lo que el código ya hace.
Por qué no empezar arreglando
La tentación con el código heredado es abrir el fichero que falla y arreglarlo. Con un agente es todavía más tentador, porque lo hace en dos minutos. El problema es que no sabes qué más depende de ese comportamiento. En uno de los módulos que audité, otros módulos cargaban ficheros internos suyos, y el orden de los valores de una columna era, sin que nadie lo hubiera escrito, un contrato con código de fuera.
Sin contrato no hay forma de saber si un arreglo rompe algo, ni de delegarlo en un agente con garantías: el agente necesita saber qué no debe cambiar tanto como qué debe arreglar. Y un agente que arregla sin contrato hace exactamente lo mismo que hizo la IA anterior: decidir en silencio.
El método, paso a paso
1. Congelar un commit
Todo el análisis se hace sobre un commit concreto, que queda anotado en el documento. Los módulos que audito siguen vivos, y alguno recibe commits a diario. Si cada agente leyera un estado distinto, los hallazgos no cuadrarían entre sí. Y cuando cito una línea de código, el commit dice a qué versión se refiere. En los ficheros que cambian a diario, la guía que queda para después cita nombres de función en lugar de números de línea, porque las líneas se desplazan.
2. Repartir áreas entre subagentes de solo lectura
Divido el módulo en cuatro áreas que se puedan leer por separado. En uno de los módulos fueron estas: módulo y controlador; dominio y repositorios; emails y PDF; e interfaz Vue. Cada área va a un subagente que solo puede leer, y los cuatro trabajan en paralelo sobre el mismo commit.
Lo de solo lectura no es un detalle: es lo que permite lanzar cuatro agentes sobre código en uso sin miedo. Ninguno edita un fichero ni escribe en la base de datos, y las consultas son todas de lectura.
Lo que hace rendir el método viene después: cuando terminan, los mismos agentes escriben la spec de su área. Ya han leído cada fichero y conocen sus rincones, mientras que un agente nuevo tendría que volver a leerlo todo. Es una regla que uso también con código nuevo: si el trabajo siguiente cae sobre el mismo código, continúo el agente que ya tiene el contexto en vez de arrancar uno en frío.
3. El orquestador verifica
Los informes de los subagentes son buenos, pero son informes. La sesión principal, el orquestador, no se los cree a ciegas: verifica de primera mano los hallazgos críticos, leyendo el código o con consultas de solo lectura.
Y el documento final lo dice. Hay una lista de hallazgos «verificados por el orquestador», y el resto se marca como «procede del informe de un subagente». Es la misma regla del capítulo 8 de iadev: lo que entrega un agente se revisa contra la fuente, no contra su propio informe.
4. Specs de retrofit, con los defectos marcados
Lo que sale del análisis son specs de retrofit: describen lo que el código hace hoy, no lo que debería hacer. Si una función hace algo que no debería, la spec lo describe tal cual y lo marca con un ⚠ y el identificador del hallazgo.
Parece hacerlo al revés, pero tiene sentido. Una spec que describe el comportamiento deseado mezcla lo que hay con lo que habrá, y ya no sirve para saber qué rompe un cambio. Con la spec de retrofit, cada arreglo empieza cambiando la spec (se quita el ⚠ y se escribe el comportamiento nuevo) y sigue con el código. Es SDD normal, pero desde un punto de partida honesto.
5. Fases de arreglo con propiedad de ficheros
La spec de tareas cierra el análisis. Contiene los hallazgos priorizados, las decisiones que no puede tomar un agente (¿este comportamiento es intencionado?, ¿migramos los datos históricos o cambiamos el código?) y las fases de arreglo. Cada fase tiene una lista cerrada de ficheros que puede tocar, igual que con código nuevo, para que dos agentes nunca se pisen. La primera fase es siempre de contención: lo urgente, sin cambiar la lógica.
Los problemas de seguridad que aparecieron se trataron aparte, y no entran en este post.
Qué distingue una buena auditoría
Después de varias, creo que lo que hace útil un documento de auditoría no son los hallazgos, sino lo que los rodea.
- Verificado frente a reportado. Quien lea sabe qué afirmaciones ha comprobado alguien con sus propios ojos y cuáles vienen de un agente. Un hallazgo crítico sin verificar no se arregla: primero se reproduce.
- Lo que no se ha hecho. Cada documento termina con una lista explícita. En uno de ellos: no se ha tocado código, no se ha reproducido nada, no se ha mirado producción y no se ha ejecutado ningún test, porque los que había no arrancaban. Así se sabe hasta dónde llega cada afirmación y qué conviene reproducir antes de arreglar.
- Identificadores por área.
X-para lo transversal,MOD-para el módulo,DOM-para el dominio,PDF-para emails y PDF,FE-para el frontend. Son los mismos en el informe y en las specs: el⚠de la spec apunta al hallazgo, y el hallazgo a la fase que lo arregla. Cuando dos agentes encuentran lo mismo desde áreas distintas, se anota como equivalencia (por ejemplo,DOM-3 = FE-5) en lugar de contarlo dos veces.
Hay una cuarta regla, de seguridad, que vale para cualquier proyecto: las specs no llevan el detalle de las vulnerabilidades. Ese detalle va a documentación privada, nunca al repo; en la spec solo queda una frase neutra. Un informe de vulnerabilidades no debería viajar con el código, por si el código acaba donde no esperabas.
Lo que dejó la IA anterior
Uno de los módulos se había escrito en parte con otra IA, y el repo lo dejaba ver: un AGENTS.md, un documento de arquitectura, un flujo de specs, skills para agentes y tests generados. Sobre el papel, un proyecto preparado para trabajar con agentes. En la práctica:
- El
AGENTS.mdservía como mapa, pero tenía seis errores. Un agente que lo leyera partía de información falsa sobre el propio código. - El flujo de specs se usó para siete cambios pequeños en un solo día y se abandonó. Después vinieron más de 250 commits sin spec.
- Las skills eran genéricas y casi ninguna aplicaba: la de PrestaShop estaba pensada para una arquitectura que el módulo no usa.
- Los tests generados no arrancaban.
Ninguna de esas piezas era mala idea. El problema es que nadie las mantuvo, y una documentación para agentes desactualizada es peor que ninguna, porque el agente se la cree. La moraleja: la documentación de agentes también caduca, y hay que auditarla como el código. En ese módulo reescribí el AGENTS.md y el README a partir de las specs nuevas, y retiré el flujo abandonado para que no quedaran dos fuentes de verdad.
La lección del peso
El mejor ejemplo de por qué importa una buena spec lo encontré en ese mismo historial. Uno de los cambios del flujo abandonado pedía añadir un campo de peso a cada línea del documento y «persistir el peso por el flujo de guardado existente». No decía dónde.
La implementación hizo lo razonable con lo que tenía: buscó dónde había un peso que guardar y lo escribió en el catálogo de productos. Así que editar el peso en esa pantalla cambiaba el peso del producto para toda la tienda. Meses después hubo que deshabilitar el campo en la pantalla.
Nadie se equivocó al escribir el código: la spec no fijaba el modelo de datos, y el agente rellenó el hueco con la opción que tenía más a mano. Es el mismo problema con el que empieza iadev, el de los once agentes que deciden en silencio, pero con datos reales. Una spec que no fija el modelo de datos produce exactamente esto. Desde entonces, cualquier spec que añade un dato dice en qué tabla y columna vive y qué no debe tocar. En la guía del módulo quedó escrito como primer paso para añadir un campo: decidir antes dónde se guarda el dato.
El patrón se repite
He aplicado el mismo método a varios módulos heredados de PrestaShop. Todos producen documentos con la misma estructura: qué es el módulo, cómo está hecho, specs, hallazgos por gravedad con sus identificadores, fases, decisiones pendientes, cómo se hizo el análisis y qué no se hizo.
Eso tiene dos ventajas. Los documentos son comparables: pude comparar qué problemas heredaba un módulo de su predecesor y cuáles eran nuevos. Y son delegables: un agente que ha visto uno sabe leer el siguiente, y un subagente auditor sabe qué se espera de su informe.
La auditoría tampoco es el final. Uno de los módulos acabó reescrito con SDD y TDD a partir de sus specs. Ahí la verificación de punta a punta contra el core real cazó un fallo que los tests no veían: el doble que imitaba una función del core no se comportaba igual que el original. Se corrigió con un test de regresión y una nota en el AGENTS.md para el siguiente agente.
Una plantilla para probarlo
Este es el esqueleto del encargo a cada subagente auditor. Sigue el mismo prompt tipo que uso con código nuevo: qué leer, la tarea, las restricciones y el formato del informe.
Eres auditor de solo lectura del área: <ÁREA>.
Commit de referencia: <COMMIT>. No cambies de rama ni hagas pull.
Lee antes:
- AGENTS.md y README (pueden estar desactualizados: anótalo si es así)
- <FICHEROS DEL ÁREA>
- <DOCUMENTACIÓN EXISTENTE, SI LA HAY>
Tarea:
1. Describe qué hace hoy el código de tu área: flujos, entradas,
salidas, datos que lee y escribe.
2. Lista los defectos con un ID <PREFIJO>-<C|I|M><n>
(crítico, importante, menor): dónde, qué pasa, un escenario
concreto y una propuesta de arreglo.
3. Marca cada hallazgo como «confirmado leyendo el código»
o «sospecha sin confirmar».
Restricciones:
- Solo lectura: no edites ficheros ni escribas en la base de datos.
- No explotes nada ni hagas peticiones que cambien estado.
- No envíes correos ni generes documentos.
Informe (compacto, sin narración):
- Resumen del área en diez líneas
- Tabla de hallazgos: ID | dónde | qué pasa | escenario | arreglo
- Dudas que solo puede resolver una persona
- Qué no has podido comprobar
Y este es el índice de specs que se repite de un módulo a otro. Los defectos se marcan dentro de cada spec con ⚠ y su identificador, que apunta a la spec de tareas:
000 Especificación visión, actores, reglas de negocio, historias
001 Plan técnico stack, estructura, puntos de entrada, dependencias
002 Modelo de datos tablas, columnas reales, estados
003 Cálculo fórmulas con ejemplos
004 API contrato de cada acción o endpoint
005 Interfaz pantallas y casos
006 Tests estado de los tests y matriz objetivo
007 Tareas hallazgos, fases con propiedad de ficheros, decisiones
008 Emails y PDF plantillas y documentos generados
009 Instalación instalación, migraciones, hooks, configuración
Entender antes de cambiar
Con código nuevo, el método me obliga a decidir antes de escribir. Con código heredado, me obliga a entender antes de cambiar. En los dos casos el resultado es el mismo: un contrato escrito del que un agente se puede fiar, y que una persona puede revisar.
El hub de correo entre agentes y appdist son código nuevo hecho con este método. Los módulos heredados son la otra mitad del trabajo, la menos vistosa y probablemente la más frecuente: convertir código que nadie entiende del todo en algo que se puede cambiar sin miedo.