Blog | Empleados, tecnología, noticias, eventos | Mimacom

Agentes de IA: ¿hace falta seguir leyendo código?

Escrito por Daniel Willig | 20 oct 2026, 2:47:11

Unos agentes de IA construyeron una interfaz de administración para un proyecto de cliente, y otros agentes, los de revisión, detectaron fallos que a Daniel Willig, Senior Software Engineer, se le habían escapado. La pregunta es: ¿sigue teniendo que leer código un desarrollador? Daniel explica qué comprobaciones pueden pasar sin riesgo a los agentes y a reglas automatizadas, y qué código necesita a una persona responsable que lo lea.

Puntos clave

  • Los agentes de IA ya pueden escribir la mayor parte del código de un proyecto. La verdadera pregunta es quién lo revisa y cómo.
  • En un proyecto reciente para un cliente, Daniel repartió la revisión en tres partes: las reglas automatizadas comprobaban la estructura, los agentes de revisión comprobaban el detalle y él comprobaba lo que el cliente necesitaba de verdad y quién podía acceder a qué.
  • El resultado es software construido a la velocidad de los agentes, en el que las partes que protegen los datos y los accesos siguen pasando por alguien que responde de ellas.

Hace poco construí una interfaz de administración para un proyecto de cliente sin escribir yo mismo ni una sola línea de código. Un grupo de agentes de IA escribió la implementación y un segundo grupo, los agentes de revisión, examinó cada cambio, mientras un analizador estático de código y un linter aplicaban las reglas que yo había definido. Revisé los cambios con todo el cuidado que pude, y aun así los agentes de revisión encontraron más cosas que yo, en una fracción del tiempo.

Con frecuencia señalaban casos límite en los que yo ni siquiera había pensado y, en una ocasión, un fallo en una decisión que había tomado yo. Al principio del proyecto había especificado, sin pensarlo demasiado, que cada inicio de sesión fallido debía generar una entrada de auditoría, es decir, un registro en el log que deja constancia de qué usuario hizo qué y cuándo. Un inicio de sesión fallido no tiene un usuario autenticado, así que esas entradas no tenían autor y apenas registraban nada útil. Peor aún: cualquiera podía llenar la base de datos de auditoría simplemente enviando intentos de inicio de sesión con credenciales incorrectas. Un agente de revisión lo detectó, mientras que yo nunca había cuestionado mi propio requisito.

Experiencias como esta me hicieron preguntarme si leer código sigue siendo parte de mi trabajo o si mi trabajo consiste ahora en comprobar que lo que sale coincide con lo que entra. Tiendo a estar de acuerdo con la idea de que la IA es un nivel más de abstracción. Los programadores dejaron de leer ensamblador cuando los compiladores se volvieron fiables, y siguieron preocupándose por la corrección del programa subiendo su atención un nivel, al código fuente. Si los agentes convierten de forma fiable la intención en código que funciona, la atención podría subir otra vez, a la especificación y al comportamiento observado.

Creo que la analogía funciona para la mayor parte del código, pero falla en algunos puntos, y esos puntos dicen mucho sobre por qué construimos abstracciones. La cuestión va más allá de los desarrolladores. Cuando los agentes escriben el código, quien lo paga necesita saber qué se sigue revisando, quién lo revisa y qué garantías obtiene a cambio.

 

Dónde funciona la analogía de la abstracción y dónde falla

Podemos confiar en un compilador sin leer lo que genera porque su traducción es determinista y porque su entrada, el código fuente, define el comportamiento por completo. Solemos atribuir el mérito a la herramienta, pero buena parte de esa confianza viene de la precisión de la entrada.

Un ticket o un prompt es mucho menos preciso. El lenguaje natural deja huecos, y un agente los rellena con decisiones que parecen razonables y que yo solo veo cuando aparecen en el código. Un agente construye lo que se le pide, huecos incluidos, así que la calidad de los requisitos determina ahora en gran medida la calidad del software. Comparar entrada y salida solo puede sustituir a la lectura del código si la entrada es lo bastante exacta como para servir de referencia. En mi proyecto, esa precisión venía de criterios de aceptación detallados en los tickets, a partir de los cuales los agentes generaban los tests. Los criterios cumplen ahora el papel que antes cumplía el código fuente, así que merecen la lectura atenta que antes recibía la implementación. Los tests generados también merecen un segundo vistazo, porque un test escrito a partir de un criterio comprueba la interpretación que el agente ha hecho de él, y cualquier hueco del criterio se traslada al test.

