Taxonomie de la dette technique (2018)
(technology.riotgames.com)- Riot Games considère la dette technique comme du code ou des données dont les futurs développeurs paieront le coût, et formalise un langage commun pour l’évaluer à partir de cas rencontrés dans le développement de League of Legends
- Les critères d’évaluation sont au nombre de trois : impact, coût de correction et contagion ; la contagion désigne en particulier la mesure dans laquelle une dette se propage, avec le temps, à d’autres systèmes, données et pratiques de développement
- Les types de dette se répartissent entre Local Debt, confinée à l’implémentation interne ; MacGyver Debt, qui relie provisoirement deux systèmes ; Foundational Debt, où des hypothèses profondes sont ancrées dans la structure ; et Data Debt, où du contenu s’accumule par-dessus des défauts
- Cataclysm de Jarvan, l’utilisation parallèle de std::string et AString, l’usage de Lua dans BlockBuilder et le bug de nommage des paramètres de bloc servent d’exemples concrets pour chaque type
- Une dette peu contagieuse peut parfois être laissée en place longtemps, mais une dette très contagieuse voit son coût de correction et son impact augmenter avec le temps : il faut donc couper tôt ses voies de propagation
Trois axes pour évaluer la dette technique
- La dette technique est « du code ou des données dont les futurs développeurs paieront le coût »
- Pour décider s’il faut corriger une dette donnée maintenant, plus tard, ou la conserver pour des raisons pratiques, il faut des critères de mesure communs
- Riot évalue la dette technique selon trois axes
- impact : l’effet sur les joueurs et les développeurs
- fix cost : le temps nécessaire à la correction et le risque lié au déploiement
- contagion : dans quelle mesure le problème se propage si on le laisse en place
-
impact : le coût visible pour les joueurs et les développeurs
- Pour les joueurs, il se manifeste sous forme de bugs, de fonctionnalités manquantes ou de comportements inattendus
- Pour les développeurs, il s’accumule sous forme de retards d’implémentation, d’entraves aux workflows et de détails inutiles à mémoriser
- Ici, les développeurs ne désignent pas seulement les ingénieurs, mais aussi les designers, les artistes VFX et les autres métiers impliqués dans la création du jeu
- Certaines dettes empêchent les ingénieurs d’écrire du nouveau code, tandis que d’autres gênent les designers dans l’écriture de nouveaux scripts ou les artistes VFX dans la création de nouvelles particules
-
fix cost : temps d’implémentation et risque de déploiement
- Le coût de correction inclut non seulement le temps de développement effectif, mais aussi les risques liés au déploiement des changements
- Une simple erreur dans une fonction unique peut se corriger en quelques minutes, mais une hypothèse profonde affectant toute la base de code du jeu peut prendre des semaines, voire des mois
- Même un système « mauvais » peut déjà servir d’outil pour créer un bon jeu, et sa correction peut casser du contenu existant
- Par exemple, modifier la gestion des erreurs du moteur de scripting ou la manière de calculer le temps de génération des particules peut affecter plus de 500 compétences sur plus de 140 champions
-
contagion : la mesure dans laquelle la dette se propage avec le temps
- La contagion désigne la mesure dans laquelle une dette technique se transmet à d’autres systèmes, données ou méthodes de développement lorsqu’on la laisse en place
- La propagation se produit via les interfaces avec le système problématique, via le copier-coller de données empilées par-dessus, ou via des changements dans la manière d’implémenter de nouvelles fonctionnalités
- Une dette bien isolée peut être corrigée plus tard sans que le coût diffère beaucoup d’une correction immédiate
- Une dette très contagieuse devient plus difficile à corriger avec le temps, et son impact augmente à mesure que davantage de systèmes sont infectés par le compromis central
Local Debt : une boîte noire seulement désordonnée à l’intérieur
- La Local Debt ressemble au modèle classique de programmation en boîte noire
- Vu de l’extérieur, le système fonctionne de manière stable, mais son implémentation interne peut être terrible ou confuse
- Ex. : compétences, couche réseau, moteur de scripts
- Si le développement des systèmes environnants ne nécessite pas de tenir compte de la dette interne, sa contagion est plutôt faible
-
Analogie dans le monde réel : l’œil humain
- Par sa structure, l’œil humain reçoit les images à l’envers, et les nerfs de la rétine créent un point aveugle près du centre de chaque œil
- Le cortex visuel du cerveau inverse les données et comble le point aveugle, afin que le reste du cerveau interagisse avec une image « correcte »
- Cette particularité est localisée dans l’œil et le système du nerf optique, et les autres systèmes peuvent facilement l’éviter : l’état est donc « suffisamment bon »
-
Exemple dans League : Cataclysm de Jarvan
- Cataclysm de Jarvan est encore aujourd’hui créé sous forme de minion
- Lorsqu’un designer veut attacher un effet de gameplay à une position ou à un ensemble de positions donné, il peut utiliser un outil qui génère un « invisible minion »
- Dans un commentaire Reddit, RiotXypherous explique ce que signifie ici « minion »
- Ce type d’objet de jeu est une méthode stable et bien comprise pour suivre et exécuter la logique de script
- Le mur de Jarvan nécessite exactement 24 minions pour empêcher les joueurs de s’en échapper
- Il y en avait auparavant 12, mais comme des joueurs parvenaient parfois à s’échapper entre les murs, Riot Exgeniar en a porté le nombre à 24
- L’alternative est une structure de type ring-terrain, un fragment logique unique contrôlant la pathability de Cataclysm, qui permettrait de clarifier la logique et de réduire légèrement le coût de calcul
-
Évaluation de Cataclysm
- impact : 1/5
- Le fait que le mur soit composé de minions n’affecte presque pas les autres développeurs qui créent du nouveau contenu
- Le « Jarvan Ult Hitch » était le résultat de l’interaction entre cette dette et un bug de chargement qui tentait de lire une définition d’auto-attack manquante
- fix cost : 2/5
- Aujourd’hui, il n’est pas possible de créer une géométrie personnalisée à partir de formes composées sans nouveau code
- Créer un « area trigger » en forme d’anneau nécessiterait du code mathématique dédié pour calculer les collisions de l’anneau
- Riot explore la Constructive Solid Geometry pour d’autres usages, ce qui pourrait réduire fortement le coût de correction
- contagion : 1/5
- L’implémentation du mur de Jarvan n’a pas besoin d’être prise en compte lors du développement de fonctionnalités, elle est donc bien isolée
- Le risque de contagion survient si d’autres designers copient-collent cette implémentation dans de nouveaux champions, ce qui s’est effectivement produit à l’occasion
- En tant que problème d’implémentation, la propagation potentielle de Cataclysm est faible et bien comprise
- impact : 1/5
-
Comment traiter la Local Debt
- La caractéristique typique de la Local Debt est un faible score de contagion
- Lorsque l’impact dépasse le fix cost, les développeurs qui jouent le rôle de bons citoyens ont tendance à la corriger avant trop longtemps
- Si elle n’est réellement pas contagieuse, il est sûr de la conserver aussi longtemps que nécessaire
- Se précipiter immédiatement sur une Local Debt qui titille le perfectionnisme des ingénieurs, mais dont l’impact n’est pas assez large, est l’une des grandes erreurs possibles
- Comme la portée du changement est locale, la validation de la correction et les tests de régression sont généralement faciles
- Parmi les exemples récemment corrigés figurent des bugs liés aux inhibiteurs, Janna’s Monsoon et Tear of the Goddess
- Un bug qui, dans certaines situations, faisait que l’inhibiteur envoyait le pathing d’un champion vers les coordonnées 0,0,0
- Un problème où Janna’s Monsoon ignorait le spell shield
- Un problème où Tear of the Goddess gagnait des charges lors de sorts lancés sans mana
Dette MacGyver : deux systèmes assemblés au ruban adhésif
- La dette MacGyver est un type de dette nommé d’après la série télévisée MacGyver du milieu des années 1980
- Dans le contexte de la dette technique, elle désigne une situation où deux systèmes incompatibles sont maintenus ensemble par du « ruban adhésif » aux points d’interface répartis dans toute la base de code
-
Analogie dans le monde réel : Seattle
- Seattle comptait autrefois deux colonies concurrentes, chacune avec sa propre grille urbaine
- À mesure que ces deux colonies sont devenues l’Emerald City moderne, leurs grilles légèrement différentes ont fusionné, créant des pâtés de maisons et des bâtiments aux formes maladroites, ainsi qu’une utilisation inefficace de l’espace
-
Exemple de League : std::string et AString
- Dans la base de code de League, le std::string de C++ coexiste avec la classe personnalisée AString de Riot
- Les deux sont des moyens de stocker, modifier et transmettre des chaînes de caractères
- Riot considère que std::string entraîne de nombreuses allocations mémoire « cachées » et des coûts de performance, et qu’il facilite l’écriture de mauvais code
- AString a été conçu avec une gestion prudente de la mémoire en tête
- La stratégie de remplacement consistait à faire coexister les deux systèmes et à permettre les conversions entre eux via
.c_str()et.Get() - Des améliorations ont été ajoutées à AString pour le rendre plus agréable à utiliser, et les ingénieurs ont été encouragés à remplacer std::string de leur propre initiative lorsqu’ils modifient du code
- De cette manière, std::string disparaît lentement et progressivement, et les interfaces « au ruban adhésif » entre les deux systèmes diminuent aussi à mesure que le code est nettoyé
-
Évaluation de std::string vs AString
- impact : 2/5
- La plupart des allocations à fort impact causées par std::string ont déjà été éliminées grâce au profiling
- Le principal coût actuel est le petit effort mental nécessaire pour passer d’un système à l’autre lors des conversions
- coût de correction : 3/5
- La migration vers AString n’est pas un simple find-and-replace
- AString propose des variantes adaptées à différents usages, comme AStackString, qui effectue l’allocation initiale sur la mémoire de stack, ARefString pour les références à des chaînes statiques, ou AString basé sur une allocation heap
- Pour effectuer le bon remplacement, une personne doit examiner chaque point et décider au cas par cas, et le processus de suppression progressive de l’ancien système sera long et lent
- contagion : -2/5
- En rendant AString plus facile à utiliser que std::string, la contagion a été inversée dans le bon sens
- Chaque fois qu’un ingénieur check-in une modification du code du jeu, AString a une chance de se répandre davantage
- impact : 2/5
-
Comment corriger la dette MacGyver
- Le principal coût de la dette MacGyver est souvent le coût intellectuel nécessaire pour changer de mode lorsqu’on franchit une frontière
- Lorsqu’un bug ou une fonctionnalité se retrouve bloqué parce qu’il est dans le « mauvais » système, déplacer le point cible vers le « bon » système est généralement une opération directe
- La contagion relative du nouveau système et de l’ancien est l’indicateur clé
- Si l’on inverse l’équilibre pour que le nouveau système soit plus contagieux, le meilleur système finit par l’emporter
- Il faut faire en sorte que le système globalement meilleur soit aussi plus attractif au niveau local
- Si des ingénieurs pressés par le temps, dans leurs tâches quotidiennes, choisissent l’état final souhaité même en faisant une optimisation gloutonne, c’est que l’on va dans la bonne direction
- Une autre approche consiste en un refactoring massif par brute force ; selon la proximité avec laquelle les systèmes se correspondent, une clever regex peut corriger une partie ou la totalité du problème
Dette fondatrice : quand une hypothèse profonde est ancrée dans toute la structure
- La Foundational Debt désigne un état où une hypothèse située au cœur du système est intégrée en dur dans l’ensemble de son fonctionnement
- Les utilisateurs expérimentés du système peuvent avoir du mal à la remarquer, car ils la voient comme « simplement comme ça »
-
Analogie du monde réel : United States Customary Units
- Une personne ayant grandi aux États-Unis mémorise des conversions comme 1 mile = 5 280 pieds, 1 quart = 2 pintes, 1 gallon = 4 quarts
- Le gouvernement américain a envisagé plusieurs fois le passage au système métrique, mais les États-Unis restent l’un des 7 pays à ne pas avoir officiellement adopté le Système international comme système de mesure
- Cette dette est ancrée dans les panneaux routiers, les recettes, les écoles primaires et l’esprit des gens
-
Exemple dans League : BlockBuilder et Lua
- Parmi les grands exemples de Foundational Debt auxquels Riot s’est attaqué figurent Determinism in League of Legends et Game Data Server
- L’utilisation du Lua scripting language dans League est aussi un exemple de Foundational Debt
- Les designers de League créent des comportements complexes avec un outil appelé BlockBuilder, en assemblant des blocs de fonctionnalités
- Ces blocs incluent le calcul de la distance entre des points, la création de minions, le traitement des dégâts et divers contrôles de flux de script
- L’ensemble des opérations que les designers peuvent choisir est varié mais limité, et les paramètres de chaque opération sont eux aussi contraints
- Au début de League of Legends, il a été décidé de ne pas stocker les blocs et leurs paramètres dans un format simple et contraint adapté aux données, mais de les stocker dans les arrays et tables du langage Lua, puissant mais excessivement complexe pour cet usage
- Environ dix ans de développement du jeu se sont ensuite construits sur cette base, et la manipulation d’objets Lua est devenue l’une des opérations les plus courantes du moteur
-
Évaluation de BlockBuilder Lua
- impact : 4/5
- L’inadéquation entre Lua et cet espace de problème entraîne de nombreux coûts
- À chaque frame de la logique BlockBuilder, la callstack est polluée par environ 6 stack frames de marshalling
- Les opérations de marshalling ne sont pas négligeables en termes d’utilisation CPU côté serveur
- Lire les diffs de changements de script est inutilement difficile
- Pour analyser/rechercher dans les fichiers de script afin de comprendre une fonctionnalité, il faut une compréhension assez approfondie du langage Lua
- coût de correction : 4/5
- Lua est profondément ancré dans le moteur, ce qui le rend difficile à supprimer
- L’une des propositions actuelles consiste à créer une wrapper class qui se comporte comme un objet Lua, mais qui est en interne une struct beaucoup plus simple, afin de faire évoluer progressivement l’intérieur du scripting vers une forme plus adaptée
- Quelle que soit l’approche retenue, elle devra être menée avec prudence et réflexion
- contagion : 4/5
- Chaque fois qu’un système touche au scripting, il est façonné par les opérations et exigences du backend Lua
- Le scripting est l’unité centrale de logic de LoL
- Riot ajoute en moyenne un nouveau Building Block environ tous les 3 à 4 jours, et chaque Building Block manipule directement des objets Lua
- Plus Lua reste longtemps sans être remplacé, plus il devient difficile de le remplacer
- impact : 4/5
-
Comment réduire la Foundational Debt
- La Foundational Debt a tendance à obtenir des scores élevés sur les trois axes : impact, coût de correction et contagion
- Un coût de correction élevé pousse à continuer d’utiliser un système imparfait, et c’est parfois le bon choix
- Mais en raison de son impact élevé et de sa forte contagion, corriger une Foundational Debt sérieuse peut apporter un bénéfice important
- La stratégie de correction la plus courante observée chez Riot consiste à construire un nouveau système à côté de l’ancien
- Quand c’est possible, on transforme la foundational debt existante en MacGyver Debt, puis on fait cohabiter le nouveau système et l’ancien via une conversion operation, en portant progressivement le code
- Cette approche permet de commencer à tirer des bénéfices dans certaines zones tout en limitant l’exposition au risque
- Lorsque ce type de transition est impossible, on peut créer un compile time switch, ou si possible un loading time switch, afin de gagner en confiance dans le nouveau système
- L’approche par compile time switch est utilisée pour la transition GDS
- L’approche par loading time switch s’est révélée efficace pour Determinism
Data Debt : quand une masse de contenu s’accumule sur des défauts
- La Data Debt apparaît lorsqu’une grande quantité de contenu s’accumule par-dessus d’autres catégories de dette technique
- Le point de départ peut être un bug dans un scripting system, un file format inadapté aux items, ou deux systèmes qui s’accordent mal
- Lorsque de grandes quantités de contenu comme de l’art, des scripts ou des sons sont créées au-dessus de ce défaut de code, corriger la dette technique initiale devient très risqué
- Avec le temps, déterminer ce qui va casser devient douloureusement difficile
-
Analogie dans le monde réel : l’ADN
- Le genome d’un organisme s’accumule lentement pendant des millions d’années via des mutations, des transcription errors et des evolutionary pressures
- Certaines erreurs de copie sont inutiles mais inoffensives, certaines sont nocives, et d’autres apportent un avantage puissant
- Il est très difficile de découvrir ce que fait réellement un fragment d’ADN
- Nous comprenons parfaitement ce que signifient les base pairs, et comment des ensembles de base pairs sont traduits en amino acids pour la protein construction
- Nous commençons aussi à mieux comprendre certains rôles non encodants de l’ADN
- Mais parmi les plus de 3 milliards de base pairs du genome humain, beaucoup restent encore très mal compris
- L’épisode de Radiolab sur CRISPR traite de l’une de ces énigmes récemment résolues
-
Cas de League : bug de nommage des paramètres de bloc
- Dans League of Legends, la Data Debt a son plus gros impact lorsqu’elle transforme ce qui aurait dû être une modification mineure en travail laborieux
- Les game engineers accumulent une connaissance approfondie de la façon dont les systèmes de jeu sont implémentés, et deviennent experts pour prédire quels changements de code casseront quelles données
- La Data Debt est l’un des facteurs les plus importants à prendre en compte lors des changements du moteur de LoL
- Un exemple de Data Debt corrigé il y a quelques années était un bug lié aux paramètres de bloc dans le langage de script BlockBuilder
- Dans un toy example, si l’on tente d’augmenter l’armor de Owner avec une variable et une constante, la valeur attendue est 25 de bonus d’armor, soit la variable Delta 20 plus la constante 5
- Si le nom de la variable était identique au nom du parameter, le résultat était auparavant 40
- L’auteur dit ne pas savoir pourquoi ce n’était pas 45
-
Processus de correction réel
- Quand NoopMoney, ingénieur de la Champions team, a voulu corriger ce comportement, la modification effective du code ne consistait qu’à supprimer 4 lignes
- Mais une dette très contagieuse exigeait une planification rigoureuse, même pour un petit changement
- Parmi les 400 000 lignes de script de LoL, n’importe quel numerical parameter pouvait être doublé par ce bug
- Le plus gros problème était que le jeu avait été équilibré et tuné autour de ces valeurs potentiellement doublées, si bien que ces scripts fonctionnaient « correctement »
- NoopMoney a dû rendre le fix activable/désactivable sur Live via un toggle, afin de parer aux bugs inattendus
- Pour identifier les scripts qui dépendaient de ce bug, une vaste recherche par regex et une passe QA ont été menées
- Au final, les problèmes provoqués par la correction sont restés relativement limités, et seuls quelques scripts de champions ont dû être modifiés
- À cause de la Data Debt, il était difficile de prédire le résultat
-
Évaluation du Parameter Naming Bug
- impact : 2/5
- L’impact, lorsqu’il se produisait, était faible
- Il pouvait doubler la valeur transmise et ignorer la constante
- C’est devenu un élément de tribal knowledge inutile de plus que les designers et engineers informés devaient mémoriser
- Le developer mindshare est une ressource trop précieuse pour être gaspillée ainsi
- fix cost : 2/5
- Globalement, la correction elle-même était directe
- Un live feature toggle pouvait être créé pour augmenter la confiance dans la sécurité du fix
- La partie la plus coûteuse était le screening initial visant à évaluer le périmètre du problème afin de définir les cibles de test
- contagion : 4/5
- Ce bug était malheureux parce qu’il touchait un comportement très logique
- Pour infliger des damage à une unit, il est parfaitement logique de stocker la valeur dans une variable appelée « Damage »
- Le bug se déclenchait lorsque le bloc ApplyDamage recevait l’amount via un parameter du même nom
- Si quelqu’un copiait/collait ce bloc pour créer un spell similaire, le bug se propageait davantage
- impact : 2/5
-
Pourquoi la Data Debt est si contagieuse
- La Data Debt est généralement jugée coûteuse à corriger parce qu’elle rend difficile l’évaluation de l’impact des changements
- Plus inquiétant encore, par la nature même des data, elle est presque toujours très contagieuse
- La pratique consistant à créer de nouvelles data en copiant/collant des data existantes est généralement acceptée
- Pour créer un nouveau skillshot spell, partir du Mystic Shot d’Ezreal permet de gagner beaucoup de temps, mais les problèmes des data existantes se propagent aussi aux data descendantes
- Comme les data font rarement l’objet d’une revue technique comparable à une code review, même lorsque de mauvaises pratiques sont bien connues, il est difficile de remarquer leur propagation et de l’arrêter
- Corriger un problème de data nécessite généralement une validation directe par une personne avec des yeux et un cerveau ; le compiler et la formal logic ne suffisent pas
-
Deux approches pour corriger la Data Debt
- La première approche est la do it right checkbox
- Elle consiste à créer, pour les data creators, un toggle entre l’ancien comportement « broken » et le nouveau comportement « fixed »
- Idéalement, la fixed version devient la valeur par défaut, tandis que l’old content utilise la broken version
- Ensuite, comme pour la MacGyver Debt, on peut migrer vers la nouvelle version par un remplacement lent et régulier
- L’inconvénient est le coût permanent d’ajouter de plus en plus d’éléments inutiles à l’editing UI
- La deuxième approche est just fix the damn thing
- C’est l’approche utilisée par NoopMoney pour le parameter naming bug
- Après avoir corrigé le bug, on tente de réparer toutes les data affectées de manière significative
- Parmi les techniques qui rendent l’opération moins effrayante : beaucoup de grep et de recherches regex pour comprendre l’impact théorique, des tests ciblés, et la préparation d’un toggle permettant de revenir à l’ancien comportement après le ship si des oublis plus graves sont découverts
- Le Déterminisme aide beaucoup à tester ce type de changement, en permettant de vérifier si le serveur produit les mêmes résultats avant et après la modification
- La première approche est la do it right checkbox
Conclusion : il faut intégrer la contagion dans l’évaluation du coût
- Les indicateurs de mesure de la dette technique sont impact, fix cost et contagion
- L’impact correspond aux effets sur les clients et les développeurs
- Le fix cost correspond au temps et au risque
- La contagion correspond au degré de propagation du problème
- La plupart des développeurs prennent régulièrement en compte l’impact et le fix cost, mais les discussions sur la contagion sont relativement rares
- La contagion peut devenir le pire ennemi des développeurs lorsqu’un problème s’enracine profondément et devient de plus en plus difficile à éliminer
- À l’inverse, si l’on rend le fix plus contagieux que le problème, on peut transformer la contagion en arme
- La plupart des dettes techniques observées dans League relèvent de l’une des quatre catégories suivantes
- Local Debt : une dette qui ressemble à une black box désordonnée à l’intérieur
- MacGyver Debt : une dette où deux systèmes ou plus sont assemblés au ruban adhésif via une conversion function
- Foundational Debt : une dette où toute la structure est construite sur des hypothèses malheureuses
- Data Debt : une dette où d’immenses volumes de data s’accumulent au-dessus d’autres types de dette, rendant les corrections risquées et chronophages
1 commentaires
Commentaires sur Hacker News
La contagiosité est précisément la raison pour laquelle les interfaces sont l’un des éléments les plus importants de la conception, et méritent une réflexion suffisante.
Si une belle interface est adossée à une implémentation imparfaite, on peut facilement faire le ménage quand on a le temps ; l’inverse est rarement vrai.
Parfois, les deux manquent.
Dans ce cas, imposer de petits modules et des responsabilités uniques de manière composable peut empêcher la contagion de devenir excessive. Cela ne demande pas beaucoup de connaissance du futur ni beaucoup de temps ; il suffit d’éviter les interfaces à large surface en poupées russes, qui contrôlent de multiples variantes de comportement via des paramètres. Mieux vaut repousser la configuration, le parsing et les décisions de comportement vers les bords de la logique, plutôt que les laisser s’infiltrer dans tout le sous-modèle.
Même lorsqu’on part consciemment avec l’intention d’éviter la dette technique à tout prix, il est difficile de le faire correctement, et cela exige plus qu’une simple confiance technique ou une vision d’architecture. En pratique, on entre dans le domaine de la prévision de l’avenir.
Si l’on intègre implicitement une implémentation stupide dans l’interface, il devient souvent impossible de corriger le problème en ne changeant que l’implémentation. L’exemple qui me vient à l’esprit est le comportement de tri et de pagination. Les développeurs juniors — et désormais beaucoup de seniors qui devraient mieux savoir — commencent souvent par des requêtes avec des paramètres du type
limit/offset, ce qui entraîne de terribles problèmes de performance et des comportements étranges. La manière dont la pagination fonctionne efficacement, et les options de tri que l’on peut prendre en charge avec de bonnes performances, sont intrinsèquement liées à la forme des données et au choix du stockage. Quelqu’un qui n’a pas traversé ce processus aux couches basses a peu de chances de concevoir correctement l’interface de haut niveau, à moins de creuser d’abord suffisamment l’implémentation.La plupart du temps, je ne veux pas voir l’implémentation ; je veux seulement voir une interface bien documentée. Si l’on ne peut pas expliquer le comportement d’une interface en termes simples, c’est qu’il y a un problème.
QWERTY est célèbre pour ne pas être une interface physique optimale, et le volant pourrait être un cas similaire. Côté informatique, x86 est l’exemple typique d’une interface qui n’est pas optimale en surface.
Il est assez surprenant que cet article ait été écrit par un engineering manager.
Parmi les managers avec lesquels j’ai travaillé, aucun n’aurait été capable de parler de notre codebase à ce niveau de détail technique. Même ceux qui avaient auparavant été ingénieurs.
Pour être juste, toutefois, nous n’avions pas de managers promus en interne, et nous avons la mauvaise habitude de recruter les managers à l’extérieur, parce que les gens en interne, moi compris, ne veulent pas arrêter l’ingénierie.
Il me semble qu’il manque le type de dette le plus courant que j’aie vu : la dette de fondateur.
C’est la dette créée par les fondateurs pour sortir rapidement une technologie utile. Ce qui semblait être un fruit facile à cueillir finit par devenir la base de tout le système.
Les documents fondateurs de nombreux pays entrent aussi dans cette catégorie, lol (mais pas USA! USA! USA!).
La dette MacGyver et la dette de fondation sont les plus proches, mais aucune des deux ne cerne exactement ce phénomène.
Excellent article d’un point de vue technique.
Cela dit, je vois cela davantage comme une nomenclature que comme une « taxonomie ». Ce n’est ni volontairement exhaustif ni mutuellement exclusif, même si je peux me tromper. Les exemples physiques de chaque entrée sont particulièrement bons et donnent matière à réflexion.
Comme toujours, il y a un petit point philosophique qui me chiffonne. Les « trois axes » du début ressemblent aux bénéfices et investissements du ROI traditionnel, avec en plus une sous-catégorie particulière de bénéfices futurs et conditionnels. Je suppose que cette décision a bien fonctionné en pratique, et les pratiques de développement de jeux vidéo n’ont pas besoin d’être absolument scientifiques, mais un peu plus de certitude philosophique ne ferait pas de mal.
On en avait déjà discuté à l’époque :
A Taxonomy of Technical Debt - https://news.ycombinator.com/item?id=16810092 - avril 2018 (113 commentaires)
Et il y a aussi ceci :
A Taxonomy of Tech Debt (2018) - https://news.ycombinator.com/item?id=39782923 - mars 2024 (1 commentaire)
Définir la dette technique comme « du code ou des données dont les futurs développeurs devront payer le coût » est l’une des meilleures formulations que j’aie vues.
Comme pour toute dette, lorsqu’on s’endette, il faut appliquer un seuil qui équilibre le besoin immédiat et le coût futur. J’ai l’impression que la plupart des gens, et pas seulement les développeurs, surestiment les besoins immédiats et sous-estiment les coûts futurs.
Personnellement, j’ai une aversion presque pathologique pour toute forme de dette. Il m’arrive de passer une journée de plus à isoler quelque chose qui pourrait être utile plus tard. J’ai raison environ 50 % du temps, mais chaque fois que je fais ce travail, cela renforce l’habitude et rend aussi mon flux de travail de base plus rapide.
J’ai travaillé jusqu’ici dans trois « startups », et je les ai toutes rejointes après qu’elles avaient déjà assez de revenus pour payer des salaires proches de la normale.
Ce que j’ai vu le plus souvent, c’est que plusieurs fondateurs mélangeaient confusément l’idée qu’ils avaient eue, ce qui avait réellement été construit, et ce qui fonctionnait effectivement parmi les parties implémentées.
Depuis que j’ai lu cet article pour la première fois, j’utilise le mot contagiosité pour expliquer la dette technique, et cela colle plutôt bien.
Je ne suis pas sûr qu’on puisse appeler « dette locale » de la dette technique dans les situations ordinaires.
En pratique, il y a toujours des zones désordonnées quelque part, et les encapsuler pour les cacher de façon que personne ne se blesse est normal. Tant que les exigences ne changent pas, elles n’ont presque jamais besoin d’être modifiées ; et si elles changent, n’importe quelle implémentation devrait de toute façon être ajustée, donc ça va.
Si les 24 instances de minions de l’exemple sont un vrai problème, et pas simplement quelque chose de peu élégant, alors cela ressemble plutôt à de la dette de fondation : le « minion » est devenu l’unité de base la plus simple, alors qu’il aurait pu exister quelque chose de plus léger.
Encourager les développeurs à faire des changements en incluant même les modules anciens, matures et qui n’ont pas besoin d’être touchés est un bon moyen d’éviter que cela ne s’accumule au point de devenir un problème.
Un aspect important est le cas où l’on contracte consciemment de la dette technique pour obtenir un gain à court terme.
Ce gain devient alors un autre axe à mettre dans la balance.
Vous voulez construire un nouveau bâtiment maintenant pour terminer le travail, plutôt que d’attendre d’avoir le capital dans 15 ans ? Vous empruntez.
La dette est un outil, mais un outil puissant et dangereux. Si vous ne reconnaissez pas que vous l’utilisez et ne le respectez pas, vous vous blesserez. Ou bien quelqu’un à qui vous aurez passé la grenade se blessera. Comme avec une vraie dette.