Le client demande : "Puisque l’ajout de cette fonctionnalité dans une nouvelle application prendrait une semaine, pourquoi faut-il ici trois semaines ?"
C’est une très bonne question.
Et souvent, la réponse n’est pas : "parce que les développeurs travaillent plus lentement".
Le problème peut se situer beaucoup plus profondément - dans l’architecture du système, ses dépendances, la manière dont les données sont stockées, l’absence de tests, les décisions prises par le passé et les changements successifs ajoutés au fil des années.
C’est précisément pour cela que le coût du développement logiciel n’est pas constant.
La même fonctionnalité peut coûter une somme totalement différente dans deux systèmes différents.
Le code n’est pas évalué uniquement selon le nombre de fonctionnalités
À première vue, la tâche peut sembler banale.
"Ajoutons la possibilité d’exporter les données vers Excel."
Ou : "Ajoutons un nouveau rôle utilisateur."
Ou : "Connectons le système à notre CRM."
Le problème, c’est qu’une fonctionnalité n’existe jamais totalement isolée du reste du système.
Une nouvelle fonctionnalité peut nécessiter des changements dans :
- la base de données,
- l’API,
- le backend,
- le frontend,
- le système de permissions,
- la journalisation,
- le reporting,
- les intégrations,
- les tests,
- les mécanismes de cache,
- la documentation,
- le processus de déploiement.
Plus le système est interconnecté, plus il faut analyser d’éléments avant toute modification.
Le coût le plus important peut apparaître avant même d’écrire la première ligne de code
Dans un système mature, le développeur ne devrait pas simplement commencer à coder.
D’abord, il faut répondre à :
- Où cette fonctionnalité doit-elle être ajoutée ?
- Avec quels modules va-t-elle communiquer ?
- Quelles données utilise-t-elle ?
- Les mécanismes de permissions existants la couvrent-ils ?
- Le changement aura-t-il un impact sur d’autres processus ?
- Quels tests faut-il mettre à jour ?
- L’architecture actuelle permet-elle seulement de le faire correctement ?
Tout cela fait partie du coût de réalisation de la fonctionnalité.
C’est pourquoi, dans un ancien système, une grande partie du travail peut consister non pas à programmer, mais à identifier les dépendances et les limites de la solution existante.
La dette technique fonctionne comme des intérêts
Une bonne façon de penser la dette technique est justement le coût des changements successifs.
Si une solution a été réalisée rapidement à une certaine époque, cela peut être tout à fait justifié.
Le problème survient lorsque la solution temporaire devient un élément permanent du système.
Une nouvelle fonctionnalité apparaît.
Puis une autre.
Une exception apparaît.
Puis une autre exception.
S’y ajoutent une intégration, un contournement du problème, un processus manuel et une règle supplémentaire.
Au bout de quelques années, plus personne ne se souvient pourquoi le système fonctionne exactement de cette manière.
Mais chaque nouveau changement doit tenir compte de toutes ces décisions historiques.
Martin Fowler décrit la dette technique comme l’effort supplémentaire nécessaire lors des changements d’un système en raison de problèmes liés à sa qualité interne.
On peut donc dire : la dette technique n’a pas besoin de stopper immédiatement le développement. D’abord, elle rend chaque changement successif plus coûteux.
Premier signal : "au passage, il faut encore corriger cinq choses"
C’est l’un des symptômes les plus caractéristiques.
Le client commande une fonctionnalité.
Lors de l’analyse, il s’avère que pour la mettre en œuvre, il faut :
- corriger la structure de la table,
- modifier le mode d’autorisation,
- mettre à jour la bibliothèque,
- corriger l’ancienne API,
- réécrire un fragment du frontend.
Soudain, une petite fonctionnalité cesse d’être petite. Ce n’est pas parce que l’exigence est compliquée. C’est parce que le système n’a plus les bonnes frontières architecturales.
Deuxième signal : un changement exige de tester tout le système
Si une petite modification nécessite une régression manuelle complète, l’organisation paie l’absence d’automatisation.
Avec la croissance du système, le nombre de combinaisons possibles augmente.
Sans un ensemble de tests adéquat, il devient de plus en plus difficile d’être certain que la nouvelle fonctionnalité n’a pas cassé l’ancienne.
Cela entraîne à son tour de la prudence.
Les déploiements sont plus rares.
Les changements sont plus importants.
Le risque augmente.
Et des déploiements plus importants sont plus difficiles à diagnostiquer en cas de problème.
Un cercle vicieux se met en place.
Troisième signal : "mieux vaut ne pas toucher à ce module"
Cette phrase devrait allumer un voyant d’alerte.
Si un module précis est devenu une zone que l’équipe évite parce que son comportement est imprévisible, le système présente un problème de maintenabilité important.
C’est encore pire si une seule personne connaît son fonctionnement. Dans ce cas, l’entreprise n’a pas seulement de la dette technique. Elle a aussi un knowledge risk.
Le départ d’un seul collaborateur peut signifier la perte des connaissances nécessaires pour faire évoluer le système en toute sécurité.
Quatrième signal : chaque fonctionnalité nécessite des exceptions
Un système bien conçu devrait avoir des règles prévisibles.
Si chaque nouvelle fonctionnalité nécessite d’ajouter une exception spéciale, une condition supplémentaire ou un parcours individuel, l’architecture commence probablement à freiner le développement.
Cela conduit souvent à un code dont le comportement n’est plus facile à prévoir.
Et le manque de prévisibilité entraîne un coût plus élevé d’analyse, de tests et de maintenance.
Faut-il tout réécrire ?
Non.
Et c’est ici que nous arrivons à une distinction très importante.
La dette technique n’implique pas automatiquement la nécessité d’un rewrite.
Les solutions possibles comprennent :
La refactorisation
C’est-à-dire l’amélioration de la structure du code existant sans changer son comportement métier.
C’est une bonne direction lorsque le système a encore une architecture cohérente, mais que certains fragments sont difficiles à maintenir.
La modernisation de composants sélectionnés
Il n’est pas nécessaire de remplacer toute l’application.
On peut commencer par le module, l’intégration ou la couche les plus problématiques.
La migration progressive
Les nouveaux éléments peuvent fonctionner à côté de l’ancien système, tandis que les zones suivantes sont progressivement transférées.
Cette approche permet de limiter le risque d’une migration en une seule fois. Dans la littérature consacrée à la modernisation des systèmes legacy, on utilise souvent précisément l’extraction progressive des fonctionnalités et le remplacement des parties successives du système.
Rewrite
Construire un nouveau système a du sens lorsque l’architecture actuelle est à ce point limitante que la poursuite de la modernisation ne procure pas de retour justifié.
Mais un rewrite devrait être une décision issue d’une analyse, et non une réaction à la frustration de l’équipe.
Quand ne vaut-il pas encore la peine d’investir dans la modernisation ?
La dette technique en soi n’est pas une raison d’arrêter le développement. Chaque système a un certain niveau de dette technique. Parfois, la rembourser n’a pas de sens économique.
Si l’application :
- fonctionne de manière stable,
- est sécurisée,
- a un faible nombre de changements,
- prend en charge un processus qui ne se développera pas de manière significative,
- ne génère pas de problèmes opérationnels,
il peut être rationnel de la laisser dans son état actuel.
Il ne s’agit pas de faire en sorte que chaque système soit technologiquement parfait.
Il s’agit de faire en sorte que le niveau de dette soit une décision consciente.
Quand le coût de la dette devient-il un problème business ?
Lorsqu’il commence à avoir un impact sur les résultats de l’entreprise.
Par exemple :
Une nouvelle fonctionnalité devait arriver sur le marché en un mois, mais elle en nécessite trois.
L’intégration avec un nouveau partenaire s’éternise, car l’API de l’ancien système ne permet pas de gérer facilement de nouvelles données.
Une personne clé de l’équipe doit participer à chaque fois aux travaux, car elle seule connaît l’ancien module.
Chaque déploiement majeur nécessite des heures de tests de régression.
Un concurrent lance plus vite de nouvelles fonctionnalités, car sa plateforme permet d’expérimenter plus rapidement.
À ce moment-là, la technical debt cesse d’être un problème du service IT.
Elle devient un problème business.
Comment mesurer si la situation s’aggrave ?
Il n’est pas nécessaire de créer un système KPI complexe.
Il vaut la peine d’observer quelques indicateurs simples :
Lead time - combien de temps s’écoule entre le début du travail sur un changement et son déploiement.
Fréquence des déploiements - à quelle fréquence l’équipe peut livrer des changements en toute sécurité.
Taux d’échec des changements - à quelle fréquence les déploiements provoquent des problèmes.
Temps de rétablissement - à quelle vitesse on peut revenir à un fonctionnement stable après une panne.
Temps de réalisation d’une fonctionnalité - si des tâches similaires exigent de plus en plus d’efforts.
Il vaut également la peine d’analyser le nombre d’opérations manuelles, la couverture des tests, l’actualité des dépendances et le temps nécessaire pour intégrer un nouveau développeur au projet.
De telles données permettent de voir si le problème est réellement technique, ou s’il résulte du processus, des exigences ou de la manière d’organiser le travail.
La pire solution est "encore un petit correctif rapide"
Si l’équipe sait que l’architecture nécessite des changements, mais reporte le sujet à chaque fois, le système peut entrer dans une spirale.
"Faisons un contournement pour l’instant."
"Nous ferons la refactorisation plus tard."
"Pour le moment, cela suffit."
"Au prochain release."
Le problème, c’est que le prochain release apporte de nouvelles exigences.
Et chaque contournement supplémentaire augmente le coût du changement suivant.
C’est pourquoi la décision de rembourser la technical debt devrait faire partie de la stratégie de développement du produit, et non d’une réaction ponctuelle à une crise.
Une bonne application n’est pas celle qui ne vieillit jamais
Chaque système va évoluer.
Les technologies vont évoluer.
Les clients auront de nouveaux besoins.
De nouvelles intégrations apparaîtront.
La manière de travailler de l’entreprise changera.
C’est pourquoi l’objectif ne devrait pas être de créer une application qu’il n’aura jamais besoin de moderniser. L’objectif devrait être de créer une architecture, dans laquelle la modernisation est possible sans arrêter l’activité. C’est une énorme différence.
Parce que le meilleur système n’est pas celui qui paraît le plus moderne le jour du lancement. C’est celui qui, même après quelques années, permet à l’entreprise de réagir rapidement aux changements.
Et si chaque nouvelle fonctionnalité coûte de plus en plus cher, cela ne signifie pas toujours que la fonctionnalité est difficile.
Peut-être que c’est le système lui-même qui est déjà devenu difficile.
