Be Software Architecture Wise
Be Wise Apps

Read in English · Lire en français · Auf Deutsch lesen

Sección 1.7 de 1.8 · Intermedio · 20 min de lectura

Deuda técnica, erosión y deriva de la arquitectura

Videolección

4 min · narrado en inglés, con subtítulos
Solo escuchar (audio)
Leer la transcripción

Todos los equipos toman atajos, y todos los diseños envejecen a medida que aprendes más. Esta lección explica la deuda técnica: qué significa realmente, cómo verla y cómo decidir qué vale la pena saldar.

Imagina que te mudas de casa con prisa y lo metes todo en cajas sin etiquetas. Te mudaste a tiempo, que era lo importante. Pero ahora, cada vez que necesitas algo, pierdes diez minutos buscándolo. Esos minutos perdidos son los intereses. Desempacar y etiquetar bien es saldar la deuda. Un poco de esto está bien, y a veces es sensato. El peligro es dejar que se acumule.

Ward Cunningham acuñó el término en 1992. Entregar código por primera vez es como endeudarse: un poco te acelera, siempre que lo pagues pronto. Más tarde explicó que nunca fue una licencia para escribir código chapucero. Su argumento trataba del entendimiento. Entregas código que refleja lo que sabes ahora y, a medida que aprendes más, lo refactorizas para que coincida. Eso solo funciona si el código está lo bastante limpio para refactorizarlo.

Las palabras financieras ayudan. El principal es el esfuerzo necesario para corregir el problema. El interés es el esfuerzo adicional que pagas en cada cambio que lo toca. Si una funcionalidad llevaría cuatro días en un código limpio pero lleva seis, los dos días adicionales son intereses. Así que la deuda en código que nunca cambia no cuesta nada. Saldarla es una decisión de inversión: vale la pena cuando los intereses que te ahorrarás superan el principal.

Martin Fowler clasifica la deuda según dos ejes: ¿se contrajo de forma deliberada o inadvertida, y fue imprudente o prudente? Prudente y deliberada: hay que entregar ya y asumir las consecuencias. Es consciente y planeada, con un plan de pago, y puede ser un buen negocio. Imprudente y deliberada: no tenemos tiempo para diseñar. El equipo sabe hacerlo mejor, pero toma atajos igualmente, y normalmente se frena a sí mismo en cuestión de semanas. Imprudente e inadvertida: ¿qué es una capa? El equipo no ve la deuda que está creando. El remedio son las habilidades, la revisión y la mentoría, no solo la refactorización. Prudente e inadvertida: ahora sabemos cómo debimos hacerlo. Todo equipo que aprende acaba aquí, y por eso la refactorización continua es trabajo normal, no una señal de fracaso.

La deuda de código vive dentro de un módulo, y a menudo puede saldarse en horas o días. La deuda arquitectónica vive en la estructura, como en unos servicios que comparten una base de datos. Saldarla afecta a muchos equipos, y a menudo a los datos, así que es la más costosa. Perry y Wolf nombraron dos formas en que se degrada la estructura. La erosión viene de las violaciones, como un controlador que consulta la base de datos directamente, saltándose la capa de servicios. La deriva viene de la falta de atención: cambios que no rompen ninguna regla pero ignoran los conceptos previstos, de modo que el sistema pierde poco a poco su forma.

¿Cómo encuentras la deuda que importa? Una técnica sitúa cada archivo según la frecuencia con que cambia y su complejidad. Los archivos complejos que rara vez cambian cuestan pocos intereses. Déjalos como están por ahora. Los archivos sencillos que cambian a menudo suelen estar bien. Pero donde coinciden la complejidad y los cambios frecuentes, pagas intereses en cada commit. Estos hotspots son donde saldar la deuda rinde antes.

