Refactoring du code legacy : recommencez à le modifier sans crainte, sans arrêter la roadmap

Nous entrons dans votre code avec votre équipe et le rendons à nouveau facile à modifier, un pas à la fois, sans le réécrire de zéro et sans mettre la roadmap en pause.

Le logiciel fonctionne, mais chaque modification coûte plus cher que la précédente

Presque personne ne nous appelle parce que le code est moche. On nous appelle quand le produit se vend, que l’équipe est compétente et que, malgré cela, chaque changement du système prend des semaines et traîne quelques régressions derrière lui.

  • Chaque fonctionnalité prend plus de temps que la précédente
  • De petites modifications provoquent des régressions inattendues loin dans le système
  • Certaines parties du code, l’équipe préfère ne pas y toucher
  • Les tests automatisés sont rares, et chaque release dépend de longues vérifications manuelles
  • Une ou deux personnes seulement connaissent vraiment les modules critiques
  • Une modification produit oblige à toucher de nombreux modules qui n’ont rien à voir avec elle
  • Dans l’équipe, on commence à parler de tout réécrire de zéro

C’est de la friction, et elle se concentre justement sur les modifications qui comptent pour le métier, les rendant un peu plus chères chaque mois

Refactoring incrémental, dans votre code

Le refactoring du code legacy rend à nouveau modifiable un système en production en changeant sa structure sans changer son comportement, par petits pas protégés par des tests. Nous ne réécrivons pas le système, et nous ne travaillons pas sur une copie à part. Le refactoring se fait dans le code qui part en production, par petits pas réversibles, pendant que l’équipe continue de livrer des fonctionnalités ; chaque pas laisse le système fonctionnel, et un peu plus facile à modifier qu’avant.

Votre système porte des années de connaissance du domaine, y compris des cas limites que plus personne ne se souvient avoir traités. Un parcours incrémental conserve cette connaissance. Une réécriture doit la reconstruire depuis le début, et la paie presque toujours plus cher que prévu.

Sur les délais, nous préférons être clairs dès le départ : les premiers progrès mesurables arrivent après trois à six mois au moins. Le parcours complet dure davantage, et dépend de l’ampleur et de la localisation de la dette ; le plan de sortie en fixe les conditions de clôture dès le premier jour.

Deux approches, choisies selon les dégâts

Toute la dette n’appelle pas la même réponse. C’est pourquoi nous travaillons selon deux approches différentes, et nous ne choisissons pas entre les deux a priori.

L’approche principale

Équipe dédiée

Quand la dette bloque la livraison sur des zones centrales du produit

Un binôme de développeurs QMates rejoint quelques développeurs de votre équipe et forme une petite équipe d’experts avec un objectif précis : relancer la livraison là où elle est aujourd’hui à l’arrêt. En travaillant sur le code, l’équipe dédiée transmet les techniques au reste de l’équipe, qui les voit appliquées sur son propre système avant même de les utiliser seule.

L’approche légère

Refactoring opportuniste

Quand les dégâts restent limités, et que le système tient encore le rythme du produit

Les équipes continuent de livrer des fonctionnalités et, avant d’en ajouter une, améliorent le code qu’elle doit toucher. Le refactoring suit la roadmap et se concentre là où va le produit, sans devenir un chantier séparé avec son propre budget.

C’est l’audit de la dette technique, première étape du parcours, qui détermine l’approche à suivre. Il mesure où la dette pèse vraiment et quels dégâts elle cause ; à partir de là, nous décidons s’il faut une équipe dédiée ou si le refactoring opportuniste suffit.

Les techniques que nous utilisons, et à quoi elles servent