Vale la pena detenerse en el problema de la auditoría, porque el fallo estaba en mi requisito y la implementación lo siguió al pie de la letra. El agente lo encontró razonando sobre las consecuencias: quién puede provocar un inicio de sesión fallido y qué contiene la entrada resultante. Por tanto, los agentes de revisión pueden cuestionar la intención, siempre que esa intención haya dejado rastro en el código. Lo que no pueden ver es la intención que nunca llega al repositorio, como una expectativa que tiene el cliente pero que nadie dejó por escrito. Si los agentes que revisan y los que escribieron el código se basan en modelos parecidos, también pueden pasar por alto las mismas cosas, de modo que su acuerdo vale menos que el de dos personas independientes.

La autorización es el otro punto débil, y una interfaz de administración es, en gran parte, autorización. Un test puede confirmar que un administrador puede editar un registro. Demostrar desde fuera que ningún usuario con un rol inferior puede hacer lo mismo es mucho más difícil, porque esa propiedad afecta a todos los caminos posibles a través del código y ningún conjunto de tests los recorre todos.

 

Por qué abstraemos: tres restricciones

En su ensayo de 1986 «No Silver Bullet», Fred Brooks distinguió entre la complejidad esencial, propia del problema, y la complejidad accidental, que proviene de nuestras herramientas y métodos. Moseley y Marks desarrollaron esa distinción en su artículo de 2006 «Out of the Tar Pit». Esta distinción ayuda a decidir si una abstracción elimina complejidad o solo la traslada a otro sitio. Lo que no dice es a quién o a qué sirve la abstracción, que es justo la pregunta que plantean los agentes. Para eso me resulta más útil preguntar de qué protege una abstracción, y veo tres restricciones distintas detrás de las abstracciones que usamos.

La primera es la memoria de trabajo humana. Solo podemos tener en mente unas pocas ideas que interactúan entre sí, así que escondemos el detalle detrás de nombres y patrones conocidos y dividimos la lógica en funciones pequeñas. La teoría de la carga cognitiva de John Sweller, el marco de dimensiones cognitivas de Green y Petre (que evalúa las notaciones según propiedades como lo difícil que es modificarlas y cuántas dependencias ocultan) y el libro de Felienne Hermans The Programmer's Brain explican por qué esto ayuda. El precio es la indirección, ya que para saber qué ocurre hay que seguir las llamadas a través de interfaces y configuración.

La segunda es el propio contexto del modelo. Los modelos leen mucho más código de una vez que nosotros, y parte de esta compresión les sobra. Aun así, su contexto es limitado, y cada token, la unidad en la que los modelos leen el texto y en la que se factura su uso, tiene un coste. En un codebase grande, además, un modelo tiene que encontrar los archivos adecuados antes de poder hacer algo útil con ellos, y los nombres claros y los módulos coherentes facilitan esa búsqueda. Por eso, muchas abstracciones pensadas para las personas también ayudan a los modelos, solo que a partir de umbrales distintos.

La tercera restricción no tiene nada que ver con quién lee el código. David Parnas defendió en 1972 que los módulos deben ocultar las decisiones de diseño que probablemente cambien, para que cada cambio se quede en un ámbito local. Unos años antes, Melvin Conway había observado que los sistemas adoptan la estructura de comunicación de las organizaciones que los construyen, y por eso muchas fronteras en el software existen para que equipos distintos puedan trabajar sin coordinarse constantemente. Las fronteras de seguridad, que limitan a qué puede acceder cada componente, también pertenecen a este grupo, igual que los puntos en los que los tests pueden sustituir dependencias reales como una base de datos. Estas abstracciones protegen propiedades del sistema en ejecución y de la forma en que se construye, y que el código lo escriba un agente no cambia en nada por qué son necesarias.

La mayoría de las abstracciones sirven a más de una de estas restricciones a la vez. Lo importante es cuál de ellas justifica una abstracción concreta, porque una que solo facilita la lectura humana merece cuestionarse cuando son los agentes quienes hacen la mayor parte de la lectura y la escritura.

 

 

 

El formato solo sirve a los lectores humanos, mientras que los nombres y las funciones pequeñas también ayudan a los modelos a orientarse en un codebase. Las abstracciones que llegan a la columna de la derecha siguen siendo necesarias, escriba quien escriba el código.

 

Qué dicen las primeras evidencias

La investigación que pone a prueba estas restricciones de forma directa todavía es escasa. En un estudio de 2023, Jain y otros investigadores de UC Berkeley reescribieron los datos de entrenamiento de generadores de código para hacerlos más estructurados y legibles, y comprobaron que los modelos resultantes generaban mejor código. Parece que los modelos se benefician de la misma estructura que las personas, algo poco sorprendente si tenemos en cuenta que aprendieron de código escrito para personas.

