Dette techniqueArchitectureLivraison logicielle

Logiciel legacy : quand vivre avec, quand le faire évoluer, quand le réécrire

Tout logiciel legacy ne mérite pas d’être réécrit, et tout ne peut pas non plus rester en l’état. La décision repose sur trois variables que presque personne ne mesure avant de choisir. Un guide pour qui doit décider quoi faire d’un système qui fonctionne mais qui freine.

QMates· Software Advisory14 avril 202611 min de lecture

Le problème du mot « legacy »

Dans le langage courant, « logiciel legacy » est devenu un terme presque exclusivement négatif : vieux, problématique, à remplacer au plus vite. Ce cadrage émotionnel conduit à des décisions coûteuses. Un système qualifié de legacy se retrouve automatiquement en tête de liste des réécritures, sans égard pour l’ampleur réelle du ralentissement qu’il cause, pour le coût de son remplacement, ni pour la valeur qu’il continue de produire.

La définition la plus citée dans la littérature vient de Michael Feathers : le code legacy, c’est simplement du code sans tests. Utile, mais elle nous enferme dans un seul symptôme. En pratique, nous préférons une définition plus large. Un système legacy est un système qui tourne en production et génère de la valeur, mais qui présente au moins l’une de ces caractéristiques : des technologies que la communauté ne maintient plus, une connaissance concentrée sur très peu de personnes, une difficulté croissante à le modifier pour une raison structurelle quelconque. Cela peut être un système écrit il y a trois ans qui a déjà accumulé assez de dette pour devenir difficile à modifier.

La question n’est pas « ce système est-il legacy ? » mais « combien coûte son maintien en l’état, combien coûterait sa modification, et combien coûterait son remplacement ? » Sans réponse chiffrée à ces trois questions, toute décision reste arbitraire.

Les trois variables que nous observons

D’après notre expérience, le choix entre cohabitation, évolution et réécriture dépend surtout de trois variables qu’il faut mesurer avant de choisir.

La première, c’est la fréquence de modification. Un système qui ne change presque jamais a un coût de maintenance faible, quelle que soit sa complexité technique. La dette technique se paie à chaque modification du système : tant qu’on n’y touche pas, elle reste latente. Un module legacy qui fait une seule chose, stable depuis des années et hors du chemin critique de la croissance, peut rester tel quel indéfiniment. Décider d’intervenir ne devient urgent que lorsque cette stabilité est remise en cause par de nouvelles exigences.

La deuxième, c’est le poids dans les dépendances. Un système legacy isolé, avec peu de connexions vers le reste de l’architecture, pose bien moins de problèmes qu’un système legacy au centre d’un réseau de dépendances. Si chaque nouvelle fonctionnalité doit passer par ce système, sa dette se multiplie à chaque modification du système plus large. La position architecturale du système legacy compte souvent plus que son état interne.

La troisième, c’est le risque de perte de connaissance. C’est le facteur le plus sous-estimé des trois. Un système qui fonctionne bien mais que seules deux personnes connaissent vraiment représente un risque opérationnel concret. Dès que ces personnes ne sont plus disponibles, en congé, malades, ou parce qu’elles sont parties, cette partie du système devient opaque. Le risque de perte de connaissance peut transformer en urgence, en très peu de temps, un système qui semblait jusque-là bien géré.

Premier scénario : vivre avec

Vivre avec un système legacy est le bon choix quand les trois variables indiquent que le coût de sa modification ou de son remplacement dépasse le bénéfice attendu.

Le profil type : un système très stable (faible fréquence de modification), peu pesant dans les dépendances (peu de connexions vers le reste de l’architecture), et dont la connaissance est partagée entre plusieurs personnes ou bien documentée. Dans ce scénario, le système legacy fonctionne comme un actif qui continue de générer de la valeur à un coût de maintenance acceptable.

Vivre avec un système, c’est le traiter comme un composant stable, avec trois engagements précis : ne pas lui ajouter de responsabilités (toute nouvelle fonctionnalité se construit ailleurs), documenter la connaissance existante (pour réduire la dépendance à quelques individus), et surveiller les variables susceptibles de changer l’évaluation (une intégration stratégique qui oblige à y toucher, la fin de vie d’un composant dont il dépend, le départ de la personne clé).

Le signal que la cohabitation a cessé d’être le bon choix apparaît quand l’un de ces déclencheurs se matérialise. Jusque-là, toute énergie dépensée à refactorer ou réécrire un système stable est de l’énergie détournée de problèmes plus urgents.

Deuxième scénario : faire évoluer progressivement

