Guide

Moderniser une application Legacy

Une approche pragmatique pour restructurer du code sans accroc

Moderniser une application legacy est une tâche délicate car le code est souvent fragile et indigeste, pas (ou insuffisamment) testé, et a généralement accumulé des années de logique métier rarement documentées...

Les tentations sont alors nombreuses : tout réécrire "proprement", adopter une architecture cible complexe trop rapidement ou tenter de résorber immédiatement l'intégralité de la dette technique... mais elles sont souvent des erreurs.


Le pragmatisme doit donc guider chaque étape de ce processus : l'objectif final peut être ambitieux — clean code, architecture robuste (Clean Architecture, DDD, CQRS...) — mais ne doit pas être une marche forcée vers la perfection.

Un refactoring réussi, c'est souvent des semaines, des mois ou des années de petits progrès cohérents qui tiennent compte des priorités business, des contraintes extérieures, du temps d'adaptation de l'équipe et des risques inhérents à chaque étape.

Chaque situation étant différente, cet article propose un cadre de réflexion et non une recette miracle : le bon sens et l'adaptation resteront toujours les meilleures boussoles !

Les pièges de la réécriture intégrale

Face à un code vieillissant et à l'ampleur de la tâche, la tentation de la réécriture « from-scratch » est forte : repartir de zéro, proprement, sans le poids du passé. Mais cette stratégie est risquée : date de sortie sans cesse repoussée, dépérissement du projet actif...

Un exemple célèbre est celui de Netscape [1] :

En 1998, l'équipe décide de réécrire intégralement le navigateur ; après presque trois longues années de développement (et un budget conséquent), Netscape 6 sort mais est jugé inférieur à la version 4 qu'il devait remplacer... Pendant ce temps, Internet Explorer avait déjà capturé le marché ; l'inaction compétitive imposée par la réécriture lui aura été fatale.

La réécriture pose également un problème fondamental : le code legacy, aussi illisible soit-il, contient des années de connaissance métier implicite : des cas limites gérés sans documentation, des années de bugs corrigés ou devenus des règles de gestion, des comportements sur lesquels des processus entiers reposent... Une réécriture perd tout cela.

C'est pourquoi, une approche plus prudente consiste à remplacer progressivement le code existant (refactoring), sans altérer les fonctionnalités existantes ni interrompre le service.

Prévenir les régressions

Aucun refactoring d'ampleur ne devrait débuter sans tests automatisés, car ils constituent le filet de sécurité permettant de modifier le code existant en toute confiance : toute introduction de bugs serait en effet immédiatement détectée. Ils servent également de documentation explicite autant que fonctionnelle.

Les tests unitaires sont précis et rapides, mais souvent délicats à mettre en place sur du code legacy : couplages forts, classes tentaculaires, absence d'interfaces... C'est pourquoi on se contente parfois d'une base minimum pour sécuriser les fonctionnalités les plus critiques, complétée par des tests plus larges couvrant des comportements observables — tels que les tests d'intégration, fonctionnels, ou de caractérisation (« Golden Master »).

Les tests unitaires exhaustifs viendront ensuite progressivement, au fur et à mesure que le code sera restructuré et rendu testable. Ils nécessitent du temps mais constituent un investissement à long terme qui portera ses fruits à chaque modification future.

Quand la réécriture s'envisage malgré tout

Le refactoring progressif a néanmoins ses limites : sur une codebase très ancienne ou technologiquement obsolète, les contraintes de l'existant peuvent freiner chaque évolution au point où l'on passe plus de temps à contourner le legacy qu'à faire avancer le produit, justifiant ainsi un changement radical de technologie [2]. Dans ces cas, la réécriture peut redevenir pertinente — à condition d'adopter également une approche progressive [3].

C'est là qu'intervient le patron « Strangler Fig [4] » (Figuier étrangleur), conceptualisé par Martin Fowler. Son nom s'inspire d'une plante tropicale parasite qui pousse autour d'un arbre existant, l'enveloppant progressivement jusqu'à le recouvrir entièrement.

Appliqué au logiciel, le principe est le même : on développe la nouvelle application en parallèle du legacy, en basculant les fonctionnalités une par une vers le nouveau système. Le legacy reste en production et pleinement fonctionnel à chaque instant — un rollback est donc aussi toujours possible. Progressivement privé de trafic, l'ancien système s'éteindra de lui-même lorsque la migration sera complète.

L'aspect humain

C'est une dimension souvent sous-estimée — pourtant la modernisation d'une application legacy implique presque toujours la découverte de nouveaux principes et méthodes de travail pour une bonne partie de l'équipe. Par exemple, adopter le DDD ou la Clean Architecture demande en effet un temps d'apprentissage et d'adaptation non négligeable, qui doit être anticipé et intégré au planning.

Le pair programming est un outil précieux dans ce contexte : il permet de transmettre la connaissance du code existant, d'aligner les pratiques, et d'accélérer la montée en compétences. Les code reviews jouent un rôle similaire, en diffusant progressivement les nouveaux standards ou des conseils au sein de l'équipe.

Mon expérience

Les tests sont le filet de sécurité indispensable au refactoring et à l'évolutivité d'une application. Or, une équipe qui n'a pas l'habitude de tester risque de reconstruire, malgré un code techniquement plus propre, une application fragile et difficile à faire évoluer...

Le refactoring est l'occasion de rompre avec le cycle du legacy et pas seulement de nettoyer le code. Cela se prépare : montée en compétences de l'équipe sur les tests, définition de standards clairs, mise en place de revues de code et des pratiques de test dès le départ, et l'assurance que la culture du "on écrira les tests plus tard" ne s'installe pas à nouveau.


Une transition réussie requiert une réelle volonté de toute l'organisation — pas seulement des développeurs.

Les décideurs ont tout intérêt à embrasser de nouvelles méthodes de développement, même si les bénéfices concrets — une équipe plus productive et un code plus fiable et facile à faire évoluer — ne seront visibles qu'à moyen terme, un argument parfois difficile à défendre côté métier où la pression business impose souvent des décisions précipitées.

Références

Pour aller plus loin

  • Anna Filina — conférences et articles sur le testing de code legacy (disponibles sur YouTube et son blog )
  • 📔 Martin FowlerRefactoring: Improving the Design of Existing Code (2e éd.)
  • 📔 Michael FeathersWorking Effectively with Legacy Code (2004)