Hace unas semanas asistí al evento Future of Software Engineering event en Engelberg, Suiza. Como cabría esperar de un evento celebrado a mediados de 2026, muchas de las conversaciones giraron en torno a las prácticas de ingeniería de IA y los agentes de programación.
Durante el evento encontré distintos puntos de vista. Algunas personas comentaron que preferían mantener a los agentes bajo un control estricto, revisando sus resultados línea por línea para asegurarse de que el código fuera mantenible, estuviera bien estructurado y cumpliera con todo lo que asociamos con las buenas prácticas de software craftsmanship.
Otras afirmaron que preferirían delegar la mayor parte del ciclo de vida del desarrollo de software a los agentes. En términos generales, a este grupo no le preocupaba demasiado el código que los agentes realmente producen; consideraban que era mejor dedicar el tiempo a mejorar las especificaciones y las verificaciones deterministas que guían al agente, dejando que este se encargue completamente del código. (Siempre que la compilación siga funcionando, claro).
¿Las personas dejarán de necesitar revisar el código?
El extremo de la “autonomía total” es el que más me hace dudar. Me recordó una idea más amplia que veo circular con frecuencia en mis círculos sociales y en redes:
Pronto, ninguna persona tendrá que volver a revisar o mantener código. ¿Seguirá importando un buen diseño de software dentro de unos años?
El argumento es más o menos así: a medida que evolucionen los flujos de trabajo agénticos, llegará un momento en el que las personas solo tendrán que crear especificaciones en Markdown, árboles de contexto o cualquier nuevo patrón de interacción que se vuelva popular. Esa especificación podrá entregarse a un equipo de agentes de IA que generará, probará, revisará, desplegará y posteriormente evolucionará el código. Con el tiempo, el código podría convertirse en poco más que un detalle de implementación para la mayoría de las empresas. Incluso podríamos tener agentes de guardia respondiendo a incidentes de producción a las dos de la mañana.
Algunas variantes del desarrollo basado en especificaciones ya proponen tratar las especificaciones como la nueva lengua franca del desarrollo de software y el código como un artefacto efímero.
La segunda línea del Manifiesto Ágil dice: “Software funcionando sobre documentación exhaustiva”. ¿Deberíamos reformularla como “Software funcionando como documentación exhaustiva”? Sin duda, es una pregunta desafiante para quienes, como yo, sentimos orgullo profesional al crear código limpio y legible. Y sí, admito que existe cierto conflicto de intereses: después de todo, este es un artículo escrito por una programadora explicando por qué la programación sigue siendo necesaria.
Aun así, lo que sigue es una recopilación de mis reflexiones personales sobre este tema, basadas tanto en las conversaciones que mantuve durante el evento como en mi propia experiencia manteniendo sistemas en producción con ayuda de agentes.
A lo largo del resto del artículo explicaré por qué creo que un buen diseño sigue siendo importante, incluso cuando los agentes realizan la mayoría de los cambios; por qué las personas deben seguir formando parte del proceso para producir software bien diseñado; y por qué esas mismas personas necesitan continuar trabajando a nivel de código para conseguirlo.
Pero antes de entrar en ello, conviene entender el argumento de que simplemente estamos ante una nueva capa de abstracción. Desde mi punto de vista, no es así, y creer que lo es constituye precisamente el núcleo del problema.
¿Son realmente las especificaciones la siguiente capa de abstracción?
No es ningún misterio que el software parece haber avanzado de una capa de abstracción a otra. Pasamos del código binario y las tarjetas perforadas al ensamblador, y del ensamblador a lenguajes de más alto nivel. En gran medida, dejamos de asignar y liberar memoria manualmente. Los servidores fueron sustituidos por máquinas virtuales, que posteriormente fueron sustituidas por contenedores, y así sucesivamente. Estas abstracciones no hicieron desaparecer las capas inferiores, pero sí cambiaron la frecuencia con la que necesitamos interactuar con ellas.
Quienes desarrollamos aplicaciones rara vez pensamos hoy en ensamblador, porque la abstracción que existe sobre él suele ser suficiente, aunque pueda ser incompleta. De hecho, la mayoría trabajamos desde la parte más alta de una torre de abstracciones construidas unas sobre otras. Lo hacemos conscientemente porque resulta más productivo y simplemente aceptamos que perdemos de vista algunos detalles de bajo nivel.
Es tentador predecir que la programación agéntica podría convertirse en el siguiente bloque de esa torre y que las especificaciones serán para el código fuente lo que C fue para el ensamblador.
En lugar de expresar nuestra intención mediante código, ahora podemos hacerlo mediante lenguaje natural. El agente traduce el texto en código fuente, de forma similar a como un compilador traduce C a binario. Algunas personas sostienen que, dentro de unos años, saber leer código podría convertirse en algo tan especializado como saber leer tarjetas perforadas. El rol de “programador” cambiaría radicalmente para dedicarse a producir especificaciones suficientemente precisas o, quizá, desaparecería por completo.
Esto haría obsoletas muchas preocupaciones tradicionales del diseño de software: ¿para qué crear abstracciones elegantes si ninguna persona va a verlas? ¿Por qué eliminar duplicaciones, malos patrones de código o nombres ambiguos si el agente consigue hacer el trabajo de todas formas?
Un buen diseño también importa para los agentes
Hay buenas razones para ser prudentes antes de declarar obsoleto el diseño orientado a las personas: después de todo, los agentes de programación actuales fueron entrenados con software escrito por personas.
El código con el que aprendieron no es un conjunto aleatorio y minificado que simplemente produce exactamente el resultado descrito en su documentación. Los agentes han aprendido de código cuidadosamente dividido en funciones y clases con nombres significativos, con abstracciones y patrones de pensamiento humano presentes en todas partes. Los datos de entrenamiento de cada modelo reflejan décadas de modelos mentales, patrones de diseño y lenguaje compartido entre personas.
Por eso, muchas de las prácticas que asociamos con un “buen diseño” no solo son útiles para quienes leen el código. También son los patrones en los que los agentes han aprendido a apoyarse cuando intentan determinar qué deben hacer a continuación dentro de una base de código existente. No importa si el código anterior fue escrito por una persona o no: si está enredado y resulta incomprensible para una persona, es muy probable que un agente también tenga dificultades para modificarlo.
Por eso, y esto es algo que he observado en mi experiencia, cuando un agente se encuentra con un sistema fuertemente acoplado, nombres inconsistentes, lógica de negocio duplicada o la clásica “gran bola de lodo”, sus resultados se vuelven menos fiables. He visto a agentes realizar muchas más suposiciones incorrectas y cometer errores directamente cuando trabajan con bases de código menos legibles, lo que termina requiriendo intervención humana. También consumen cantidades cada vez mayores de contexto para comprender correctamente cómo funciona un sistema y pueden adentrarse en caminos innecesarios al intentar depurar comportamientos inesperados. Eso significa consumir más tokens y aumentar la probabilidad de cometer errores.
Un mal diseño no deja de importar simplemente porque sea una IA quien escriba y lea el código. El coste simplemente cambia. En lugar de pagarlo únicamente en esfuerzo cognitivo humano, también lo pagamos en exceso de contexto, fiabilidad y, por supuesto, dinero real en tokens. De hecho, quizá sea la primera vez en la historia del software que podemos cuantificar el coste exacto de la deuda técnica: solo tenemos que mirar la factura mensual de Claude del equipo.
En otras palabras, la forma en que los agentes fallan cuando encuentran un mal diseño me hace pensar que un buen diseño sigue siendo relevante para la economía del mantenimiento de productos de software.
Pero ¿no podríamos simplemente pedirles a los agentes que creen código que sea fácil de modificar para ellos mismos? Volvamos a hablar de las personas dentro del proceso.
¿Por qué las personas siguen siendo necesarias para producir software “bien diseñado”?
Creo que existen varias razones por las que el diseño continúa siendo una responsabilidad humana y no algo que podamos delegar completamente. Todas ellas van más allá de repetir simplemente el argumento de que “la IA todavía no es lo suficientemente buena”.
Quiero centrarme en tres problemas que considero limitaciones inherentes de los flujos de trabajo exclusivamente agénticos que producen código sin asistencia.
Problema 1: el código agéntico se degrada con el tiempo, y los agentes lo permiten
Una de las cosas más impresionantes de los agentes es la velocidad con la que pueden producir software funcional partiendo de cero. Sin embargo, siguen siendo demasiado recientes como para que la mayoría de las empresas haya experimentado qué ocurre con una base de código generada por agentes después de la funcionalidad número cien o del segundo año en producción.
Que el software “se deteriore” no es un fenómeno nuevo. Cuando ocurre con código creado por personas, lo llamamos entropía del software o deuda técnica e intentamos, con distintos grados de éxito, explicárselo a nuestros stakeholders cuando nos preguntan por qué ha disminuido nuestra velocidad.
¿Recuerdas aquel gráfico que mostraba la relación entre deuda técnica y velocidad? Existen muchas versiones que han circulado durante años, y Martin Fowler tiene uno similar en este artículo.)
Creo que la mayoría de los productos de software generados por agentes en la industria podrían encontrarse actualmente en esa curva de “ahorro inicial”.
Los agentes son incluso peores que las personas siguiendo la regla del Boy Scout™ y tienden a dejar el código en peores condiciones de las que lo encontraron. Incluso con los mejores linters o herramientas sofisticadas como ArchUnit, encuentran formas sorprendentes de hacer trampas para conseguir que la compilación pase correctamente. Además, las inconsistencias tienden a acumularse a medida que se utilizan agentes para añadir cada vez más funcionalidades. Todas esas excepciones y optimizaciones locales contribuyen, una a una, a aumentar la complejidad general del sistema y se convierten en “una cosa más que recordar” para el siguiente agente. Eso es deuda técnica de manual.
La diferencia entre la deuda técnica generada por personas y la generada por agentes es que los agentes pueden introducir esos cambios mucho más rápido de lo que las personas jamás podrían, por lo que sus efectos se acumulan antes. Sin alguien que periódicamente se detenga, identifique problemas emergentes de diseño y reorganice deliberadamente el sistema, la base de código se convertirá en una gran bola de lodo todavía más rápido.
Tampoco debemos olvidar que, incluso en un sistema desarrollado completamente por personas, a medida que se incorporan nuevas funcionalidades, la arquitectura original representa cada vez peor unos requisitos que evolucionan constantemente. El diseño debe evolucionar junto con el sistema, con personas que analicen cómo los nuevos requisitos pueden entrar en conflicto con el diseño actual.
Sin embargo, los agentes no pueden cuestionar una especificación ni detenerse a reevaluar la arquitectura como lo hacen las personas. Tampoco pueden escalar preocupaciones arquitectónicas al resto del equipo y sus stakeholders. Si conseguimos automatizar eso, entonces consideraré alejarme de la industria y buscar otras opciones profesionales.
Si el código va a seguir siendo una caja negra porque un agente puede hacerlo más rápido, quienes programan simplemente tendrán que aumentar la estructura de sus especificaciones. Llegado cierto punto, ya no estaremos sustituyendo la programación, sino simplemente programando en un lenguaje peor y más ambiguo.
Si el código va a seguir siendo una caja negra porque un agente puede hacerlo más rápido, quienes programan simplemente tendrán que aumentar la estructura de sus especificaciones. Llegado cierto punto, ya no estaremos sustituyendo la programación, sino simplemente programando en un lenguaje peor y más ambiguo.
Problema 2: una especificación suficientemente precisa podría ser código
Una forma evidente de intentar solucionar el primer problema es escribir especificaciones de diseño muy sólidas que los agentes deban respetar antes de realizar cualquier cambio.
En lugar de pedir a un agente que implemente la funcionalidad X, podríamos especificar la arquitectura, los límites entre módulos, el manejo de errores, las convenciones de nombres, la estrategia de pruebas, los requisitos de rendimiento y todas las restricciones importantes de diseño. Incluso podríamos pedirle que escribiera todo tipo de conjuntos de pruebas deterministas que hagan fallar la compilación cuando no se respeten esas restricciones.
Todo lo anterior depende mucho del contexto, de lo que haga la aplicación y de quién vaya a utilizarla. No podemos simplemente crear un libro universal de Clean Code for Agents y cargarlo indiscriminadamente en el contexto de cada agente. Nuestra especificación de diseño debe adaptarse exactamente al tipo de sistema que estamos construyendo, y determinadas funcionalidades pueden requerir instrucciones todavía más específicas para modificar convenciones existentes. Quizá cada nueva funcionalidad necesite sus propias restricciones y excepciones de diseño.
Espero que ya se vea hacia dónde voy, pero para dejarlo claro: a medida que las especificaciones se vuelven más y más precisas, empiezan a parecer sospechosamente código.
Sí, el lenguaje natural es expresivo y mucho más accesible que el código, pero también es muy ambiguo. Eliminar esa ambigüedad solía requerir que quienes programaban tradujeran los requisitos de su PO/PM y completaran los vacíos necesarios para poder escribir código sin ambigüedades y darle un diseño adecuado. Pero si el código debe permanecer como una caja negra porque aparentemente un agente puede hacerlo más rápido, simplemente tendremos que aumentar la estructura de las especificaciones. Llegado cierto punto, ya no estaremos sustituyendo la programación, sino programando en un lenguaje de programación peor y más ambiguo.
Vale la pena remontarse a 1978 para encontrar unas palabras útiles sobre esto:
"La virtud de los textos formales es que sus manipulaciones, para ser legítimas, solo necesitan cumplir unas pocas reglas simples; si se piensa bien, son una herramienta sorprendentemente eficaz para descartar todo tipo de disparates que, cuando utilizamos nuestras lenguas maternas, resultan casi imposibles de evitar.
En lugar de considerar la obligación de utilizar símbolos formales como una carga, deberíamos considerar la posibilidad de utilizarlos como un privilegio [...]. En última instancia, la ‘naturalidad’ con la que utilizamos nuestras lenguas maternas se reduce a la facilidad con la que podemos utilizarlas para formular afirmaciones cuyo sinsentido no resulta evidente."
— Edsger W. Dijkstra, On the foolishness of 'natural language programming (1978)
Personalmente, prefiero diseñar el código junto con un agente en lugar de simplemente entregarle especificaciones en lenguaje natural. Esto permite resolver la ambigüedad de forma incremental y convertirla en un conjunto de reglas bien organizado a medida que se escribe el código. No solo ahorra tiempo de idas y vueltas, sino que también permite utilizar el código como un vocabulario compartido y no ambiguo para un buen diseño mientras trabajamos junto al agente.
Problema 3: los agentes no son deterministas
Este es mi último punto y puede parecer evidente, pero merece repetirse. La mayor diferencia entre la programación agéntica y las capas de abstracción anteriores es que los agentes no son deterministas, mientras que históricamente las capas anteriores sí lo han sido.
Los compiladores no son cajas negras mágicas a las que les entregamos código para que salga un binario esperando que haga lo que escribimos sin introducir casos límite alucinados. Los compiladores siguen reglas. Con la misma entrada, producen de forma fiable la misma salida. En cambio, si pides a un agente que implemente cinco veces la misma funcionalidad, probablemente recibirás cinco soluciones diferentes.
Como los agentes toman decisiones de programación introduciendo contexto y suposiciones en una caja negra basada en datos de entrenamiento desconocidos, alguien sigue teniendo que estar al otro lado de esa caja negra para determinar si esas decisiones son adecuadas. Necesitamos decidir qué compromisos tienen sentido para el sistema y determinar si la implementación encaja con la visión técnica.
Para mí, esa es una tarea de criterio humano que va mucho más allá de generar código.
Un buen diseño requiere analizar los detalles del código
Los problemas que he señalado me llevan a la misma conclusión: un buen diseño de software no es algo que pueda especificarse una vez en un archivo Markdown o en un conjunto de pruebas y luego olvidarse mientras dejamos que los agentes trabajen indefinidamente. Nuestra industria incluso tiene un nombre para la práctica de crear grandes especificaciones arquitectónicas y después pasar años delegando su implementación: Waterfall.
En cambio, coincido con la perspectiva de XP de que el diseño surge mediante feedback y ajustes continuos y se renegocia constantemente a medida que evoluciona el contexto.
Además, creo que este criterio debe aplicarse directamente sobre el código, no solo sobre documentos de diseño, conjuntos de pruebas o diagramas de arquitectura. No se puede decidir de forma significativa si una responsabilidad pertenece a un módulo u otro, si una abstracción se ha vuelto demasiado genérica o si dos conceptos deberían fusionarse sin observar la implementación actual. Es en el código donde se hacen visibles el acoplamiento, la cohesión, la duplicación y la complejidad. Para mí, es la prueba definitiva de cualquier supuesto de diseño.
“El diseño del software no está completo hasta que ha sido codificado y probado [...] El diseño estructural de alto nivel no constituye un diseño completo de software; es únicamente un marco estructural para el diseño detallado [...] El diseño detallado terminará influyendo, o debería poder influir, en el diseño de alto nivel al menos tanto como otros factores.
Refinar todos los aspectos de un diseño es un proceso que debería producirse durante todo el ciclo de diseño. Si algún aspecto queda fuera del proceso de refinamiento, no debería sorprender que el diseño final sea deficiente o incluso inviable.”
— Jack W. Reeves, What is Software Design? (1992)
Incluso si los agentes llegan a encargarse de producir la mayor parte del código, las personas seguirán necesitando descender a ese nivel inferior de abstracción para evaluar la salud del sistema. En mi experiencia trabajando con agentes, he tenido que hacerlo prácticamente todos los días, especialmente al trabajar con sistemas críticos en los que estaban en juego transacciones de clientes.
Por eso, mi predicción general es que el código de producción no desaparecerá como artefacto de ingeniería. Seguirá siendo algo que inspeccionaremos, cuestionaremos y mejoraremos continuamente, independientemente de que los cambios los escriban personas o los generen agentes.
La IA no es la siguiente capa de abstracción
En resumen, el diseño de software sigue siendo importante porque sigue siendo importante que el software pueda cambiar. Y el criterio humano sobre el código continúa siendo fundamental para el proceso de diseño.
No creo que dejemos de necesitar personas capaces de leer código y, como consecuencia, seguiremos necesitando construir código bien diseñado que las personas puedan entender. Puede que colectivamente tengamos que leer menos código, pero no creo que dejemos de necesitar profesionales de programación en el corto plazo si queremos: a) producir software que funcione y b) mantenerlo funcionando durante al menos unos años.
Para mí, el mayor cambio es que las personas ya no son la única audiencia del buen código y del buen diseño. Debemos crear software que sea fácil de modificar tanto para personas como para agentes. La audiencia se ha ampliado de personas a personas y agentes, y probablemente descubriremos que algunos principios de diseño evolucionan mientras otros permanecen estables. Quizá dediquemos menos tiempo a escribir código repetitivo y más a revisar implementaciones generadas, evaluar compromisos y modificar la dirección arquitectónica. Esto cambia significativamente el trabajo de quienes programan, pero no equivale a convertir el código en una “caja negra”.
En lugar de pensar que las especificaciones sustituirán a los lenguajes de programación, una colega me ofreció otra interpretación: “piensa en ellas como un cambio en el patrón de interacción”.
En vez de escribir cada línea directamente, colaboramos con sistemas capaces de generar y modificar un primer borrador del código a partir de lenguaje natural. Sin embargo, eso no nos exime de comprender lo que finalmente ponemos en producción.
En todo caso, eleva el nivel de exigencia para entender qué constituye un buen diseño, porque ahora debemos construir sistemas que sigan siendo fáciles de modificar para ambos tipos de colaboradores. Creo que la conversación se vuelve más interesante cuando dejamos de preguntarnos si serán las personas o los agentes quienes escribirán el código y empezamos a preguntarnos qué significa realmente un “buen diseño” en un mundo donde ambos participan.
*Nota: en este artículo utilizo “buen código” y “buen diseño de software” de manera intercambiable porque considero que son conceptos en gran medida equivalentes. Para mí, un código limpio es la prueba más concreta de que el diseño de una aplicación es sensato y mantenible.
Gracias a mi colega Chris Ford por sus comentarios sobre los primeros borradores y por nuestras conversaciones sobre ingeniería agéntica.