La Dette Technique, c'est p̶a̶s̶ automatique.
1. Les monuments égyptiens et le monde informatique
Rappelez vous de la grandeur des monuments égyptiens, de ces chefs d’œuvres d'architecture ?
Imaginez un monde informatique dans lequel tout le monde (Business, Datas, Tech, Sécurité, Finance, etc ...) peut à tout moment intervenir sur n’importe quelle partie du code pour corriger des bugs ou ajouter des fonctionnalités.
- Tous les membres de l’équipe peuvent le faire facilement parce que chacun est intervenu à un moment ou un autre sur chaque partie du code. Dans ce monde merveilleux, tout le code est régulièrement amélioré, refactoré pour gagner en qualité.
- L’architecture a été si bien pensée qu’aucun changement majeur n’est nécessaire pour mettre en oeuvre toutes les améliorations nécessaires.
- C’est un monde dans lequel la couverture de test est telle que vous pouvez faire toutes ces modifications sans inquiétude.
- Les tests indiqueront de toute façon les éventuelles régressions introduites.
Le bonheur.
Malheureusement on ne reste que peu de temps dans cet univers fantasmagorique, car il y a ... la vraie vie.
Le réflexe naturel de vouloir tout refondre est le plus souvent une très mauvaise solution. On veut refondre intégralement parce qu’on sous-estime souvent largement la valeur du code existant, mais aussi parce qu’on imagine une solution idéale qui n’est en fait qu’un mirage.
2. Le dur (et trop souvent) retour à la réalité
- Dans la vraie vie, les architectures ne sont pas parfaites.
- Dans la vraie vie, les développeurs vont et viennent.
- Dans la vraie vie, on n’a jamais le temps d’entretenir le code comme il faudrait.
- Dans la vraie vie, les langages évoluent régulièrement, changent et se perfectionnent.
- Dans la vraie vie des technologies, les solutions pourtant si géniales il y a deux ans commencent à devenir obsolètes plus vite que vous ne l’aviez imaginé.
- Dans la vraie vie, il y a toujours un moment où vous vous retrouvez avec ces fonctionnalités critiques maudites. Mais toute modification est susceptible d’introduire un bug local ou un comportement inattendu dans un autre composant qui interagit avec lui. Les tests de ce composant sont au mieux incomplets, au pire inexistants. Toute modification s’apparente à une opération de gestion de crise : vous savez qu’à la moindre erreur, vous risquez de créer plus de problèmes que vous n’en n’avez résolus.
- Dans la vraie vie, ces situations se produisent souvent lorsque les auteurs ne sont plus là. Ils laissent derrière eux un code peu lisible et dont l’architecture parait anormalement complexe.
A l’époque où il fallait plusieurs années pour sortir chaque nouvelle version d’une application, on appelait Code Legacy, le code de la version précédente dont les auteurs étaient partis.
Une très belle définition qui nous vient de Michael Feathers (un pro du domaine), en a donné une définition plus rigoureuse :
"Le Code Legacy est le code pour lequel vous n’avez pas de tests" (sous-entendus : tests complets et automatiques).
En général, ce Code Legacy est facile à identifier :
1. aucun développeur ne veut plus le modifier;
2. Il n’a d’ailleurs pas été modifié depuis très longtemps alors qu’il aurait dû...
3. Autres raisons invocables mais durement justifiables ...
3. De la Dette Technique à la Faillite Technique
Donner naissance à du code legacy, signifie que vous n’avez pas fait l’investissement nécessaire pour l'éviter ... et on le paye chèrement.
Au début des années 90, Ward Cunningham, l’inventeur du wiki, a trouvé une métaphore qui a fait date :
"tout le travail que vous avez à faire pour revenir dans le monde merveilleux que je vous ai décrit, c’est votre dette technique".
Si vous n’investissez pas dans le remboursement de votre dette, vous allez au devant d’une catastrophe : la Faillite Technique.
La moindre correction engendre plus de bugs qu’elle n’en corrige au prix d’un effort considérable. Vous empruntez de l'argent pour rembourser votre dette. C’est l’enlisement total de la productivité, l’échec des efforts d’amélioration,
Il faut donc s’attaquer au Code Legacy, établir une approche claire pour reprendre la main et repartir sur de bonnes bases.
4. Refondre ?
Il va falloir se remonter les manches pour refondre. Stop ! Vous commettez une erreur.
Une attitude naturelle est de :
- Commencer à établir la liste des fonctionnalités de l’application,
- Collecter les améliorations qui sont réclamées,
- Définir la nouvelle architecture idéale,
- Se préparer à attaquer ce chantier,
- Evaluer cette refonte à quelques mois de travail,
- Lancer les développements, pour une durée indéterminée,
- Créer de nouvelles bases saines pour une application meilleure, pour les générations futures des utilisateurs finaux
- Vous ne voyez toujours pas la lumière au bout du tunnel.
- A bout de 6 mois, la v1, sera buggée et mal finie
- Cette nouvelle version à de nombreux défauts que l’ancienne version n’avait pas.
Conclusion : Bref, on est mal.
5. Un héritage de grande valeur
Le Code Legacy est générateur de valeur.
Quoi ?? Mais il ...
- tourne probablement depuis des années,
- a rendu de fiers services à vos utilisateurs pendant très longtemps,
- génère du chiffre d’affaire indirectement, et c’est sa valeur aujourd’hui,
- a une valeur infiniment supérieure à celle du code idéal que vous avez dans votre tête et qui n’existe pas encore dans la vraie vie,
- a été testé, corrigé, éprouvé dans la vraie vie par de vrais utilisateurs (et non par les équipes internes). Face à une nouvelle application toute fraîche et pas encore déployée,
La première étape de notre repentance est d'admettre la valeur considérable de ce code quels que soient ces défauts.
6. Le mirage de la solution parfaite
"On ne voit pas plus loin que les choix qu'on ne peux pas comprendre", merci les frères Cohen pour la référence :)
La solution évolutive et moderne n'existe pas. C'est un mirage.
En terme de proportion,
- 80% à 90% des services rendus viennent du Code Legacy;
- Pour le reste, la vision générale et grossière qui ne gère que 10% à 20% des fonctionnalités
Rien n'est parfait, il faut accepter cette idée. Sauf si ... si vous avez une batterie de tests complète avec une couverture de 100%.
- les tests d'unitaires,
- les tests d'intégrations,
- les tests modulaires,
- les tests de chargements,
- les tests de charge,
- les tests de sécurité statiques (SAST),
- les tests de sécurités dynamiques (DAST),
- les tests d'UX/UI,
- etc ...
- Vous testez vos propres tests (Inception Effect)
Si c’était le cas, vous pourriez probablement refactorer ces fonctionnalités en vous appuyant sur les tests et vous n’en seriez probablement pas là.
En conclusion : Refondre ce Code legacy, c’est faire un investissement très important pour reconstruire un composant qui existe déjà malgré ses imperfections.
Est-ce vraiment un choix sain et rentable ?
7. Une refonte clairvoyante, véloce et ensuite agile
Le premier problème d’une refonte intégrale est le manque clarté, de pragmatisme mais aussi de vélocité.
Une fois que vous êtes lancé, vous êtes coincé, il faut terminer le projet.
La première chose est de clarifier cette question élémentaire :
- Pourquoi refondre cette application ?
- Quelle valeur doit apporter cette refonte ?
- De nouvelles fonctionnalités ?
- Une meilleure fiabilité ?
- De meilleures performances ?
Une fois que vous avez une réponse claire à ces questions : "First solve the problem. Then write the code."
8. Règle n°1 de la refonte : Ne pas refondre
Une fois que vous êtes convaincu de la valeur du code legacy, que vous avez bien identifié votre cible, à savoir :
- Si c’est un problème de performance, mesurez les performances et ciblez les traitements les plus lents.
- Si c’est un bug, identifiez-le de la manière la plus précise possible.
- Si vous devez ajouter une nouvelle fonctionnalité, ciblez les interactions de cette fonctionnalité avec l’existant.
Vous êtes prêt pour appliquer la règle n°1 de la refonte : Ne pas refondre.
9. Règle n°2 de la refonte : Penser la stratégie de Modernisation (il y a 6 solutions possibles)
La suite au prochain article :)