Ce sont des techniques connues de quiconque travaille depuis des années sur du code legacy. Ce qui fait la différence, c’est l’ordre dans lequel nous les appliquons, et le critère qui choisit où les appliquer.

  1. 01

    Tests de caractérisation comme filet de sécurité

    Avant de changer une partie du système, nous photographions son comportement actuel avec des tests qui le décrivent tel qu’il est, même là où il est incorrect. Ils disent ce que le système fait aujourd’hui, pas ce qu’il devrait faire, et signalent aussitôt si une modification le change sans le vouloir. C’est la technique décrite par Michael Feathers dans Working Effectively with Legacy Code, et c’est le prérequis de tout le reste.

  2. 02

    Refactoring des hotspots

    La dette se paie là où le code change souvent. Nous croisons l’historique des modifications avec la complexité du code et intervenons d’abord sur les hotspots, les quelques zones où complexité et fréquence de modification se cumulent ; un module compliqué que personne ne touche depuis des années peut rester tel quel.

  3. 03

    Frontières des modules et couplage

    Quand une modification produit traverse dix modules, le problème est dans les frontières. Nous rendons explicites les frontières entre les parties du système, réduisons les dépendances qui les traversent et déplaçons les responsabilités là où le domaine les veut, pour qu’une modification ne touche qu’un seul endroit.

  4. 04

    Strangler Fig pour le monolithe

    Quand une partie du monolithe doit être remplacée, il n’existe pas de jour J du passage. Le nouveau code grandit à côté de l’ancien, derrière une frontière claire, et prend en charge une fonction à la fois, jusqu’à ce que l’ancien ne serve plus ; le monolithe continue de fonctionner pendant tout ce temps.

  5. 05

    Architecture, seulement là où c’est nécessaire

    Microservices, événements ou nouvelles couches ne sont pas un objectif en soi. Nous intervenons sur l’architecture quand une contrainte structurelle empêche le produit d’évoluer, et seulement à cet endroit : un monolithe modulaire aux frontières claires peut tenir des années.

Comment nous mesurons les progrès

Au début du parcours, nous choisissons avec vous quelques indicateurs liés à ce qui coûte trop cher aujourd’hui, et nous les suivons dans la durée. Le choix dépend du système et de ce qui freine le métier ; par exemple :

  • le lead time des modifications
  • les régressions qui arrivent en production
  • le temps nécessaire pour une release
  • le temps pour mettre une nouvelle fonctionnalité en production

Nous ne promettons aucun chiffre avant d’avoir vu le code. Nous promettons de mesurer dès le début, pour que les progrès, ou leur absence, soient visibles de tous.

Là où nous l’avons déjà fait

Deux parcours réels, dans le code de nos clients et avec leurs équipes.

SureVIVE

Le contexte
Un logiciel critique pour la coordination des secours, opérationnel jour et nuit, où une architecture devenue inadaptée et des zones de code legacy bloquaient la croissance du produit et de l’organisation.
Ce que nous avons fait
Un parcours de refactoring qui a travaillé sur la testabilité et le design, et anticipé les fonctionnalités produit qui en avaient besoin, en avançant avec leurs équipes.
Le résultat
Dette technique réduite de 15 %, et des fonctionnalités jusque-là irréalisables devenues possibles, avec de nouvelles opportunités de vente pour le produit.
Lire l’étude de cas En italien

Zextras

Le contexte
Une suite de collaboration B2B développée par plus de trois équipes distribuées, avec une équipe passée de 5 à environ 30 développeurs et une livraison qui s’enrayait souvent.
Ce que nous avons fait
Sur douze mois, des formations de quatre jours sur les pratiques techniques pour chaque équipe, et un binôme QMates dans l’équipe cœur, qui a aidé à construire les feature teams et piloté une partie du refactoring pour extraire la portion de codebase nécessaire.
Le résultat
Bugs en baisse de 15 % et cycle time amélioré ; le lead time est resté inchangé, car les releases suivent une cadence fixe.
Lire l’étude de cas

L’équipe continue seule

Un refactoring réussi laisse un code meilleur et une équipe qui n’a plus besoin de nous. Les techniques passent à l’équipe pendant que nous travaillons ensemble, en binôme sur du code réel, et notre présence diminue à mesure que l’équipe les utilise seule.

Le plan de sortie se décide dès le début

  1. 01Nous fixons quelles compétences doivent rester dans l’équipe, et comment nous saurons qu’elles y sont restées
  2. 02Nous réduisons la présence de QMates par paliers, pendant que l’équipe prend en charge les zones refactorisées
  3. 03Nous laissons des tests, des frontières et des indicateurs que l’équipe sait lire et maintenir
  4. 04Nous clôturons quand l’équipe fait vivre le refactoring dans son travail quotidien

Quand nous déconseillons la réécriture, et quand nous la recommandons

La question qu’on nous pose le plus souvent est de savoir si le système doit être réécrit. Nous répondons presque toujours non. Une réécriture coûte plus cher qu’il n’y paraît, parce qu’elle doit aussi reconstruire la connaissance cachée dans le code existant.