Para defender el caso, una tienda en línea mide la deuda de un módulo que comparten los pedidos y los pagos. Cambió cuarenta y ocho veces en seis meses: es el principal hotspot. El esfuerzo adicional en cada cambio, más los incidentes, suma unos catorce días-desarrollador al mes. Eso son unas ocho mil cuatrocientas libras. Separar los pedidos de los pagos, en pasos pequeños, costaría unos cuarenta y cinco días-desarrollador, unas veintisiete mil libras. Con unos once días ahorrados cada mes, se recupera en unos cuatro meses. Incluso duplicando el costo, se recupera en unos ocho. Ese es un argumento en los términos del patrocinador: días, dinero y recuperación, no calidad del código.

Eso es la deuda técnica. La sección completa trata más formas de detectarla y cómo gestionarla con un registro de deuda, una capacidad protegida y la refactorización sobre la marcha. A continuación, en la Sección uno punto ocho, veremos la arquitectura a lo largo del ciclo de vida. Nos vemos allí.

Lo que necesitas saber

Lo que quería decir realmente Ward Cunningham

El término deuda técnica procede del informe de experiencia de Ward Cunningham sobre el sistema de gestión de carteras WyCash, presentado en la conferencia OOPSLA en 1992. Escribió que entregar código por primera vez es como endeudarse: un poco de deuda acelera el desarrollo, siempre que se pague pronto con una reescritura, y el peligro llega cuando no se paga: Cada minuto dedicado a un código que no es del todo correcto cuenta como interés de esa deuda. Organizaciones de ingeniería enteras, advirtió, pueden quedar paralizadas bajo el peso de la deuda.

La metáfora se malinterpreta a menudo como un permiso para escribir código chapucero. Cunningham explicó más tarde (en un breve video de 2009) que nunca quiso decir eso. Su argumento trataba del entendimiento. Un equipo entrega código que refleja su entendimiento actual y parcial de un problema, porque esa es la forma más rápida de aprender del uso real. A medida que el entendimiento mejora, el código deja de coincidir con lo que el equipo sabe ahora, y cada cambio tiene que sortear ese desajuste. Saldar la deuda significa aplicar refactorización al código para que refleje lo aprendido. Eso solo funciona si, para empezar, el código se escribió con la limpieza suficiente para poder refactorizarlo. Según esta lectura, la deuda es un subproducto del aprendizaje, no del descuido.

Desde entonces, el uso se ha ampliado. Hoy "deuda técnica" abarca cualquier carencia de calidad interna que haga que los cambios futuros sean más lentos o más arriesgados, tanto si surgió del aprendizaje como de atajos deliberados, del desconocimiento o de la simple dejadez. El significado amplio es útil siempre que recuerdes que los distintos tipos requieren respuestas distintas.

El principal y los intereses

El vocabulario financiero es lo que hace que la metáfora sea tan eficaz en las conversaciones con personas que no son ingenieros. El principal es el esfuerzo necesario para corregir el problema: reestructurar el módulo, separar la base de datos, eliminar el ciclo. El interés es el esfuerzo adicional que se paga en cada trabajo que toca el problema mientras este persiste. El ejemplo de Martin Fowler: si una funcionalidad llevaría cuatro días en un código limpio pero lleva seis por culpa de la suciedad acumulada, los dos días adicionales son intereses.

De ello se derivan tres consecuencias. Primera, la deuda que nunca se toca no cuesta nada: un módulo desordenado que nadie cambia no acumula intereses, así que saldarla puede ser un esfuerzo desperdiciado. Segunda, los intereses se capitalizan: los parches se convierten en la base de más parches, los nuevos desarrolladores copian los patrones que ven y el costo de la reparación final aumenta. Tercera, saldar la deuda es una decisión de inversión: vale la pena pagar el principal cuando los intereses que te ahorrarás en un horizonte razonable lo superan. Eso convierte un argumento emocional ("el código es un desastre") en uno económico ("este módulo nos cuesta once días por trimestre").

El cuadrante de la deuda técnica de Fowler

