Deuda técnica, deuda cognitiva y deuda de intención
(martinfowler.com)- En un entorno donde los LLM producen código en masa, no solo se debilitan los problemas del código en sí, sino también la comprensión compartida del equipo y el registro de los objetivos del sistema, por lo que crece la necesidad de abordarlo en tres capas: deuda técnica, deuda cognitiva y deuda de intención
- La deuda técnica limita la posibilidad de cambios futuros; la deuda cognitiva debilita la capacidad del equipo para razonar sobre cambios en el sistema; y la deuda de intención difumina el registro de objetivos y restricciones, dificultando la evolución continua tanto para humanos como para agentes de IA
- La teoría Tri-System que sitúa a la IA como System 3 distingue la rendición cognitiva (cognitive surrender), en la que se confía sin crítica en el razonamiento artificial externo y se omite la deliberación, de la delegación cognitiva estratégica (cognitive offloading)
- Cuanto más baja el costo de programar, más caro se vuelve verificar; además, los criterios de exactitud y éxito cambian según el contexto, como en ETA de tráfico, asignación de conductores u operación de cientos de microservicios, lo que vuelve más importante el juicio humano y el diseño de sistemas de verificación
- El eje central de la ingeniería se desplaza de la cantidad de implementación hacia la verificación, y en adelante los humanos seguirán encargándose de capturar el significado del sistema mediante abstracciones útiles, nombrado, definición de acceptance criteria y diseño de test harness
Tres tipos de deuda y la salud del sistema
- En un entorno donde los LLM generan grandes volúmenes de código, los equipos pueden perder con facilidad la comprensión de lo que hace el sistema, y está creciendo la visión de esto como deuda cognitiva
- Tres capas de salud del sistema divide la deuda en dimensiones de código, personas y artefactos
- La deuda técnica existe en el código, se acumula cuando las decisiones de implementación dañan la capacidad de cambio futuro y limita cómo puede evolucionar el sistema
- La deuda cognitiva existe en las personas, se acumula cuando la comprensión compartida del sistema se debilita más rápido de lo que puede reponerse y limita la capacidad del equipo para razonar sobre cambios
- La deuda de intención existe en los artefactos, se acumula cuando los objetivos y restricciones que deberían guiar el sistema no se registran ni se mantienen correctamente, y limita si se sigue reflejando lo que originalmente se quería construir, así como si humanos y agentes de IA pueden seguir evolucionando el sistema de manera efectiva
- Esta perspectiva incluye incluso secciones para diagnosticar y mitigar cada tipo de deuda, y también trata cómo las tres interactúan entre sí
- Los equipos deben realizar en conjunto actividades generales para mantener estas tres deudas bajo control
Teoría Tri-System y rendición cognitiva
- Un artículo que agrega los LLM al modelo de pensamiento de dos sistemas de Kahneman amplía el marco al ubicar a la IA como System 3
- System 1 toma decisiones rápidas por intuición, y System 2 corresponde a la etapa de pensamiento deliberado sobre un problema
- Para ahorrar energía, los humanos tienden por defecto a depender de la intuición, y como resultado pueden pasar por alto elementos que habrían detectado si hubieran reflexionado
- Como resultado de System 3 aparece la rendición cognitiva (cognitive surrender), definida como el estado en el que se confía sin crítica en el razonamiento artificial generado externamente y se elude System 2
- El artículo la distingue de la delegación cognitiva (cognitive offloading)
- La delegación cognitiva se aborda como el acto de delegar estratégicamente parte de la cognición dentro del proceso deliberativo
- El artículo desarrolla en detalle la Tri-System theory of cognition y, al menos en entornos de laboratorio, valida con varios experimentos qué tan útil es esta teoría para predecir el comportamiento
La notación <> como ícono de código
- Algunas ilustraciones recientes usan el símbolo “<>” como ícono de código, pero esta notación se siente extraña como forma de envolver elementos de un lenguaje de programación
- Que se use “<>” en vez de otros símbolos como “{ }” parece deberse a que remite a HTML o XML
- Cuando incluso se usa la forma “</>”, la asociación con HTML se vuelve más directa, pero HTML no se trata como un lenguaje con el que los programadores escriben programas
Cuando programar se abarata, lo caro es verificar
- if coding agents make coding free, what becomes the expensive thing sostiene que, cuanto más reducen los agentes de programación el costo de programar, más cara se vuelve la verificación
- Los criterios de exactitud cambian constantemente según el contexto
- Un algoritmo de ETA para el tráfico de Jakarta y uno para el tráfico de Ho Chi Minh City pueden tener significados distintos de lo “correcto”
- En la asignación de conductores hay que equilibrar al mismo tiempo la equidad en los ingresos, el tiempo de espera del cliente y la utilización de los vehículos, por lo que no existe una sola definición de “exitoso”, sino varias
- En un entorno donde cientos de ingenieros despliegan continuamente cerca de 900 microservicios, “correcto” no se divide en una sola definición sino en miles, todas cambiantes y dependientes del contexto
- Este tipo de juicio queda como una tarea que los agentes no pueden reemplazar
- Los agentes están funcionando especialmente bien en flujos donde existe una buena verificación, idealmente automatizada, sobre el resultado del trabajo
- Estos flujos fomentan Test Driven Development
- Como la cantidad de verificación que sigue siendo necesaria continúa siendo grande, hace falta más esfuerzo en formas de ayudar a las personas a comprender con facilidad un alcance de pruebas más amplio
- También se presentan algunas discrepancias sobre la modernización de sistemas legacy
- La expectativa de que el agentic coding resolverá por completo la modernización legacy está sobreestimada
- Aun así, hay evidencia sólida de que los LLM ayudan mucho en understanding what legacy code is doing
Reorganización hacia una organización centrada en la verificación
- Si los agentes se encargan de la ejecución, el trabajo humano se desplaza hacia el diseño de sistemas de verificación, la definición de calidad y el manejo de casos ambiguos que los agentes no logran resolver
- La estructura organizacional también debe cambiar para adaptarse a esto
- La pregunta del standup del lunes por la mañana pasa de “qué se desplegó” a “qué se verificó”
- En vez de seguir el volumen de producción, se pasa a seguir si el resultado fue correcto
- Un equipo de 10 ingenieros que antes construía funcionalidades podría reconfigurarse en 3 ingenieros y 7 personas encargadas de definir acceptance criteria, diseñar test harness y monitorear outcomes
- Esta reorganización puede resultar incómoda, porque reduce el estatus del acto de construir y eleva el del acto de juzgar
- Una cultura de ingeniería que no rechace este cambio tiene más probabilidades de hacerlo bien
El futuro del código fuente y los lenguajes amigables para LLM
- En la corriente que trata a los LLM como programadores, el propio futuro del código fuente surge como pregunta
- several views of the future of code reúne varias perspectivas sobre el futuro del código
- Algunos están experimentando con lenguajes completamente nuevos diseñados pensando en los LLM
- Otros creen que los lenguajes existentes, en especial los de tipos estrictos como TypeScript y Rust, pueden adaptarse mejor a los LLM
- El texto tiene muchas citas y poco análisis propio, pero vale como material panorámico para recorrer la discusión en su conjunto
Las abstracciones y el nombrado que siguen en manos humanas
- Incluso al crear software junto con LLM, los humanos seguirán a cargo de construir abstracciones útiles que permitan hablar de lo que hace el código
- Esto se conecta con el Ubiquitous Language de DDD
- growing a language aborda una forma de hacer crecer un lenguaje junto con LLM
- Programar no consiste solo en introducir sintaxis de código para que la computadora la entienda y ejecute, sino también en dar forma a una solución
- Implica dividir el problema en partes enfocadas, agrupar juntos los datos y comportamientos relacionados, y elegir nombres que revelen la intención
- Un buen nombre separa la complejidad y convierte el código en un esquema que todos pueden seguir, conectando con claridad la estructura del problema y la estructura de la solución
3 comentarios
El término deuda de intención es uno que definí a mi manera hace tiempo, así que me alegra volver a verlo así.
Supongo que al final todos pensamos de forma bastante parecida.
Entrada del blog: https://brunch.co.kr/@limjk/2
También me genera cierto rechazo que se haga una reestructuración organizacional rápida para ajustarse a los cambios; se siente un poco conservador.
Comentarios en Hacker News
Si eres del tipo de persona que necesita entender a fondo todo lo que despliega para quedarse tranquilo, este cambio puede sentirse bastante inquietante
Ahora, incluso un proyecto de modernización que antes tomaría 2 meses puede terminarse en alrededor de una semana si, en vez de meterse a fondo en toda la lógica de negocio para copiarla tal cual, te concentras en diseñar bien los límites y las interfaces
Y, como dice el artículo, basta con construir un buen test harness para verificar la equivalencia tanto como sea posible
Yo también tuve bastante conflicto al principio, pero pensándolo bien, tampoco conocía tan a fondo la implementación interna ni la lógica de negocio de otros sistemas que mantengo todos los días
Era código que escribí hace años y que casi no toqué, así que cuando había que cambiar algo, al final terminaba confiando en la suite de pruebas o siguiendo la estructura del código para entender cierto comportamiento
He visto muchas veces que, en etapas de recortes o reducción de costos, quienes tienen mucho de ese conocimiento sobreviven más que quienes no lo tienen
Me da curiosidad qué se me está escapando
No es que a los LLM les falte por completo la virtud de la pereza
Si les das un prompt base alineado con esa intención, he logrado que un agente basado en Claude minimice cambios de código, haga pasadas de deduplicación y adopte comportamientos que se sienten muy propios de un desarrollador senior
No creo que el modelo sea incapaz de aprender ese conocimiento; más bien, en la configuración por defecto no lo tiene tan al frente
Supongo que todos hemos visto modelos con un estilo de sobrecorrección que tocan todo el codebase sin control y no muestran ninguna preocupación por los cambios ajenos ni por el riesgo de pérdida de conocimiento
Lo de generar y validar documentación también se parece, al final, al problema tradicional de locking, y existen soluciones tradicionales
El agente puede leer git y entender suficientemente bien qué pasó primero y si, por convención, cierto cambio está esperando a otros
Yo también soy bastante senior, e incluso he trabajado en el mismo equipo que algunas de las personas mencionadas en el artículo
No creo que ellas cuestionaran mis estándares de ingeniería
Pero en mi flujo de trabajo con LLM casi no veo ese tipo de deuda; de hecho, siento que la calidad de los proyectos es mejor que hace 5 o 10 años, incluso usando los mismos estándares de antes
No hay nada mágico: solo hay que usar bien agentes que compartan esas prioridades de calidad
Y yo estoy logrando hacer mucho más trabajo real que perseguir atención en conferencias
Pero la parte de que incluso según métricas tradicionales de calidad de software esto es mejor que hace 5 o 10 años me suena demasiado vaga
Me gustaría saber con más detalle qué métricas usas y cómo compararías el código de hace 10 años, el de hace 5 y el de ahora
Sin esa explicación, siento que el mensaje se vuelve más difuso y se aleja del punto central
Si tienes más para compartir, sí me daría mucha curiosidad
Hasta podrías escribir un libro llamado Practical LLM Coding
Creo que lo más subestimado aquí es el marco de intent debt
La deuda cognitiva o la deuda técnica al menos dejan rastros visibles en el código, pero la intent debt no se ve
Por eso solo aparece cuando entra un cambio que parece razonable localmente pero está mal a nivel global
La restricción original no quedó registrada en ningún artefacto
El caso más difícil es cuando, en un sistema empresarial, esa restricción era un requisito regulatorio, cambió silenciosamente hace 3 años y nadie actualizó el código
Como las pruebas siguen pasando, es todavía más fácil que se escape
La suite de pruebas por sí sola no puede reconstruir la intención
No creo que Martin esté completamente equivocado, pero esta lógica parece una afirmación que siempre puede hacerse cada vez que subes un nivel de abstracción
Bajo su definición, pasar de Assembly a Python también generaría enormes cantidades de intent debt y cognitive debt
Porque dejaste de pensar directamente en cómo manipular bits y se lo delegaste al intérprete
Mi contraargumento es que esa intención técnica de la que habla existe porque había que traducir la intención humana a lenguaje máquina
Pensar profundamente en el problema no requiere necesariamente construir abstracciones dirigidas por el dominio dentro del código
Puedes pensar a fondo haciendo mapas mentales, escribiendo en un diario o pegando post-its en la pared
La abstracción orientada a objetos no es magia
En ese proceso suelen salir a la luz ambigüedades, detalles no considerados e incluso la necesidad de replantear todo el enfoque
Es cierto que escribir en lenguaje natural también puede ser una herramienta de pensamiento, pero hay algo esencialmente distinto en forzar el pensamiento a encajar en un lenguaje formal que no tolera ambigüedad
Es parecido a hacer matemáticas solo con lenguaje natural, sin notación matemática: se vuelve más engorroso y más propenso a errores
Solo que se hace en un lenguaje más fácil de entender para humanos
Existe una correspondencia directa entre intención e implementación
Porque la correspondencia está bien definida y es determinista
El propósito de la abstracción es permitirte confiar en que lo que hiciste sigue siendo correcto sin necesidad de volver a mirar debajo
Eso es posible porque yo, o alguien en quien confío, ya pagó ese costo una vez
Pero con los LLM tienes que validar la salida cada vez que generan algo, y esa deuda hay que volver a pagarla cada vez
Por eso cuesta verlo como una abstracción
El artículo está realmente muy bien escrito
Justo ayer anoté en mis notas personales que, si no sigues evolucionando orgánicamente el código, es difícil decir que realmente lo posees
Antes era como un auto autónomo donde al menos recuerdas el paisaje del trayecto; ahora se siente más como si te teletransportaran a otro lugar y solo te enseñaran la grabación
Revisar así es menos efectivo
En herramientas pequeñas este tipo de ghosted code puede estar bien, pero en sistemas como una base de datos sí me preocupa de verdad
Por eso ahora casi no les doy permisos de escritura a los agentes y he vuelto a un enfoque de QA manual, como hace 2 años
Incluso en uso de tokens y en resultados, me ha salido más eficiente así
Claro, esto es solo mi experiencia
Por desgracia, buena parte del paper de Wharton que enlazó parece generado por IA, y además todavía no ha pasado por peer review
Entiendo que hoy muchos investigadores usan ayuda de IA para escribir, pero si el tema del paper es precisamente la cognitive surrender, se vuelve difícil tomarlo en serio
not merelynada menos que 7 vecesNo sé si un LLM repetiría tanto la misma expresión, aunque también podría ser que el autor ya haya adquirido un hábito de escribir como LLM
Martin no está del todo equivocado, pero yo sí he visto casos donde la IA produjo código perezoso, y la respuesta correcta en realidad era tener más código
En concreto, había modelos de Python que definían esquemas de base de datos para cierto conjunto de conceptos lógicos
Se agregó un concepto nuevo muy parecido a los existentes, y Claude decidió que bastaba con reutilizar el conjunto de modelos que ya estaba
En teoría funcionaba, pero del lado del consumidor terminamos necesitando toda clase de rodeos por la inferencia de tipos en runtime
Funcionaba, sí, pero era un caso de haber elegido completamente mal el nivel de abstracción
Los humanos queremos abstracción, pero a veces la repetición termina siendo la opción correcta
Si una máquina va a escribir y mantener el código, quizá esas capas extra de abstracción que antes hacían falta ya no siempre sean necesarias
Antes usábamos cosas como Duff's device y hacíamos unrolling manual de loops para generar código duplicado a propósito
Ahora el compilador entiende la intención, genera el ensamblado repetido y nosotros ya no tenemos que preocuparnos por esa duplicación
Hace poco necesité algunas piezas no triviales de código de geometría computacional con LLM, y antes eso habría implicado buscar una librería, pasar por aprobación de compliance y transformar mis estructuras de datos de dominio al formato de esa librería
Eso seguía siendo más barato que implementarlo a mano, pero de ningún modo era trivial
Ahora el LLM puede escribirme solo la parte que necesito y hacer que use directamente el formato de datos que ya guardo
No necesito meter una librería grande ni transformar estructuras de datos
La respuesta de manual sería usar una geometry library para evitar duplicación, pero aquí una sola función autocontenida simplemente funciona bien
Esto fue fácilmente de lo mejor que he leído en Hacker News en muchísimo tiempo
Me identifiqué demasiado
Entre la idea de cognitive debt de Simon Willison y la postura de que “tu trabajo es desplegar solo código cuyo funcionamiento hayas demostrado”, terminé trabajando en proyectos del tipo Intent-Driven Development
La intención original siempre parecía desvanecerse conforme se acumulaban los cambios
Quizá termine formalizando esto como un protocolo real y publicándolo luego en Hacker News
Si todavía no has visto mis escritos, te recomendaría echarles un vistazo
En resumen, la idea es esta
La visualización que tengo ahora de este problema se parece más a este diagrama: https://excalidraw.com/#json=y1fSSx2z8-0nFs7CDnqhp,d9Di8JdGU...
Creo que el cuello de botella cognitivo en ingeniería de software no está tanto dentro del código sino entre los artefactos
El código es solo uno de ellos
outcome → requirements → spec → acceptance criteria → executable proof → review
Estoy construyendo tooling experimental para automatizar las partes tediosas entre esas transiciones y dejar que los humanos se concentren en verificar que la intención haya sobrevivido en cada etapa
Yo agregaría una capa más ahí: proxies/metrics
En sistemas con mucha carga analítica, muchas veces la pérdida real no ocurre de spec → code sino de question → proxy
Una vez que el proxy queda incrustado en acceptance criteria, dashboards o evals, la gente empieza a optimizar eso y poco a poco se olvida de que solo era una métrica sustituta
En el teléfono, al intentar hacer pan, zoom o scroll, termino moviendo elementos del canvas por accidente