L’évolution progressive est le bon choix dans la majorité des cas où le système legacy se trouve sur le chemin critique de la croissance sans être pour autant assez dégradé pour justifier un remplacement complet. C’est aussi la stratégie la moins séduisante sur le papier, et la plus efficace en pratique.

Faire évoluer un système legacy, c’est appliquer systématiquement les principes du refactoring progressif décrits dans l’article précédent, avec quelques précautions propres aux systèmes portant une dette historique importante.

Le premier principe : ne jamais toucher une partie du système sans l’avoir d’abord couverte de tests. Dans les systèmes legacy, la couverture de tests est presque toujours insuffisante. Ajouter des tests sur le comportement actuel, même quand ce comportement n’est pas celui souhaité, est le préalable à toute modification sûre. Cette phase prend du temps, mais elle n’est pas optionnelle : sans le filet de sécurité des tests, chaque changement est un risque non quantifié.

Le deuxième principe : isoler avant de modifier. Avant de restructurer une partie du système legacy, on l’isole du reste par des interfaces claires. Cet isolement permet de tester indépendamment, de remplacer l’intérieur sans impact extérieur, et de vérifier le comportement sur des cas précis. C’est le pattern Strangler Fig appliqué à l’échelle d’un seul module.

Le troisième principe, propre aux systèmes legacy : ne pas chercher à tout comprendre avant de commencer. Dans des systèmes qui ont des années d’histoire et peu de documentation, une compréhension complète avant d’intervenir est souvent impossible. L’approche qui fonctionne est exploratoire : on circonscrit une zone, on ajoute des tests, on intervient, on observe ce qui en ressort. La connaissance du système s’acquiert par petites touches, à travers le processus de modification sûre.

Troisième scénario : réécrire

Une réécriture complète ne se justifie que dans un nombre limité de cas. Les identifier précisément compte d’autant plus que le réflexe émotionnel pousse à répondre par une réécriture à des problèmes qui pourraient se résoudre avec des interventions plus ciblées et moins risquées.

Le premier cas où la réécriture se justifie, c’est quand le système repose sur des technologies qui ne sont plus maintenues, dont l’absence de support devient un risque de sécurité ou de continuité d’activité. Le risque tient à la technologie qui ne reçoit plus de correctifs de sécurité, qui n’a plus de communauté active, ou qui est incompatible avec des exigences réglementaires que l’organisation doit respecter. Une technologie ancienne en soi, on l’a vu, n’est pas de la dette tant qu’elle fonctionne. Ici, le risque vient de l’extérieur du système et ne se règle pas par du refactoring interne.

Le deuxième cas, c’est quand l’architecture du système est si contraignante que toute évolution coûte plus cher qu’une réécriture. Ce seuil est rarement atteint, mais il existe : des systèmes au couplage si envahissant qu’aucun isolement n’est possible, des systèmes dont le modèle de données s’est tellement éloigné des besoins actuels qu’il exige une refonte complète, des systèmes où le coût de chaque modification dépasse sa valeur business. Le test, concrètement : si le coût moyen du changement dépasse durablement la valeur de chaque fonctionnalité, une réécriture pourrait rapporter à moyen terme.

Le troisième cas, c’est la rupture stratégique : un changement de modèle économique, une acquisition, un changement de plateforme technologique qui oblige à tout réimplémenter. Ici, c’est le changement des besoins qui justifie la réécriture, car il rend le système existant inadapté, quelle que soit sa qualité technique.

Dans tous les autres cas, une réécriture est plus risquée qu’elle n’en a l’air, et bien moins nécessaire que ne le laisse croire la dette accumulée.

Comment décider : un cadre en trois questions

Face à une décision sur un système legacy, ces trois questions, posées dans l’ordre, produisent une réponse plus fiable que l’intuition :

Combien coûtera chaque modification de ce système au cours des douze prochains mois ? Estimez le nombre de changements nécessaires et le coût moyen de chacun, compte tenu de la dette actuelle. C’est le coût de la cohabitation : celui de chaque changement que le métier réclamera de toute façon.

Combien coûterait le fait d’amener le système à un état où ces modifications coûteraient deux fois moins cher ? C’est le coût de l’évolution progressive : le coût concret de diviser par deux le coût des futurs changements. Si cet investissement se rembourse en six à douze mois de changements plus rapides, l’évolution se justifie.

Combien coûterait le fait de remplacer entièrement le système, en comptant les coûts cachés : migration des données, double fonctionnement, risque de régressions, perte de la connaissance métier incorporée dans l’ancien système ? C’est le coût de la réécriture. Il dépasse souvent largement les estimations initiales, justement parce que ces coûts cachés sont sous-évalués. Si ce coût dépasse celui de l’évolution progressive sur une période raisonnable, la réécriture ne se justifie pas économiquement.