No toda la deuda es igual. En 2009, Martin Fowler propuso clasificar la deuda según dos ejes: si se contrajo de forma deliberada o inadvertida, y si la decisión fue prudente o imprudente. Las cuatro combinaciones requieren respuestas distintas.

Prudente y deliberada: el equipo conoce el mejor diseño, juzga que entregar ahora compensa el costo y planea saldarla. Es el sentido original de Cunningham, y puede ser un buen negocio: una startup que demuestra que hay demanda antes de invertir en escala. Imprudente y deliberada: el equipo sabe hacerlo mejor, pero se salta el diseño porque cree que no tiene tiempo, y normalmente se frena a sí mismo en cuestión de semanas. Imprudente e inadvertida: el equipo no sabe lo suficiente para ver que está creando deuda; el remedio son las habilidades, la revisión y la mentoría, no solo la refactorización. Prudente e inadvertida: un equipo competente hace un buen trabajo y solo más tarde entiende cómo debería haberlo estructurado. Fowler sostiene que esto es inevitable en cualquier equipo que aprende, y por eso la refactorización continua forma parte del trabajo normal y no es una señal de fracaso.

El cuadrante de la deuda técnica

Cuadrante de la deuda técnica de Martin Fowler: imprudente o prudente, deliberada o inadvertidaImprudentePrudenteDeliberadaInadvertida“No tenemos tiempopara diseñar.”Lo sabe, perotoma atajos igual“Hay que entregar ya yasumir las consecuencias.”Consciente, planeada,con un plan de pago“¿Qué es una capa?”Le falta conocimientopara ver la deuda“Ahora sabemos cómodebimos hacerlo.”Aprender revelaun diseño mejor
Según Martin Fowler (2009): los dos cuadrantes imprudentes son problemas de disciplina o de habilidad; los dos prudentes son consecuencias normales de entregar en condiciones de incertidumbre.

Deuda arquitectónica frente a deuda de código

La deuda existe en todos los niveles, pero su perfil de costo cambia con la escala. La deuda de código vive dentro de un módulo: lógica duplicada, una función que hace cinco cosas, pruebas que faltan, nombres poco claros. Es real, pero es local; un solo desarrollador suele poder saldarla en horas o días, a menudo mientras trabaja en una funcionalidad cercana. La deuda arquitectónica vive en la estructura: servicios que comparten una base de datos, de modo que ninguno puede cambiar su esquema por sí solo; un dominio dividido por las líneas equivocadas, de modo que cada funcionalidad afecta a cuatro equipos; una cadena de llamadas síncronas que hace que un servicio lento tumbe el proceso de pago; un framework soldado a cada capa. Saldarla afecta a muchos módulos, a menudo a muchos equipos y, a veces, a los datos.

La investigación respalda la intuición de que la deuda estructural es la costosa. Una encuesta de 2015 a 1831 ingenieros y arquitectos, realizada por Neil Ernst y sus colegas del Software Engineering Institute, concluyó que las decisiones arquitectónicas eran la fuente de deuda técnica más importante de las señaladas, por delante de los problemas a nivel de código. Las herramientas que detectan code smells son buenas para encontrar deuda de código y malas para encontrar deuda arquitectónica, porque el problema está en las relaciones entre las partes y no en un archivo concreto.

La deuda arquitectónica importa por la misma razón por la que importa la arquitectura: como mostró Por qué importa la arquitectura, las decisiones estructurales son las que tienen el mayor costo de cambio.

Erosión y deriva