Los modelos tampoco encuentran por sí solos, de forma fiable, el nivel de abstracción adecuado. Los autores de KISS Sorcar, un asistente de programación reciente, describen cómo los modelos añaden clases auxiliares y capas extra de indirección que hacen el código más difícil de depurar, y lo contrarrestan indicando al modelo que prefiera el código sencillo. Un modelo que copia patrones de sus datos de entrenamiento sin valorar cuándo están justificados puede producir abstracciones que ninguna de las tres restricciones justifica.

El formato es donde las necesidades humanas y las del modelo divergen claramente. Un estudio midió qué parte del presupuesto de tokens de un modelo se va en espacios en blanco y maquetación, que existen casi exclusivamente para los lectores humanos, y propuso herramientas que muestran a las personas el código formateado mientras le dan al modelo una versión compacta. Es el ejemplo más claro que conozco de una abstracción que solo sirve a la cognición humana y que puede separarse del propio código.

Los propios desarrolladores no han renunciado a la legibilidad. En una encuesta de Atlassian de 2025, la mayoría de sus desarrolladores afirmó que el código legible sigue siendo esencial para el mantenimiento y la colaboración, aunque la IA escriba cada vez más. Mientras sean las personas quienes posean y mantengan el código, es una postura razonable.

 

Un caso de prueba: la arquitectura hexagonal

Cuando empecé el proyecto, decidí que seguiría una arquitectura hexagonal. Durante la implementación dudé muchas veces de esa decisión, sobre todo porque no podía descartar haberla elegido simplemente porque siempre lo hago.

En una arquitectura hexagonal, también llamada de puertos y adaptadores, la lógica de dominio define interfaces para lo que necesita, llamadas puertos, y los adaptadores las implementan para una tecnología concreta, como una base de datos o un framework web. Los argumentos habituales a su favor son que la infraestructura puede cambiarse sin tocar la lógica de negocio y que el dominio puede probarse sin un sistema en marcha. Ambos dan por hecho que hay bastante lógica de dominio y una posibilidad real de que los adaptadores cambien. Una interfaz de administración, en su mayor parte, crea, lee, actualiza y elimina registros detrás de controles de acceso, así que en mi caso ninguno de los dos argumentos tenía mucho peso.

Lo que no esperaba era lo útiles que iban a ser las reglas de la arquitectura en cuanto los agentes empezaran a escribir el código. La arquitectura hexagonal se reduce a unas pocas reglas de dependencia, sobre todo que el dominio nunca importa de los adaptadores y que los adaptadores solo dependen de los puertos. Herramientas como ArchUnit, dependency-cruiser o eslint-plugin-boundaries pueden comprobar estas reglas de forma mecánica, y en mi proyecto unas reglas de linter personalizadas detectaron las infracciones de frontera típicas, como lógica de dominio que importaba un adaptador directamente en lugar de pasar por su puerto. Un desarrollador que trabaja meses en un proyecto interioriza sus convenciones y las aplica sin que nadie se lo diga. Un agente empieza cada sesión sin memoria de las decisiones tomadas en las anteriores, así que su código se va alejando de la estructura prevista aunque cada cambio por separado parezca razonable. Por eso las convenciones tienen que vivir fuera del agente, en reglas que se comprueban en cada cambio, y eso hace que una arquitectura con reglas comprobables valga más en un proyecto dirigido por agentes que en uno tradicional.

 

 

 

Las flechas continuas muestran la única dirección de dependencia permitida. La discontinua es el tipo de infracción que las reglas del linter detectaron en el código escrito por agentes.

El mismo enfoque funcionó para la autorización, que antes describí como la propiedad más difícil de verificar desde fuera. Cada ruta de la interfaz de administración está envuelta en un wrapper de permisos, una función que declara qué permiso necesita un usuario para poder usar la ruta. Una regla del linter exigía que cada ruta tuviera ese wrapper. Las rutas públicas no eran una excepción: necesitaban un wrapper explícito, PublicPermissions, para superar la comprobación. Muchos sistemas están abiertos por defecto, de modo que una ruta sin control es simplemente accesible y un control olvidado pasa desapercibido. Con esta regla, cada ruta estaba cerrada hasta que alguien decidía lo contrario, y los permisos siempre se declaraban; nunca se deducían de la ausencia de un control. La regla no puede saber si el permiso declarado es el correcto. Lo que sí garantiza es que ninguna ruta esquiva la pregunta, y reduce mi parte de la revisión a recorrer una lista de rutas y los permisos asignados a cada una, en lugar de buscar en el código caminos que pudieran haber quedado sin proteger. Para el cliente, esto significa que ninguna página ni acción del sistema puede superar las comprobaciones sin que alguien haya decidido quién puede usarla. Además, la lista completa de quién puede hacer qué es algo que el cliente o sus auditores pueden revisar por sí mismos, en lugar de darlo por bueno sin más.

