- Riot Games considera la deuda técnica como código o datos cuyo costo pagarán futuros desarrolladores, y a partir de casos de desarrollo de League of Legends organiza un lenguaje común para evaluar esa deuda
- Los criterios de evaluación son tres: impacto, costo de corrección y contagio; en particular, el contagio indica en qué medida la deuda se propaga con el tiempo a otros sistemas, datos y prácticas de desarrollo
- Los tipos de deuda se dividen en Local Debt, confinada a la implementación interna; MacGyver Debt, que conecta temporalmente dos sistemas; Foundational Debt, donde supuestos profundos quedan incrustados en la estructura; y Data Debt, donde el contenido se acumula sobre defectos
- Cataclysm de Jarvan, el uso paralelo de std::string y AString, el uso de Lua en BlockBuilder y el block parameter naming bug se usan como ejemplos concretos de cada tipo
- La deuda con bajo contagio puede estar bien aunque permanezca mucho tiempo, pero la deuda con alto contagio aumenta su costo de corrección y su impacto con el paso del tiempo, por lo que hay que cortar temprano sus vías de propagación
Tres ejes para evaluar la deuda técnica
- La deuda técnica es “código o datos cuyo costo pagarán futuros desarrolladores”
- Para decidir si conviene corregir una deuda específica ahora, más tarde o dejarla de manera realista, hace falta un criterio de medición común
- Riot evalúa la deuda técnica en tres ejes
- impact: impacto sobre jugadores y desarrolladores
- fix cost: tiempo necesario para corregirla y riesgo de despliegue
- contagion: cuánto se propaga el problema si se lo deja como está
-
impact: el costo visible para jugadores y desarrolladores
- Para los jugadores aparece como bugs, funciones faltantes o comportamientos inesperados
- Para los desarrolladores se acumula como demoras de implementación, obstáculos en el flujo de trabajo y detalles innecesarios que deben recordar
- Aquí, “desarrolladores” no solo incluye ingenieros, sino también roles involucrados en la creación del juego, como diseñadores y artistas de VFX
- Algunas deudas impiden que los ingenieros escriban código nuevo, mientras que otras dificultan que los diseñadores escriban nuevos scripts o que los artistas de VFX creen nuevas partículas
-
fix cost: tiempo de implementación y riesgo de despliegue
- El costo de corrección incluye no solo el tiempo real de desarrollo, sino también el riesgo que surge al desplegar los cambios
- Un error simple en una sola función puede corregirse en minutos, pero un supuesto profundo que afecta toda la línea de código del juego puede tomar semanas o meses
- Incluso un sistema “incorrecto” puede estar usándose ya como herramienta para crear un buen juego, por lo que al corregirlo el contenido existente puede romperse
- Por ejemplo, cambiar la forma en que el motor de scripting maneja errores o la forma en que calcula el tiempo de creación de partículas puede afectar más de 500 habilidades de más de 140 campeones
-
contagion: cuánto se propaga con el tiempo
- contagion significa cuánto se transmite la deuda técnica a otros sistemas, datos o formas de desarrollo cuando se la deja como está
- La propagación ocurre a través de interfaces con sistemas problemáticos, la copia y pegado de datos acumulados encima de ellos, y cambios en la forma de implementar nuevas funciones
- Una deuda bien aislada no tiene una gran diferencia de costo entre corregirla más adelante y corregirla ahora
- La deuda con alto contagio se vuelve cada vez más difícil de corregir con el tiempo, y a medida que más sistemas se infectan con el compromiso central, el impacto también crece
Local Debt: una caja negra desordenada solo por dentro
- Local Debt se parece al modelo clásico de programación de caja negra
- Vista desde afuera, el sistema funciona de manera estable, pero la implementación interna puede ser terrible o confusa
- Ej.: habilidades, capa de red, motor de scripts
- Si al desarrollar sistemas circundantes no hace falta tener presente la deuda interna, su contagio suele ser bajo
-
Analogía del mundo real: el ojo humano
- Por su estructura, el ojo humano recibe las imágenes invertidas, y los nervios de la retina crean un punto ciego cerca del centro de cada ojo
- El centro visual del cerebro invierte los datos y rellena el punto ciego para que el resto del cerebro interactúe con una imagen “correcta”
- Esta particularidad está localizada en el sistema del ojo y el nervio óptico, y otros sistemas pueden evitarla con facilidad, por lo que es un estado “suficientemente bueno”
-
Caso de League: Cataclysm de Jarvan
- Cataclysm de Jarvan sigue estando hecho con minions hasta el día de hoy
- Cuando los diseñadores quieren asociar un efecto de gameplay a una ubicación específica o a un conjunto de ubicaciones, pueden usar una herramienta que crea un “minion invisible”
- RiotXypherous explica en un comentario de Reddit el “minion” al que se refiere aquí
- Este tipo de objeto de juego es una forma estable y bien entendida de rastrear y ejecutar lógica de scripts
- La pared de Jarvan necesita exactamente 24 minions para impedir que los jugadores escapen
- Antes eran 12, pero como a veces los jugadores se escapaban entre las paredes, Riot Exgeniar lo aumentó a 24
- La alternativa es una estructura de ring-terrain, una única pieza lógica que controla la pathability de Cataclysm, que permitiría ordenar la lógica y reducir ligeramente el costo de cálculo
-
Evaluación de Cataclysm
- impact: 1/5
- El hecho de que la pared esté hecha con minions casi no afecta a otros desarrolladores que crean contenido nuevo
- “Jarvan Ult Hitch” fue resultado de la combinación de esta deuda con un bug de carga que intentaba leer una definición de auto-attack faltante
- fix cost: 2/5
- Actualmente no se puede crear geometría personalizada con formas compuestas sin código nuevo
- Para crear un “area trigger” en forma de anillo haría falta código matemático dedicado para el cálculo de colisiones del anillo
- Riot está explorando Constructive Solid Geometry para otros fines, y eso podría reducir mucho el costo de corrección
- contagion: 1/5
- Está bien aislado, porque al desarrollar funciones no hace falta considerar la implementación de la pared de Jarvan
- El riesgo de contagio aparece si otros diseñadores copian y pegan esta implementación en nuevos campeones, algo que de hecho ha ocurrido ocasionalmente
- Como problema de implementación, el potencial de propagación de Cataclysm es bajo y bien comprendido
- impact: 1/5
-
Cómo tratar Local Debt
- Una característica típica de Local Debt es una puntuación baja de contagion
- Si el impacto es mayor que el fix cost, los desarrolladores que actúan como buenos ciudadanos tienden a corregirla antes de que pase demasiado tiempo
- Si realmente no es contagiosa, es seguro dejarla todo el tiempo que sea necesario
- Lanzarse de inmediato sobre una Local Debt que despierta el perfeccionismo de los ingenieros, pero no tiene un impacto lo bastante amplio, es uno de los grandes errores
- Como el alcance del cambio es local, la verificación de la corrección y las pruebas de regresión suelen ser fáciles
- Entre los ejemplos corregidos recientemente se incluyen bugs relacionados con inhibitors, Janna’s Monsoon y Tear of the Goddess
- Un bug que, en ciertas situaciones, hacía que un inhibitor hiciera pathing de un campeón hacia las coordenadas 0,0,0
- Un problema por el que Janna’s Monsoon ignoraba spell shield
- Un problema por el que Tear of the Goddess acumulaba cargas con lanzamientos sin maná
Deuda MacGyver: cuando dos sistemas están pegados con cinta adhesiva
- Deuda MacGyver es un tipo de deuda que toma su nombre del programa de TV MacGyver de mediados de la década de 1980
- En el contexto de la deuda técnica, se refiere a cuando dos sistemas en conflicto están “pegados con cinta adhesiva” en puntos de interfaz a lo largo de toda la base de código
-
Analogía del mundo real: Seattle
- En el pasado, Seattle tenía dos asentamientos competidores, cada uno con su propia cuadrícula
- A medida que esos dos asentamientos crecieron hasta convertirse en la moderna Emerald City, las cuadrículas ligeramente distintas se fusionaron, lo que dio lugar a manzanas y edificios de formas incómodas, y a un uso ineficiente del espacio
-
Caso de League: std::string y AString
- En la base de código de League conviven std::string de C++ y la clase personalizada AString de Riot
- Ambas son formas de almacenar, modificar y pasar cadenas de texto
- Riot considera que std::string provoca muchas asignaciones de memoria “ocultas” y costos de rendimiento, y facilita escribir mal código
- AString fue diseñada pensando en una gestión cuidadosa de la memoria
- La estrategia de reemplazo consistió en mantener ambos sistemas juntos y permitir convertir entre ellos mediante
.c_str()y.Get() - A AString se le agregaron mejoras para aumentar su usabilidad, y se alentó a los ingenieros a reemplazar std::string de manera autónoma al modificar código
- Con este enfoque, std::string desaparece lentamente y por etapas, y las interfaces de “cinta adhesiva” entre ambos sistemas también se reducen a medida que se limpia el código
-
Evaluación de std::string vs AString
- impacto: 2/5
- La mayoría de las asignaciones de alto impacto que generaba std::string ya se eliminaron mediante profiling
- El costo principal actual es el pequeño costo mental de cambiar de contexto al convertir de un sistema al otro
- costo de corrección: 3/5
- La transición a AString no es un simple find-and-replace
- AString tiene variantes para propósitos específicos, como AStackString, que asigna inicialmente en memoria de stack; ARefString, para referencias a static strings; y AString, basada en asignación en heap
- Para hacer el reemplazo correcto, una persona debe revisar y decidir cada punto directamente, y el proceso de eliminar gradualmente el sistema anterior será largo y lento
- contagio: -2/5
- Al hacer que AString sea más fácil de usar que std::string, el contagio se invirtió en una dirección favorable
- Cada vez que un ingeniero hace check-in de cambios en el código del juego, aumenta la posibilidad de que AString se extienda más
- impacto: 2/5
-
Cómo corregir la Deuda MacGyver
- El gran costo de la Deuda MacGyver suele ser el costo intelectual de tener que cambiar de modo al cruzar límites
- Si un bug o una funcionalidad queda bloqueada porque está en el sistema “incorrecto”, trasladar el punto objetivo al sistema “correcto” suele ser un trabajo directo
- El contagio relativo entre el sistema nuevo y el existente es una métrica clave
- Si se invierte el equilibrio para que el sistema nuevo sea más contagioso, el mejor sistema termina ganando
- Hay que hacer que el sistema globalmente mejor también sea más atractivo a nivel local
- Si un ingeniero bajo presión de tiempo, al hacer optimización codiciosa en su trabajo diario, elige aun así el estado final deseado, entonces se va en la dirección correcta
- Otro enfoque es un refactor masivo por fuerza bruta; según qué tan estrechamente se mapeen los sistemas entre sí, quizá se pueda corregir parte o todo con una clever regex
Deuda fundacional: cuando una suposición profunda queda incrustada en toda la estructura
- La deuda fundacional es el estado en el que alguna suposición en lo más profundo del sistema queda incorporada en toda la forma en que funciona
- Puede ser difícil de detectar porque los usuarios con más experiencia del sistema la ven como “simplemente así son las cosas”
-
Analogía del mundo real: unidades tradicionales de Estados Unidos
- Quienes crecieron en Estados Unidos terminan memorizando conversiones como que 1 milla son 5,280 pies, 1 cuarto son 2 pintas y 1 galón son 4 cuartos
- El gobierno de Estados Unidos ha considerado varias veces la transición al sistema métrico, pero sigue siendo uno de los 7 países que no han adoptado el Système International como sistema oficial de medición
- Esta deuda está incrustada en las señales de tránsito, las recetas, las escuelas primarias y la cabeza de las personas
-
Caso de League: BlockBuilder y Lua
- Entre los grandes casos de deuda fundacional que Riot ha abordado están Determinism in League of Legends y Game Data Server
- El uso de Lua scripting language en League también es un ejemplo de deuda fundacional
- Los diseñadores de League usan una herramienta llamada BlockBuilder para conectar bloques de funciones y crear comportamientos complejos
- Los bloques de funciones incluyen calcular la distancia entre puntos, crear minions, procesar damage y diversos script flow control
- El conjunto de operaciones que elige el diseñador es variado pero limitado, y los parámetros de cada operación también están restringidos
- Al inicio de League of Legends, en lugar de guardar los bloques y parámetros en un formato simple y restringido adecuado para los datos, se decidió almacenarlos como arrays y tables del lenguaje Lua, potente pero demasiado complejo para este propósito
- Luego, unos 10 años de desarrollo del juego avanzaron sobre esa base, y la manipulación de Lua object se convirtió en una de las tareas más comunes dentro del motor
-
Evaluación de Lua en BlockBuilder
- impact: 4/5
- La falta de correspondencia entre Lua y ese espacio de problema genera muchos costos
- En cada frame de la lógica de BlockBuilder, el callstack queda contaminado con unas 6 marshalling stack frame
- El trabajo de marshalling no es barato en términos de uso de CPU del server
- Leer los diff de cambios en scripts es innecesariamente difícil
- Para hacer parsing/searching de un script file con el fin de entender una funcionalidad, se necesita un conocimiento bastante profundo del lenguaje Lua
- fix cost: 4/5
- Lua está profundamente incrustado en el motor, por lo que eliminarlo es difícil
- Una de las propuestas actuales es crear una wrapper class que se comporte como un Lua object, pero que internamente sea un struct mucho más simple, para ir cambiando poco a poco el interior del scripting a una forma más adecuada
- Cualquier enfoque debe aplicarse con cuidado y criterio
- contagion: 4/5
- Cada vez que un sistema entra en contacto con el scripting, ese sistema queda moldeado por las operaciones y requisitos del backend de Lua
- El scripting es la unidad central de logic en LoL
- Riot agrega, en promedio, un nuevo Building Block aproximadamente cada 3 o 4 días, y cada Building Block manipula directamente un Lua object
- Cuanto más tiempo pase sin reemplazar Lua, más difícil será reemplazarlo
- impact: 4/5
-
Formas de reducir la deuda fundacional
- La deuda fundacional tiende a tener puntajes altos en los tres ejes: impact, fix cost y contagion
- Un fix cost alto hace que se siga usando un sistema imperfecto, y a veces esa también es la decisión correcta
- Pero debido al alto impact y la alta contagion, corregir una deuda fundacional grave puede traer grandes recompensas
- La estrategia de corrección más común observada en Riot es construir un sistema nuevo junto al sistema existente
- Cuando sea posible, se convierte la foundational debt existente en MacGyver Debt y, mediante una conversion operation, se va alternando entre el sistema nuevo y el existente mientras se porta gradualmente
- Este enfoque permite empezar a obtener beneficios en áreas específicas al tiempo que limita la exposición al riesgo
- Cuando una transición de este tipo no es posible, se puede crear un compile time switch o, si es viable, un loading time switch para ir ganando confianza en el sistema nuevo
- El enfoque de compile time switch se está usando en la transición de GDS
- El enfoque de loading time switch funcionó bien en Determinism
Deuda de datos: cuando se acumula contenido masivo sobre defectos
- La Deuda de datos ocurre cuando se acumula una gran cantidad de contenido sobre otras categorías de deuda técnica
- El punto de partida puede ser un bug en un scripting system, un file format inadecuado para un item, dos sistemas que no encajan bien entre sí, etc.
- Cuando se crea una gran cantidad de contenido como art, scripts o sounds sobre ese defecto de código, corregir la deuda técnica inicial se vuelve muy riesgoso
- Con el tiempo, entender qué se va a romper se vuelve dolorosamente difícil
-
Analogía del mundo real: ADN
- El genome de un organismo se acumula lentamente durante millones de años mediante mutation, transcription error y evolutionary pressure
- Algunos errores de copia son inútiles pero no dañinos, otros son dañinos y otros otorgan ventajas poderosas
- Es muy difícil determinar qué hace realmente un fragmento de ADN
- Entendemos por completo qué significan los base pairs y cómo un conjunto de base pairs se traduce en amino acids para protein construction
- También empezamos a entender más sobre algunos roles non-encoding del ADN
- Pero en los más de 3.000 millones de base pairs del genome humano todavía hay muchas partes que entendemos muy poco
- El episodio de CRISPR de Radiolab trata uno de esos rompecabezas resueltos recientemente
-
Caso de League: bug de nombres de parámetros de block
- En League of Legends, la Deuda de datos tiene su mayor impacto cuando convierte algo que originalmente habría sido un ajuste menor en un trabajo pesado
- Los game engineers acumulan un conocimiento profundo sobre cómo están implementados los sistemas del juego y se vuelven hábiles para predecir qué datos romperá cada cambio de código
- La Deuda de datos es una de las consideraciones más importantes al modificar el engine de LoL
- Un caso de Deuda de datos corregido hace algunos años fue un bug relacionado con los block parameters del scripting language BlockBuilder
- En un toy example, si se intentaba aumentar la armor del Owner con una variable y una constante, el valor esperado era 25 bonus armor: la variable Delta 20 más la constante 5
- Si el nombre de la variable era igual al nombre del parameter, antes el resultado era 40
- El autor dice que tampoco sabe por qué no era 45
-
Proceso real de corrección
- Cuando NoopMoney, ingeniero del Champions team, intentó corregir este comportamiento, el cambio real de código fue solo eliminar 4 líneas
- Sin embargo, una deuda altamente contagiosa requería una planificación minuciosa incluso para un cambio pequeño
- Cualquier numerical parameter en cualquiera de las 400.000 líneas de script de LoL podía estar duplicándose por este bug
- El problema mayor era que el juego había sido balanceado y ajustado en torno a esos valores potencialmente duplicados, por lo que esos scripts estaban funcionando “correctly”
- NoopMoney tuvo que hacer que el fix pudiera activarse o desactivarse en Live para prepararse ante bugs inesperados
- Para identificar qué scripts dependían de este bug, se realizaron búsquedas amplias con regex y un barrido de QA
- Al final, los problemas causados por la corrección fueron relativamente pequeños, y solo unos pocos champion scripts necesitaron cambios
- Debido a la Deuda de datos, predecir el resultado era difícil
-
Evaluación del Parameter Naming Bug
- impact: 2/5
- El impacto cuando ocurría era bajo
- Podía duplicar el valor pasado y descartar la constante
- Se convirtió en otro conocimiento tribal inútil que los diseñadores e ingenieros que lo conocían tenían que recordar
- La developer mindshare es un recurso demasiado valioso como para desperdiciarlo así
- fix cost: 2/5
- En general, la corrección en sí era directa
- Se pudo crear un live feature toggle para aumentar la confianza en la seguridad del fix
- La parte más costosa fue el screening inicial para evaluar el alcance del problema y definir los objetivos de prueba
- contagion: 4/5
- Fue desafortunado que este bug apuntara a un comportamiento muy lógico
- Para infligir damage a una unit, tiene todo el sentido guardar el valor en una variable llamada “Damage”
- El bug se disparaba cuando el block ApplyDamage recibía el amount en un parameter con el mismo nombre
- Si otra persona hacía copy/paste de ese block para crear un spell similar, el bug se propagaba más
- impact: 2/5
-
Por qué la Deuda de datos es tan contagiosa
- La Deuda de datos suele tener un costo de corrección alto porque dificulta evaluar el impacto de los cambios
- Lo más preocupante es que, por la naturaleza de los data, casi siempre es altamente contagiosa
- Crear data nuevos haciendo copy/paste de data existentes es una práctica generalmente aceptada
- Al crear un nuevo skillshot spell, empezar desde Ezreal’s Mystic Shot puede ahorrar mucho tiempo, y los problemas de los data existentes también se transmiten a sus data descendientes
- Como los data casi nunca reciben una revisión técnica similar a code review, aunque las malas prácticas sean ampliamente conocidas, es difícil notar su propagación y detenerla
- Para corregir problemas en los data, normalmente una persona con ojos y cerebro debe verificarlos directamente; el compiler y la formal logic por sí solos no bastan
-
Dos enfoques para corregir la Deuda de datos
- El primer enfoque es el do it right checkbox
- Consiste en crear, para el data creator, un toggle entre el comportamiento “broken” existente y el nuevo comportamiento “fixed”
- Idealmente, la fixed version queda como valor predeterminado y el old content usa la broken version
- Luego, como con la MacGyver Debt, se puede migrar a la nueva versión mediante un replacement lento y constante
- La desventaja es que se genera un costo permanente al agregar cada vez más elementos innecesarios a la editing UI
- El segundo enfoque es just fix the damn thing
- Es el método que NoopMoney usó para el parameter naming bug
- Consiste en corregir el bug y luego intentar reparar todos los data que se vean afectados de forma significativa
- Las técnicas para que dé menos miedo incluyen mucho grep y regex searching para entender el theoretical impact, targeted testing y tener listo un toggle para volver al comportamiento anterior si después del ship se descubren omisiones peores
- Determinism ayuda mucho a probar este tipo de cambios, ya que permite comprobar si el servidor genera el mismo resultado antes y después del cambio
- El primer enfoque es el do it right checkbox
Resumen: la contagión debe incluirse en la evaluación de costos
- Las métricas para medir la deuda técnica son impact, fix cost y contagion
- impact es el efecto sobre clientes y desarrolladores
- fix cost es tiempo y riesgo
- contagion es el grado de propagación del problema
- La mayoría de los desarrolladores consideran regularmente impact y fix cost, pero las discusiones sobre contagion son relativamente menos frecuentes
- contagion puede convertirse en el peor enemigo de los desarrolladores cuando un problema se incrusta profundamente y se vuelve cada vez más difícil de eliminar
- Por el contrario, si haces que el fix sea más contagioso que el problema, puedes convertir la contagion en un arma
- La mayor parte de la deuda técnica vista en League encaja en una de cuatro categorías
- Local Debt: deuda como una black box desordenada por dentro
- MacGyver Debt: deuda en la que dos o más sistemas quedan pegados con cinta adhesiva mediante una conversion function
- Foundational Debt: deuda en la que toda la estructura está construida sobre supuestos desafortunados
- Data Debt: deuda en la que se acumula una enorme cantidad de data sobre otros tipos de deuda, haciendo que corregirla sea riesgoso y lleve mucho tiempo
1 comentarios
Opiniones de Hacker News
La contagiosidad es precisamente la razón por la que las interfaces son uno de los factores más importantes del diseño y hay que pensarlas bien.
Si tienes una implementación no óptima detrás de una interfaz hermosa, puedes limpiarla fácilmente cuando tengas tiempo, pero al revés casi nunca funciona.
A veces faltan ambas.
En esos casos, imponer de forma composable módulos pequeños y responsabilidad única puede evitar que el contagio se vuelva demasiado grave. No requiere mucho conocimiento del futuro ni mucho tiempo; basta con evitar interfaces de superficie amplia tipo muñeca rusa que controlan múltiples variantes de comportamiento mediante parámetros. Conviene mover la configuración, el parsing y la decisión de comportamiento hacia los bordes de la lógica, y no dejar que se filtren por todo el submodelo.
Incluso si empiezas conscientemente con la intención de evitar la deuda técnica a toda costa, hacerlo bien es difícil y exige más que simple confianza técnica o visión arquitectónica. En la práctica, entra en el terreno de la predicción del futuro.
Si horneas implícitamente una implementación tonta dentro de la interfaz, suele volverse imposible arreglarla cambiando solo la implementación. Un ejemplo que se me ocurre es el comportamiento de ordenamiento y paginación. Los desarrolladores junior, y ahora también muchos senior que ya deberían saberlo mejor, a menudo empiezan con solicitudes que usan parámetros tipo
limit/offset, lo que deriva en problemas de rendimiento horribles y comportamientos extraños. La forma en que la paginación funciona eficientemente, y las opciones de ordenamiento que se pueden soportar con buen rendimiento, están inherentemente acopladas a la forma de los datos y a la elección del almacén de datos. Alguien que no haya pasado por este proceso en capas bajas difícilmente podrá diseñar bien una interfaz de nivel superior si no se mete primero lo suficiente en la implementación.En la mayoría de los casos no quiero ver la implementación; quiero ver solo una interfaz bien documentada. Si no puedes explicar el comportamiento de la interfaz con palabras simples, algo está mal.
QWERTY es famoso por no ser una interfaz física óptima, y el volante podría ser un caso similar. En computación, x86 es un ejemplo representativo de una interfaz superficialmente no óptima.
Es bastante sorprendente que este artículo lo haya escrito un engineering manager.
Ninguno de los managers con los que he trabajado podía hablar de nuestra base de código con este nivel de detalle técnico. Ni siquiera los que antes habían sido ingenieros.
Aunque, para ser justos, no había managers promovidos internamente, y como la gente de adentro —incluyéndome— no quería dejar la ingeniería, tenemos la mala costumbre de contratar managers de afuera.
Creo que falta el tipo de deuda más común que he visto: deuda de fundador.
Es la deuda que los fundadores crean para sacar rápido tecnología valiosa. Algo que parecía fruta al alcance de la mano termina convirtiéndose en la base de todo el sistema.
Los documentos fundacionales de muchos países también caen en esta categoría lol (pero no USA! USA! USA!).
La deuda MacGyver y la deuda fundacional son las más cercanas, pero ninguna captura exactamente este fenómeno.
Es un gran artículo desde el punto de vista técnico.
Aun así, diría que esto se parece más a una nomenclatura que a una “taxonomía”, porque no pretende ser exhaustiva ni mutuamente excluyente; aunque podría estar equivocado. Los ejemplos físicos de cada punto fueron especialmente buenos y dan mucho para pensar.
Como siempre, hay un detalle filosófico menor que me hace ruido. Los “tres ejes” del principio parecen ser los retornos e inversiones del RoI tradicional, más una subcategoría específica de retornos futuros y condicionales. Supongo que esta decisión habrá funcionado bien en la práctica, y las prácticas de desarrollo de videojuegos no tienen por qué ser absolutamente científicas, pero un poco más de certeza filosófica no vendría mal.
Ya se había discutido en su momento:
A Taxonomy of Technical Debt - https://news.ycombinator.com/item?id=16810092 - abril de 2018 (113 comentarios)
Y también está esto:
A Taxonomy of Tech Debt (2018) - https://news.ycombinator.com/item?id=39782923 - marzo de 2024 (1 comentario)
La explicación de que “la deuda técnica se define como código o datos cuyo costo pagará un desarrollador futuro” es de las mejores que he visto.
Como con cualquier deuda, al asumirla hay que aplicar un umbral que equilibre la necesidad inmediata con el costo futuro. Siento que la mayoría de la gente, no solo los desarrolladores, sobreestima las necesidades inmediatas y subestima los costos futuros.
Personalmente detesto casi de forma patológica cualquier tipo de deuda. A veces paso un día extra separando algo que podría ser útil en el futuro. Acierto más o menos el 50% de las veces, pero cada vez que hago ese trabajo refuerzo el hábito y mi flujo de trabajo básico también se vuelve más rápido.
Hasta ahora trabajé en 3 “startups”, y en todas entré después de que ya generaban ingresos suficientes para pagar sueldos cercanos a lo normal.
Lo que vi con más frecuencia es que varios fundadores mezclan de forma borrosa la idea que se les ocurrió, lo que realmente se construyó y la parte implementada que de verdad funciona.
Desde que leí este artículo por primera vez, uso la palabra contagiosidad para explicar la deuda técnica, y encaja bastante bien.
No estoy seguro de que la “deuda local” pueda llamarse deuda técnica en circunstancias normales.
En la práctica siempre hay partes desordenadas en algún lugar, y encapsularlas para esconderlas de modo que nadie salga lastimado es algo normal. Si casi no hace falta tocarlas mientras no cambien los requisitos, y si cuando cambian habría que modificar cualquier implementación, entonces está bien.
Si las 24 instancias de minions del ejemplo no son simplemente poco elegantes sino un problema real, parece más bien deuda fundacional, en el sentido de que el “minion” se convirtió en la unidad básica más simple y podría haber existido algo más liviano.
Animar a los desarrolladores a hacer cambios incluso en módulos viejos y maduros que no necesitan tocarse es una buena forma de evitar que esto se acumule hasta volverse un problema.
Un aspecto importante es cuando se asume conscientemente deuda técnica para obtener beneficios de corto plazo.
Entonces ese beneficio también se convierte en otro eje que hay que poner en la balanza.
¿Quieres construir ahora un edificio nuevo y terminar el trabajo, en vez de esperar 15 años hasta tener el capital? Pides un préstamo.
La deuda es una herramienta, pero una herramienta poderosa y peligrosa. Si no reconoces que la estás usando y no la respetas, te lastimas. O se lastima alguien a quien le pasaste la granada. Igual que la deuda real.