La deuda de arquitectura a menudo no se acumula por una mala decisión, sino por miles de pequeños cambios. Dewayne Perry y Alexander Wolf, en su artículo de 1992 "Foundations for the Study of Software Architecture", nombraron dos formas en que esto ocurre. La erosión de la arquitectura la causan las violaciones de la arquitectura: un controlador que consulta la base de datos directamente, saltándose la capa de servicios; un módulo que se mete en las tablas internas de otro. Cada violación hace el sistema más frágil, como quitar un muro de carga. La deriva de la arquitectura la causa la falta de atención a la arquitectura: cambios que no rompen ninguna regla explícita, pero ignoran los conceptos previstos, de modo que el sistema pierde poco a poco coherencia y claridad de forma. La deriva es menos peligrosa de inmediato que la erosión, pero oscurece la arquitectura, lo que a su vez facilita las violaciones.

Investigadores posteriores suelen usar "erosión" para la brecha más amplia entre la arquitectura prevista (lo que dicen los documentos y la cabeza del arquitecto) y la arquitectura implementada (lo que hace realmente el código). Sean cuales sean los términos, las causas son conocidas:

La presión de los plazos, que premia el camino más corto por encima del camino correcto. La pérdida de conocimiento, cuando se van las personas que entendían el diseño y el personal nuevo copia lo que encuentra. La arquitectura invisible: reglas que solo viven en una wiki que nadie lee, sin nada en la compilación que las haga cumplir. Los requisitos cambiados, que hacen que una estructura antes buena encaje mal con el problema. El cambio organizativo: por la ley de Conway, una reorganización crea presión para que el sistema se remodele según las nuevas líneas de los equipos. La tecnología que envejece: los frameworks y las plataformas que se quedan sin soporte se convierten en deuda sin que nadie toque el código.

Detectar la deuda

La deuda que no se ve no se puede gestionar, y la intuición no es fiable: los desarrolladores tienden a quejarse más del código que más tocan, lo cual es útil, pero rara vez saben cuánto cuesta cada problema. Varias técnicas hacen visible la deuda.

Hotspots (puntos calientes). Your Code as a Crime Scene (2015; segunda edición, 2024), de Adam Tornhill, aplica ideas forenses al historial del control de versiones. Un hotspot es código que es a la vez complejo y que cambia a menudo. La complejidad por sí sola no basta: un archivo complejo que nadie toca no cuesta intereses. La frecuencia de cambio por sí sola tampoco basta: un archivo de configuración sencillo puede cambiar a diario sin causar daño. Donde ambas coinciden, se pagan intereses en cada commit, y ahí es donde saldar la deuda rinde antes. En la mayoría de las bases de código, una pequeña fracción de los archivos concentra la mayor parte de la actividad de cambio.

Acoplamiento de cambio. Los archivos o servicios que cambian una y otra vez en los mismos commits están acoplados, tanto si el código muestra una dependencia como si no. Si cada cambio en el servicio de precios va acompañado de un cambio en el servicio de facturación, son una sola cosa que finge ser dos. El acoplamiento de cambio entre los límites de equipos o de servicios es una señal clara de deuda arquitectónica.

Ciclos de dependencias y violaciones de reglas. El análisis estático puede dibujar el grafo de dependencias real y compararlo con el previsto. Los ciclos (A usa B, B usa C, C usa A) significan que ninguno de los tres puede entenderse, probarse ni lanzarse por separado. Las reglas de arquitectura ("la capa de dominio no debe depender de la capa web") pueden comprobarse automáticamente en cada compilación; son funciones de aptitud (fitness functions), que se tratan en el Capítulo 11.

Tendencias de la entrega. El aumento del tiempo de entrega de los cambios, más defectos que se escapan en cada versión, una incorporación de personal más larga, más reuniones de coordinación por funcionalidad y una creciente reticencia a tocar ciertas áreas son indicadores rezagados de los intereses acumulados. Las métricas de entrega DORA (como el tiempo de entrega de cambios y la tasa de fallos de los cambios) ofrecen una forma coherente de seguir algunos de ellos a lo largo del tiempo.

Encontrar hotspots