Nous déconseillons la réécriture quand

  • le système est en production et crée de la valeur, même si le modifier demande des efforts
  • le problème se concentre sur quelques zones du code
  • la douleur principale vient du manque de tests ou de connaissance, qu’une réécriture ne résout pas
  • l’entreprise ne peut pas se permettre des mois sans nouvelles fonctionnalités, ni deux systèmes à maintenir en parallèle

Nous envisageons la réécriture quand

  • la technologie ne reçoit plus de mises à jour de sécurité, et devient un risque pour la continuité du service
  • le couplage est si omniprésent qu’aucune partie ne peut être isolée, et chaque modification coûte durablement plus que la valeur qu’elle apporte
  • l’activité change radicalement (un nouveau modèle, une acquisition, une nouvelle plateforme), et le système existant ne sert plus, indépendamment de sa qualité

Même dans ces cas-là, le remplacement se fait par parties, avec le Strangler Fig. Les variables à évaluer avant de décider, nous les avons rassemblées dans le guide sur quand cohabiter avec un logiciel legacy, quand le faire évoluer et quand le réécrire.

Ce que nous ne faisons pas

  • Nous ne réécrivons pas tout de zéro comme premier choix
  • Nous n’arrêtons pas la roadmap pour ouvrir un chantier de refactoring
  • Nous ne transformons pas le refactoring en migration cloud, comme une fin en soi
  • Nous n’offrons pas de garanties génériques : nous préférons de petits pas, vérifiés par des tests et réversibles
  • Nous ne faisons pas que de la formation : nous écrivons du code avec votre équipe
  • Nous ne créons pas de dépendance à notre présence

Nous intervenons là où le changement coûte le plus cher, et nous nous arrêtons quand l’équipe sait avancer seule

C’est le bon moment si

  • Le produit grandit, mais le code freine chaque nouvelle fonctionnalité
  • Vous envisagez une réécriture, et voulez savoir s’il existe une voie moins risquée
  • Vous avez hérité d’un système legacy que l’équipe peine à comprendre et à modifier
  • La dette technique perd toujours la priorité face aux fonctionnalités, et continue pourtant de grandir

Si le problème est au contraire un point technique unique, urgent et circonscrit, la réponse la plus rapide est l’Intervention ciblée.

Questions fréquentes

Travaillez-vous aussi sur du code sans tests ?
Oui, et c’est même le cas le plus fréquent. On part des tests de caractérisation, qui décrivent le comportement actuel du système même là où il est incorrect, et deviennent le filet de sécurité pour modifier le code sans devoir en comprendre tout de suite chaque détail. Les tests grandissent avec le refactoring, en partant des zones les plus souvent touchées.
Écrivez-vous du code, ou faites-vous de la formation ?
Nous écrivons du code. Nous travaillons dans votre repository, en binôme avec vos développeurs, sur de vraies fonctionnalités et de vrais refactorings, et les techniques passent à l’équipe pendant le travail. Quand un moment de formation dédié est utile, nous l’ajoutons au travail sur le code, sans le remplacer.
Comment le refactoring cohabite-t-il avec le développement des fonctionnalités ?
Le refactoring se fait à l’intérieur de la livraison, en parallèle du travail sur les fonctionnalités. Avec le refactoring opportuniste, on améliore le code que la prochaine fonctionnalité doit toucher ; avec l’équipe dédiée, on débloque une zone pendant que le reste de l’équipe continue de livrer. Dans les deux cas, la roadmap ne s’arrête pas.
Travaillez-vous aussi sur les monolithes ?
Oui. Souvent, il suffit de rendre le monolithe modulaire, avec des frontières claires en son sein. Là où une partie doit vraiment être remplacée, nous utilisons le Strangler Fig : le nouveau code grandit à côté de l’ancien et prend en charge une fonction à la fois, pendant que le monolithe continue de fonctionner.
Combien de temps dure un parcours de refactoring du code legacy ?
Les premiers progrès mesurables arrivent après trois à six mois au moins. La durée totale dépend de l’ampleur et de la localisation de la dette, et le plan de sortie, avec ses conditions de clôture, se définit dès le début.
Par où commence-t-on ?
Par l’audit de la dette technique, qui produit une cartographie de la dette avec ses priorités et un plan d’action, et indique aussi quelle approche de refactoring est nécessaire. Si votre équipe préfère avancer seule avec ce plan, elle le peut.

Le code legacy freine chaque nouvelle fonctionnalité ?

Nous commençons par un appel gratuit : vous nous présentez votre système, et nous vous disons s’il faut d’abord un audit

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