Les anciens systèmes ont deux choses en commun : ils fonctionnent, et ils rendent tout le monde nerveux. Ils fonctionnent parce que des années d'usage réel les ont façonnés autour de l'activité concrète. Ils rendent les gens nerveux parce que personne ne veut y toucher, la documentation est mince, et l'un des développeurs d'origine est parti il y a trois emplois.
Ce qui compte réellement comme "legacy"
Tout vieux système n'est pas un système legacy. Une application de 10 ans tournant sur une stack supportée, bien testée, avec une maintenance active, est simplement mature. Legacy signifie généralement :
- Des versions de plateforme ou de framework qui ne reçoivent plus de mises à jour de sécurité.
- Une connaissance concentrée dans une ou deux personnes.
- La peur de changer les choses parce que de petits changements cassent des fonctionnalités lointaines.
- Des déploiements manuels, des tests manuels, tout en manuel.
- Un écart qui se creuse entre ce dont l'entreprise a besoin et ce que le système peut faire.
Les deux approches qui échouent
- Ne rien faire. Le risque s'accumule. Finalement, quelque chose de critique casse à un mauvais moment.
- Réécrire de zéro. Une réécriture big-bang prend presque toujours plus de temps que prévu, découvre des exigences cachées trop tard, et tourne en parallèle de l'ancien système si longtemps que l'équipe abandonne.
Les approches qui fonctionnent réellement se situent entre ces deux extrêmes.
Stratégie 1 : étrangler l'ancien système
Gardez le système legacy en fonctionnement, mais acheminez progressivement les nouvelles fonctionnalités à travers une nouvelle couche. Au fil du temps, de plus en plus de fonctionnalités vivent dans le nouveau système ; l'ancien rétrécit jusqu'à être finalement éteint.
Cela fonctionne pour les sites web (déplacer page par page), les outils internes (déplacer module par module) et les intégrations (introduire une nouvelle passerelle d'API, puis y rediriger les appelants).
Stratégie 2 : moderniser sur place
Parfois, l'architecture est correcte, mais la stack est trop ancienne. Dans ce cas, mettez à niveau une couche à la fois : langage/runtime, puis bibliothèques, puis framework, puis déploiement. Ajoutez des tests automatisés au fur et à mesure, pour que chaque étape soit vérifiable. Pas glamour, mais très efficace.
Stratégie 3 : découper, puis moderniser
Les grands systèmes monolithiques sont souvent plus faciles à moderniser pièce par pièce une fois les coutures claires. Cela signifie d'abord définir des frontières à l'intérieur du système existant (modules, contextes délimités), puis remplacer les modules indépendamment. Les frontières comptent plus que le choix de la technologie.
Que faire avant d'écrire la moindre ligne de code
- Notez ce que le système fait réellement aujourd'hui, pas ce qu'il était censé faire.
- Identifiez les fonctionnalités dont les gens dépendent quotidiennement, elles doivent continuer de fonctionner pendant la transition.
- Identifiez les fonctionnalités mortes, elles représentent souvent une part significative du code et devraient être discrètement retirées.
- Faites l'inventaire des données, intégrations, autorisations et exports. C'est là que les projets de modernisation rencontrent généralement des difficultés.
- Décidez de ce que "terminé" signifie en termes mesurables.
Des contrôles de risque qui font une vraie différence
- Sauvegardes et restaurations testées, avant tout changement.
- Des tests automatisés pour le comportement qui vous importe, introduits progressivement.
- Des feature flags pour que les changements puissent être annulés en production sans déploiement.
- De l'observabilité, journaux, suivi des erreurs, indicateurs métier clés, pour découvrir rapidement si quelque chose a régressé.
- Un déploiement progressif : le nouveau code tourne d'abord pour les utilisateurs internes, puis un petit pourcentage d'utilisateurs réels, puis tout le monde.
Le patron strangler fig. Au lieu d'une réécriture big-bang risquée, vous acheminez le trafic à travers une fine façade qui transfère progressivement de plus en plus de requêtes vers de nouveaux services, pendant que le système legacy continue de traiter le reste jusqu'à ce qu'il soit vide.
Exemple : une route de façade minimale
Une petite passerelle peut décider, route par route, si une requête va vers le monolithe legacy ou vers le nouveau service. Écrit de manière neutre vis-à-vis du framework :
app.use((req, res, next) => {
if (req.path.startsWith('/api/v2/invoices')) {
return proxy(req, res, NEW_SERVICE_URL);
}
return proxy(req, res, LEGACY_URL);
});
Chaque module migré déplace un préfixe de LEGACY_URL vers NEW_SERVICE_URL. Les utilisateurs ne voient jamais de bascule big-bang ; l'équipe livre de petites étapes réversibles.
Piège courant : écrire le nouveau système et l'ancien comme s'ils devaient être équivalents en fonctionnalités dès le premier jour. Ce n'est pas le cas. Commencez par la plus petite pièce ayant une réelle valeur métier, prouvez le chemin de migration, puis répétez.
Erreurs courantes
- Faire de la modernisation une affaire de choix technologique. Les belles nouvelles stacks n'aident pas si le modèle de données et les intégrations restent mystérieux.
- Attendre une refonte complète avant de corriger les problèmes visibles. Les utilisateurs en paient le prix ; l'élan meurt.
- Éteindre l'ancien système trop tôt. Faites tourner les deux en parallèle assez longtemps pour faire confiance au nouveau.
- Perdre le savoir institutionnel. Travaillez en binôme avec les gens qui connaissent le système. Leur connaissance est le véritable atout.
Un calendrier réaliste
- Stabiliser : sauvegardes, surveillance, correctifs de sécurité, tests minimaux.
- Cartographier : documenter ce qui existe et ce qui est réellement utilisé.
- Prioriser : choisir la pièce pénible maintenant et utile à moderniser en premier.
- Remplacer : construire la nouvelle version de cette pièce à côté de l'ancienne.
- Basculer : acheminer le trafic, surveiller, corriger les problèmes.
- Retirer : ne mettre l'ancienne pièce hors service qu'après qu'elle soit restée silencieuse un certain temps.
- Répéter pour la pièce suivante.