Dispersión de archivos según frecuencia de cambio y complejidad, con hotspots en la esquina superior derechaFrecuencia de cambio (commits en los últimos 12 meses)AltoBajoComplejidadHotspots: empieza aquíComplejo y estable:déjalo por ahoraSimple y activo:suele ir bien
Situar cada archivo según la frecuencia con que cambia y su complejidad muestra dónde se pagan intereses con más frecuencia: los pocos archivos de la esquina superior derecha.

Gestionar la deuda

Ninguna base de código sana tiene cero deuda, e intentar llegar a cero es en sí un desperdicio. El objetivo es que la deuda sea visible, deliberada y asumible. Los equipos que lo hacen bien combinan varias prácticas.

Un registro de deuda. Anota los elementos de deuda importantes donde el equipo planifica su trabajo: cuál es el problema, dónde está, cómo surgió, el principal estimado, los intereses estimados (con pruebas como los datos de hotspots o las incidencias recientes) y el desencadenante para saldarla. Un registro convierte las quejas vagas en una lista que puede priorizarse junto con las funcionalidades.

Una parte fija de la capacidad. Muchos equipos reservan una proporción estable de cada iteración, normalmente entre el 10 y el 20 por ciento, para saldar deuda y otros trabajos de mejora. No hay pruebas de que exista un número mágico; lo que importa es que esa parte esté protegida y no desaparezca cada vez que se acerca una fecha límite.

Refactorizar junto con las funcionalidades. El momento más barato para saldar deuda es cuando ya estás trabajando en esa área, porque tienes el contexto en la cabeza y las pruebas en marcha. Fowler lo llama refactorización oportunista. Su forma cotidiana es la regla del boy scout, popularizada por Robert C. Martin: deja el código un poco más limpio de lo que lo encontraste. Como los hotspots son, por definición, donde aterrizan las funcionalidades, esto concentra de forma natural el esfuerzo donde los intereses son más altos.

Pago estructural planificado. La deuda arquitectónica rara vez cede solo con pasos pequeños. Necesita un trabajo explícito y financiado, con un objetivo, un plan para migrar por incrementos y el apoyo de las partes interesadas. Técnicas como el patrón strangler fig (Capítulo 11, Modernizar sistemas heredados) permiten a un equipo sustituir una estructura pieza a pieza sin una reescritura big-bang.

Prevención. Las comprobaciones automáticas de la arquitectura en la compilación, una revisión de código que mira la estructura además del estilo y unos registros de decisiones actualizados frenan la erosión y la deriva en su origen.

Explicar la deuda a las partes interesadas no técnicas

A los patrocinadores y a los responsables de producto no les hace falta oír hablar de dependencias cíclicas; necesitan entender el efecto sobre lo que les importa: velocidad, costo, riesgo y experiencia del cliente. Aquí la metáfora financiera es un regalo, siempre que la uses con cifras.

Habla en términos de tiempo y dinero: "Cada cambio en el proceso de pago nos cuesta unos tres días-desarrollador adicionales por cómo están enredados los pagos y los pedidos". Habla en términos de riesgo: "Dos de nuestros últimos cuatro incidentes empezaron en este módulo". Habla en términos de opciones: "Hasta que separemos esto, no podemos ofrecer los nuevos métodos de pago que ha pedido el equipo de ventas". Y presenta el pago de la deuda como una inversión con un periodo de recuperación, no como una súplica para conseguir tiempo de ingeniería. Evita dos trampas: presentar la deuda como un fallo moral de quienes estuvieron antes (normalmente es prudente o inadvertida) y pedir un "sprint de refactorización" sin límite y sin un resultado medible.

Ejemplo práctico

Ejemplo práctico: estimar los intereses de una deuda

La lógica de pedidos y de pagos de una tienda en línea comparte un gran módulo y las mismas tablas de la base de datos. El equipo sospecha que esto le cuesta mucho. Reúne pruebas de las incidencias y del historial del control de versiones de los últimos seis meses, y luego hace una estimación.