De forma simplificada, y con nombres elegidos a modo de ejemplo, la regla distingue entre estas tres rutas:

// Ruta de administración: solo los usuarios con el rol adecuado pueden listar cuentas
export const GET = withPermissions(AdminPermissions.ReadUsers, async (request) => {
  return listUsers(request);
});

// Ruta pública: aun así necesita un wrapper explícito para pasar el linter
export const POST = withPermissions(PublicPermissions, async (request) => {
  return login(request);
});

// Rechazada por el linter: no tiene ningún wrapper de permisos
export const DELETE = async (request) => {
  return deleteUser(request);
};

La objeción habitual a la arquitectura hexagonal es la cantidad de código que exige, con puertos, adaptadores y mappers para cada funcionalidad. Un agente escribe ese código prácticamente sin coste. Aun así, la indirección sigue ahí cuando algo falla, y revisar un cambio que atraviesa varias capas sigue llevando más tiempo.

De esto no puedo concluir que la arquitectura hexagonal en concreto fuera la elección correcta. Las reglas detectaron errores, lo que me dice que imponer fronteras mereció la pena, pero una estructura por capas más sencilla, con el mismo tipo de reglas de importación, quizá los habría detectado igual. En cualquier caso, la arquitectura se ganó su sitio protegiendo la estructura del sistema, algo que corresponde a la tercera restricción y que no era lo que yo tenía en mente cuando la elegí.

 

Un reparto del trabajo

Mi respuesta actual a la pregunta del principio es repartir la revisión según lo que cada participante hace bien. El análisis estático y las reglas de linting comprueban la estructura, y lo hacen de la misma manera en cada cambio. Los agentes de revisión comprueban el detalle, incluidos los casos límite y los problemas de seguridad como el de las entradas de auditoría, más rápido y a fondo de lo que puedo hacerlo yo. Lo que me queda a mí es la intención que nunca llega al código: si la especificación describe lo que el cliente necesita y si las propiedades de las que depende el sistema se cumplen de verdad.

 

 

Cada comprobación cubre algo que la anterior no puede ver. Solo el último paso sigue implicando leer código, y solo las partes que hacen cumplir las garantías.

Así que ya no necesito leer todo el código, pero sí las partes que hacen cumplir las garantías, como los controles de autorización, el acceso a los datos y las interfaces de las que dependen otros sistemas. Un error en esas partes suele ser invisible desde fuera, y ni un linter ni un agente de revisión saben cuál era la intención. Para el resto puedo juzgar el comportamiento, siempre que ese comportamiento esté fijado por criterios de aceptación lo bastante detallados como para generar tests a partir de ellos, y que yo haya leído con atención tanto los criterios como los tests.

El cliente me hace responsable del resultado, y la aprobación de un agente no cambia eso. Es una comprobación más en el proceso, y una buena, pero el criterio sobre qué hay que construir sigue siendo mío.

 

La confianza hay que construirla alrededor del agente

Sigo pensando que la IA es un nivel más de abstracción, aunque se diferencia de un compilador en algo importante. En un compilador confiamos porque su entrada es precisa y su traducción, determinista. Un agente parte de una intención imprecisa, así que la confianza hay que construirla a su alrededor: con criterios de aceptación precisos, los tests derivados de ellos y reglas comprobadas de forma mecánica, además de atención humana sobre el código que hace cumplir las garantías del sistema. Con todo eso en su sitio, comprobar la salida frente a la entrada es una alternativa razonable a leer cada línea.

El proyecto también me enseñó que una abstracción puede resultar útil por un motivo distinto del que llevó a elegirla. Escogí la arquitectura hexagonal en parte por costumbre, y resultó importante porque daba a los agentes fronteras que podían comprobarse automáticamente. Sospecho que muchas abstracciones que consideramos imprescindibles merecen una revisión parecida, y que algunas resultarán existir solo porque nos ayudaban a leer.

No sé responder cuánto tiempo seguirán siendo las personas las principales lectoras del código en producción. Por ahora seguimos revisando cambios e investigando incidencias, así que las abstracciones que hacen legible el código conservan buena parte de su valor. Si ese papel se desplaza aún más hacia especificar y verificar, la legibilidad podría convertirse en algo que se genera bajo demanda: por ejemplo, un agente que explica o reestructura una parte concreta del código para la persona que necesita entenderla, igual que el formato ya puede aplicarse con independencia del código. Si eso funcionará lo bastante bien como para confiar en ello sigue siendo una pregunta abierta.