Le cas particulier : le système legacy que personne ne veut toucher

Un cas précis mérite une attention à part : le système legacy que l’équipe évite systématiquement, dont les modifications sont repoussées autant que possible, et qui provoque de l’anxiété à chaque mise en production qui s’en approche. Ce schéma signale presque toujours un manque de tests et de connaissance pour intervenir en confiance ; une réécriture ne s’impose que dans peu de cas.

Le remède, c’est la stabilisation : ajouter des tests sur le comportement actuel, documenter les parties les plus opaques, mettre en place une surveillance qui rend les problèmes visibles avant qu’ils ne tournent à l’urgence. Ces actions peuvent transformer un système que personne ne veut toucher en un système sur lequel l’équipe sait intervenir, même s’il reste laid à l’intérieur.

La stabilisation seule ne suffit toutefois pas sans des pratiques techniques qui gardent le système sous contrôle dans la durée. Revue de code systématique, déploiements fréquents et réversibles, refactoring incrémental dans le flux de travail normal, pipeline de CI qui fonctionne et que l’on respecte : sans ces mécanismes, la dette se reconstitue à la vitesse même où on la réduit. Les tests protègent des régressions, la surveillance rend les problèmes visibles, mais ce sont les pratiques quotidiennes de l’équipe qui déterminent si le système s’améliore ou se dégrade sprint après sprint. C’est là toute la différence entre une intervention d’urgence et un état durable.

Ce qu’il faut faire concrètement

  1. Appliquez le cadre des trois questions à votre système legacy le plus critique : commencez par celui qui génère le plus de friction. Estimez le coût de la cohabitation, le coût de l’évolution et le coût de la réécriture avec des chiffres concrets. Le résultat surprend souvent : des systèmes qui semblaient exiger une réécriture s’avèrent gérables avec une évolution ciblée ; des systèmes qui semblaient stables affichent déjà un coût de cohabitation élevé.
  2. Identifiez le déclencheur qui changerait votre décision actuelle : si vous avez choisi de vivre avec, quel serait le signal qui vous ferait changer d’avis ? Le départ de la personne clé ? Une nouvelle intégration demandée par le métier ? Une faille de sécurité impossible à corriger ? Savoir précisément quel est ce déclencheur permet de surveiller les bonnes variables, sans remettre sans cesse en question une décision déjà prise.
  3. Documentez la connaissance avant toute autre chose : quelle que soit la stratégie choisie, documenter la connaissance actuelle reste toujours le premier pas. Documenter le code prend du temps et devient vite obsolète ; ce qui compte surtout, c’est de documenter les décisions et les contraintes : pourquoi ce système est structuré ainsi, quels cas limites il gère de façon non évidente, où se cache le comportement que le métier tient pour acquis. Cette connaissance est la plus difficile à reconstituer après l’avoir perdue.

Le regard de QMates

La question qu’on nous pose le plus souvent à propos d’un système legacy, c’est « faut-il le réécrire ? ». Notre réponse, presque toujours, c’est « cela dépend de ces trois variables, qu’il faut mesurer avant de décider » : un oui ou un non direct reste rare. Dans bien des cas, l’analyse révèle que le vrai problème est ailleurs : il n’y a pas de tests pour toucher au système legacy en sécurité, ou la connaissance repose sur une personne qui songe à partir, ou les outils de surveillance manquent pour comprendre ce qu’il fait réellement en production.

La réécriture répond rarement à la bonne question. Elle est souvent la réponse émotionnellement satisfaisante à la mauvaise. Un système qui revient sous contrôle, avec des tests, une connaissance partagée, une surveillance et des pratiques techniques communes, cesse d’être legacy au sens opérationnel du terme : il devient un système qui génère de la valeur à un coût prévisible, quel que soit l’âge de sa technologie. Un système neuf sans ces pratiques ne restera pas neuf longtemps.

Si vous ne savez pas encore lequel de ces trois scénarios correspond à votre cas, l’audit de la dette technique mesure ces variables sur votre système et vous remet une carte des priorités. Si la réponse est l’évolution, le refactoring du code legacy s’en charge de façon incrémentale, en partant des tests qui remettent le système sous contrôle, avec votre équipe et sans arrêter la livraison.

Dites-nous où vous êtes bloqué

Prototype fragile, legacy lourd ou livraison imprévisible, c’est notre point de départ

Nous utilisons vos données pour répondre à votre demande. Consultez notre Politique de confidentialité.