Cambios que tocan el módulo48 cambios en seis meses, unos 8 al mes (es el principal hotspot).
Esfuerzo adicional por cambioComparando con cambios similares en otras partes, los desarrolladores estiman unos 1.5 días adicionales por cambio para entender el código, sortear las tablas compartidas y hacer pruebas de regresión adicionales.
Intereses en esfuerzo8 cambios × 1.5 días = 12 días-desarrollador al mes.
Intereses por defectos3 incidentes en producción en seis meses atribuidos a este módulo, cada uno con un costo de unos 4 días-desarrollador para investigarlo y corregirlo: 12 días ÷ 6 = 2 días al mes.
Intereses totalesUnos 14 días-desarrollador al mes. Con un costo total de £600 por día-desarrollador, aproximadamente £8400 al mes, o unos £100 000 al año.
PrincipalSeparar los pedidos de los pagos detrás de una interfaz y dividir las tablas, de forma incremental: 45 días-desarrollador estimados, unas £27 000.
Intereses residuales tras saldar la deudaLos desarrolladores esperan que quede cierta sobrecarga: 3 días-desarrollador al mes estimados.
RecuperaciónAhorro mensual de unos 11 días-desarrollador. 45 ÷ 11 son unos 4 meses para recuperar el principal.
RecomendaciónSaldarla, en incrementos de unas dos semanas cada uno, empezando por las tablas que intervienen con más frecuencia en los incidentes. Seguir el tiempo de entrega de los cambios en el proceso de pago para confirmar el ahorro.

Cada cifra es una estimación con amplios márgenes de error. Duplicar el principal hasta 90 días sigue dando una recuperación en unos ocho meses, así que la conclusión es sólida; vale la pena mostrar esa comprobación a las partes interesadas.

El mismo método puede mostrar que no vale la pena saldar cierta deuda: un módulo desordenado que se cambia dos veces al año puede tener un periodo de recuperación de varios años.

El argumento se presenta en los términos del patrocinador (días, dinero, incidentes, recuperación), no en términos de calidad del código.

Comprueba lo que has entendido

¿Por qué un módulo muy desordenado puede tener poca prioridad a la hora de saldar su deuda?

Porque los intereses solo se pagan cuando el código se cambia o causa fallos. Si nadie lo toca y es estable, la deuda no cuesta casi nada.

¿Por qué la deuda imprudente e inadvertida no se cura solo con refactorización?

Porque el equipo no sabía que estaba creando deuda; a menos que mejoren sus habilidades y sus prácticas de revisión, seguirá creando el mismo tipo de deuda tan rápido como la salda.

¿Cuál es la diferencia entre la erosión y la deriva en los términos de Perry y Wolf?

La erosión es el resultado de violar las reglas de la arquitectura; la deriva es el resultado de cambios hechos sin tener en cuenta la arquitectura, que no rompen ninguna regla explícita pero desdibujan sus conceptos.

¿Por qué los análisis de hotspots combinan la frecuencia de cambio con la complejidad en lugar de usar cualquiera de las dos por separado?

El código complejo que nunca cambia acumula pocos intereses, y el código sencillo que cambia a menudo es barato de cambiar; solo la combinación muestra dónde la deuda está costando esfuerzo una y otra vez.

Siguiente · 1.8Arquitectura y ciclo de vidaEl gran diseño inicial (big design up front) frente al diseño emergente, lo que dice realmente el Manifiesto Ágil, el modelo dirigido por riesgos de Fairbanks, la arquitectura intencional y la pista arquitectónica (runway), la arquitectura continua, los esqueletos andantes, los spikes y la arquitectura en DevOps.Continuar →

Fuentes y lecturas recomendadas

¿Tienes una pregunta sobre esta sección? ArchitectBot, el asistente de estudio con IA del curso, responde primero a partir del curso.

Pregunta: “¿Cómo convenzo a mi responsable de producto para que dedique tiempo a saldar deuda arquitectónica cuando hay una larga lista de funcionalidades pendientes?”