Développement web

Comment introduire des tests dans un legacy sans bloquer tout le delivery

Jérémy Chomel Dawap
  • Publié le : 17 décembre 2025
  • Mis à jour le : 19 août 2026
  • Temps de lecture : 12 minutes
  1. Comprendre l’écart autour du quality gate
  2. Caractériser le comportement avant de le corriger
  3. Attribuer le verdict sur les contrats externes
  4. Confronter le simulateur au sandbox partenaire
  5. Extraire l’invariant sans doubler l’effet
  6. Remplacer un test legacy lent sans perdre sa protection
  7. Piloter avec le délai de correction
  8. Comparer ancien et nouveau chemin avant bascule
  9. Faire exécuter la recette par l’expert métier
  10. Pour qui cette progression convient
  11. Étendre la protection sans geler le delivery
  12. Éviter les erreurs fréquentes de test d’un legacy
  13. Arbitrer avec la preuve de non-régression
  14. Plan d’action : installer un harnais sans bloquer le delivery
  15. Guides complémentaires pour fiabiliser le harnais legacy
  16. Conclusion : protéger le changement avant de viser la couverture
Portrait de Jérémy Chomel

Une application de gestion vieille de douze ans calcule correctement les commissions, mais personne n’ose modifier la méthode centrale de 1 800 lignes. Le delivery continue par correctifs autour du bloc, chaque urgence ajoute une condition et la recette métier dure deux jours. Exiger une couverture globale avant le prochain besoin arrêterait le produit ; continuer sans protection augmente pourtant le coût de chaque changement.

Le problème se voit quand l’équipe confond absence de tests et obligation de réécrire. Le legacy contient des comportements utiles, des anomalies connues et des dépendances implicites. Les figer tous aveuglément transformerait les défauts en spécification ; les ignorer maintiendrait la peur et les vérifications orales.

La thèse consiste à poser des filets autour des zones touchées et des incidents coûteux, puis à créer des points d’observation avant d’extraire les règles. Contre-intuitivement, le premier test peut décrire un comportement imparfait : il sert alors de caractérisation temporaire, clairement nommé, avant une décision métier explicite.

Dans une démarche de développement web sur mesure, l’introduction des tests suit le flux de changement. Chaque ticket laisse le code un peu plus observable, une frontière un peu mieux maîtrisée et un scénario de reprise supplémentaire. Le delivery reste ouvert, mais aucun nouveau risque critique n’est accepté sans preuve ni owner.

Cette méthode permet de décider où poser le premier harnais, quel comportement caractériser temporairement et quelle extraction peut être livrée sans engager une réécriture globale.

Comprendre l’écart autour du quality gate

Commencer par le changement qui arrive

Dans un legacy, une gate globale de couverture bloque souvent sans distinguer code actif et zone oubliée. Le diagnostic croise incidents, fréquence de modification, conséquence et difficulté de reprise. La première protection vise le prochain ticket ou un défaut coûteux, pas les fichiers les plus faciles à tester.

Le pilote mesure le code modifié, les chemins caractérisés et le temps de diagnostic. Un seuil local peut exiger une preuve sur chaque règle critique touchée sans imposer immédiatement un pourcentage à tout le dépôt. La gate grandit avec la capacité de l’équipe à maintenir fixtures et environnements.

Caractériser le comportement avant de le corriger

Avant de refactorer, un test de caractérisation capture entrée, sortie et effets observables. Il ne déclare pas le comportement correct ; son nom indique ce qui est historique et ce qui devra être arbitré. Une anomalie connue n’est pas transformée en règle sans décision métier.

La promesse durable est ensuite isolée dans un test plus lisible. Par exemple, un calcul de commission peut être exécuté en miroir sans écrire, puis ses divergences sont classées avec l’expert métier. Le changement reste borné tant que chaque écart n’a pas une explication.

Attribuer le verdict sur les contrats externes

L’expert métier qualifie le résultat ; le développeur construit la couture ; le QA maintient les données ; l’exploitation valide reprise et visibilité. Pour un contrat externe, son owner confirme version et erreurs supportées. Un mock écrit d’après le legacy ne suffit pas à prouver le protocole réel.

Pendant un incident, la caractérisation aide à reproduire mais ne justifie pas automatiquement le statu quo. L’équipe compare version livrée, donnée, appel externe et effet durable. Le rollback ou la marche avant suit le runbook ; aucun acteur ne corrige directement la base pour rendre le test vert.

Confronter le simulateur au sandbox partenaire

Le sandbox vérifie quelques contrats et scénarios rares avec une identité dédiée. Les réponses utiles sont nettoyées puis transformées en fixtures versionnées. La suite courante ne dépend pas de sa disponibilité, mais une dérive du contrat ouvre un signal visible avant la promotion.

Le simulateur provoque timeout, erreur et réponse inconnue ; le sandbox confirme forme, authentification et comportements annoncés. Cette combinaison évite un test lent pour chaque merge sans inventer un partenaire parfaitement conforme aux hypothèses du code.

Extraire l’invariant sans doubler l’effet

Une première couture entoure lecture, décision et écriture. Horloge, identifiant et client externe deviennent injectables sans extraire toute l’application. Le test vérifie l’état avant et après, puis rejoue la commande avec la même clé pour détecter un double effet.

Les transactions et contraintes réelles restent présentes lorsque leur comportement protège l’invariant. Le refactoring se fait par pas : caractériser, extraire, comparer, basculer, supprimer l’ancien chemin. Chaque étape garde une version déployable et une marche de repli connue.

Remplacer un test legacy lent sans perdre sa protection

Ne pas retirer le seul filet avant son remplacement

Un test legacy lent peut être mauvais mais protéger une conséquence inconnue. Avant quarantaine, l’équipe relie ses assertions aux incidents et extrait une preuve plus ciblée. Le contrôle désactivé garde propriétaire, compensation et expiration ; son premier rouge n’est pas effacé par une relance.

Par exemple, un test de clôture mensuelle de trente minutes peut être découpé en intégrations sur calcul, verrou et export, puis conserver un seul parcours assemblé sur une fixture représentative. Le gain est accepté seulement si un défaut injecté déclenche encore le bon signal.

Piloter avec le délai de correction

Suivre les zones touchées

Le tableau associe fichiers actifs, incidents, preuves ajoutées, temps avant signal et temps de diagnostic. La couverture différentielle peut signaler un oubli, sans devenir une cible isolée. Une zone stable sans enjeu immédiat attend ; une zone modifiée plusieurs fois avec défauts échappés devient prioritaire.

Les seuils sont locaux et révisables. Le pilote peut limiter le harnais à une demi-journée par ticket ; si ce budget ne suffit pas, il réduit le changement ou documente une dette datée. Il n’autorise pas une règle critique sans aucune preuve parce que l’ancien code serait difficile.

Comparer ancien et nouveau chemin avant bascule

Rendre comparaison et bascule observables

Les entrées sont fixture versionnée, configuration et identifiant de cas. Les sorties comparent ancien et nouveau chemin, effets durables et appels externes. Le rapport de couverture reste secondaire ; la journalisation explique chaque divergence sans donnée sensible. Le monitoring de recette sépare les deux implémentations.

Le rollback conserve l’ancien chemin tant que les données nouvelles restent compatibles. Si une migration empêche le retour, le plan utilise expand-contract ou marche avant contrôlée. Le runbook indique seuil de divergence, personne autorisée et nettoyage du code historique après stabilisation.

Faire exécuter la recette par l’expert métier

L’expert reçoit des cas réels anonymisés ou synthétiques, leur ancien résultat et la proposition. Il classe identique, correction volontaire ou divergence à expliquer. Il ne manipule pas PHPUnit ; son verdict est traduit en exemples versionnés par l’équipe.

Les opérations jouent ensuite timeout, reprise et rollback. Une validation fonctionnelle ne prouve pas l’absence de double effet. Les deux lectures partagent la même corrélation afin que le dossier reste transmissible du métier au support.

Pour qui cette progression convient

Elle convient aux applications actives dont la réécriture globale serait trop risquée et aux équipes qui doivent continuer à livrer. Un code stable destiné à disparaître peut recevoir seulement un filet autour de ses interfaces ; un calcul financier fréquemment modifié mérite une extraction plus profonde.

Le sponsor accepte une progression asymétrique. Développement, QA, métier et exploitation réservent du temps sur les zones actives. Si aucun expert ne peut qualifier le comportement, la caractérisation le conserve temporairement et le changement reste limité.

Étendre la protection sans geler le delivery

Appliquer une règle au code touché

Une politique locale peut exiger qu’une règle modifiée gagne au moins un exemple nominal, une limite et une reprise, tandis qu’un incident gagne son scénario reproductible avant fermeture. Le périmètre exact dépend de la criticité. L’équipe ne promet pas une couverture uniforme du dépôt ; elle refuse que la zone active continue à s’endetter sans preuve.

Pour un pilote de six semaines, elle peut suivre le délai de diagnostic, les régressions sur zones touchées et le nombre de caractérisations remplacées par des tests métier explicites. Un seuil de merge ou une durée maximale de suite reste local à l’infrastructure observée. S’il pousse à écrire des assertions sans valeur, il est revu plutôt que célébré.

Séparer caractérisation, décision et migration

La documentation officielle de PHPUnit sur les doubles distingue plusieurs mécanismes ; leur usage reste limité aux collaborations que le test veut réellement contrôler. Un mock de toute l’application fabriquerait une réalité parallèle et rendrait le refactoring plus dangereux.

Avant une extraction, le harnais prouve l’ancien comportement sur des données représentatives. Le nouveau composant exécute les mêmes exemples, puis une comparaison contrôlée révèle les divergences. Le basculement commence sur un sous-ensemble réversible avec journalisation. Si les résultats diffèrent hors décisions approuvées, le routeur revient à l’ancien chemin sans perdre les opérations créées pendant l’essai.

Cas concret hypothétique. Un batch de commissions de 4 000 lignes doit intégrer une nouvelle catégorie de vendeur. L’équipe ne réécrit pas le batch. Elle capture vingt dossiers synthétiques couvrant seuils, annulation et arrondis, injecte l’horloge, puis exécute ancien et nouveau calcul en miroir. Trois divergences révèlent des exceptions historiques ; le métier en conserve une et corrige les deux autres. Le nouveau chemin s’ouvre à un seul réseau avec un seuil local de divergence nul sur les règles validées.

Le lot suivant extrait l’écriture comptable derrière une interface mais conserve la vraie transaction dans l’intégration. Le rollback réactive l’ancien calcul tant que les écritures restent compatibles. Si un dossier ne peut pas être rapproché, alors l’extension s’arrête et la sortie nouvelle n’est pas persistée. Cette progression produit une connaissance exploitable sans immobiliser les tickets voisins.

L’arbitrage compare trois options : caractériser seulement, extraire une couture ou refondre le flux. La première limite le risque immédiat ; la deuxième améliore la testabilité et garde un repli ; la troisième n’est retenue que si les contrats sont compris et le coût de coexistence supérieur. Le comité choisit par conséquence, fréquence de changement et capacité de diagnostic, jamais par attrait pour le code neuf.

La base legacy demande une attention particulière. Une transaction de test accélère l’isolation, mais masque parfois le comportement d’un worker qui ouvre une seconde connexion. Les scénarios qui protègent verrou, unicité ou reprise utilisent alors le moteur réel et un nettoyage explicite. Un dump complet n’est pas requis : la fixture conserve uniquement les relations nécessaires et sa création reste lisible.

Les sorties indirectes sont recensées avant l’extraction : fichier, email, événement, cache et export. Le mode miroir neutralise ces effets ou les écrit dans une destination de comparaison. Si l’ancien et le nouveau chemin publient tous deux, le pilote créerait le double effet qu’il cherche à prévenir. La responsabilité de chaque sortie est donc fermée avant d’augmenter le volume.

Les performances sont comparées sur un jeu représentatif. Une règle plus testable qui multiplie les requêtes peut rester acceptable sur vingt cas mais échouer sur le batch mensuel. Le seuil local porte durée, mémoire et charge SQL observées. Au-delà, l’équipe optimise ou garde l’ancien chemin ; elle ne présente pas la seule correction fonctionnelle comme un go complet.

La documentation du flux reste proche du test et du code extrait. Elle nomme ce qui est caractérisé sans être validé, les exceptions encore orales et la date de la prochaine décision. Une personne nouvellement arrivée doit pouvoir distinguer dette connue, anomalie volontairement conservée et comportement cible. Cette clarté évite que la protection temporaire fige le legacy pour une nouvelle décennie.

Les fixtures temporaires reçoivent elles aussi un propriétaire et une date de revue. Quand l’extraction devient stable, l’équipe garde les exemples métier essentiels, retire les captures redondantes et vérifie une reconstruction propre. Le harnais reste ainsi plus simple que le code qu’il aide à transformer.

Éviter les erreurs fréquentes de test d’un legacy

Imposer une couverture globale immédiate

La cible récompense les fichiers faciles et bloque les changements sans traiter les risques. Une gate différentielle sur le code touché et une liste d’invariants critiques construisent une progression défendable. Le pourcentage reste un signal, pas le verdict.

Tester l’implémentation historique

Des assertions sur méthodes privées figent le refactoring. Le harnais observe entrées, sorties et effets aux coutures. Les caractérisations temporaires sont nommées comme telles, puis remplacées par des règles métier lorsque le comportement est arbitré.

Lancer une réécriture parallèle

Deux systèmes évoluent et la comparaison arrive trop tard. Une extraction par flux, exécutée en miroir, conserve une seule livraison et rend chaque divergence visible. L’ancien chemin est supprimé après preuve, pas après impression.

Arbitrer avec la preuve de non-régression

La matrice combine fréquence de changement, conséquence, incidents, observabilité et coût d’extraction. Une zone active et irréversible passe avant une grande classe stable. Le premier test peut être une caractérisation si le métier ne peut pas encore décider, mais il porte cette limite explicitement.

Une extraction s’étend lorsque données représentatives, comparaison et repli restent maîtrisés. Si les divergences ne sont pas explicables ou si le nouveau chemin exige une migration irréversible, le périmètre demeure en miroir. Le delivery continue sur des lots plus petits.

Plan d’action : installer un harnais sans bloquer le delivery

D’abord, choisir un flux actif

L’équipe prend le prochain besoin ou un incident récurrent, puis décrit entrée, sortie, effets, dépendances et reprise. Elle mesure fréquence, conséquence et diagnostic. Une caractérisation reproduit le comportement actuel sur quelques cas représentatifs, sans prétendre valider les anomalies historiques.

Ensuite, créer une couture locale

Horloge, client externe ou persistance sont isolés au bord du flux. Les vraies contraintes restent dans une intégration lorsqu’elles portent la protection. Le code ancien continue de fonctionner ; le nouveau composant peut être testé avec un contrat clair. La CI ajoute une gate limitée au périmètre touché.

Puis, comparer et basculer

L’ancien et le nouveau chemin s’exécutent en miroir sans double écriture. Les divergences portent identifiant, données, résultats et règle supposée. L’expert métier les classe. Une population bornée reçoit ensuite le nouveau chemin avec métriques et seuil d’arrêt ; le rollback est joué.

Enfin, retirer la dette remplacée

Après stabilisation, l’équipe supprime ancien code, flag, fixtures temporaires et dérogations. Elle conserve les tests de promesse et de reprise. Le tableau suit zones actives protégées, défauts échappés et diagnostic, puis sélectionne le prochain flux selon le risque plutôt que selon la facilité.

La revue finale est menée par une personne qui n’a pas écrit l’extraction. Elle reproduit une divergence, retrouve sa classification et exécute le repli. Elle confirme aussi que la gate ne dépend pas d’un ordre de tests ni d’une donnée locale. Si cette autonomie manque, le flux reste dans le périmètre pilote et l’équipe corrige fixture, signal ou documentation avant la prochaine extension.

  1. D’abord, caractériser le flux qui doit réellement changer.
  2. Ensuite, extraire une couture sans réécriture globale.
  3. Puis, comparer sur des données représentatives et jouer le repli.
  4. Enfin, supprimer l’ancien chemin avant d’étendre la démarche.

Guides complémentaires pour fiabiliser le harnais legacy

Tester la couture sans figer le code

La documentation PHPUnit sur les test doubles aide à isoler une dépendance. Dans le legacy, la vraie contrainte reste exécutée si elle porte l’invariant ; la doublure sert surtout à provoquer une erreur contrôlée.

Les tests de workflows à exceptions permettent de transformer les cas historiques en matrice avant extraction, sans créer un end-to-end par branche.

Préparer l’ancienne et la nouvelle version

La migration Symfony sans casser le run apporte les étapes de coexistence nécessaires au miroir et au repli.

Le guide de monitoring aide à comparer les deux chemins sur une population bornée, avec un seuil local de divergence.

  • Caractériser l’effet observable avant extraction.
  • Comparer les deux chemins sans double écriture.
  • Supprimer flag et ancien code après stabilisation.

Conclusion : protéger le changement avant de viser la couverture

Tester un legacy sans bloquer le delivery demande une progression asymétrique : beaucoup de protection autour des zones actives et critiques, peu d’effort sur le code stable sans enjeu immédiat. Les caractérisations donnent un point d’appui, les tests métier clarifient ensuite la décision et les coutures locales rendent le refactoring observable.

Le premier lot peut partir d’un incident récurrent et du prochain besoin, installer un harnais, jouer la reprise puis mesurer le diagnostic. Le périmètre grandit uniquement quand les fixtures et le run restent maîtrisés.

Dawap peut vous accompagner pour cadrer cette trajectoire dans une application web sur mesure, en maintenant les livraisons utiles tout en transformant progressivement la connaissance orale en preuves que l’équipe sait exécuter et réparer.

Portrait de Jérémy Chomel

Vous avez un projet de
développement sur mesure ?

Dawap transforme ce besoin en périmètre livrable, architecture maintenable et trajectoire de mise en production adaptée à vos contraintes.

Besoin d’échanger sur votre projet ? Planifier un rendez-vous

Articles recommandés

Observabilité fonctionnelle d’un workflow métier de bout en bout Développement web Observabilité d’un workflow métier : voir le dossier réel Lire l'article
  • 17 juillet 2026
  • Lecture ~17 min

Logs techniques et disponibilité ne suffisent pas. Instrumentez états, transitions, décisions, délais et reprises pour expliquer où un dossier métier s’est réellement bloqué. Le guide relie événements fonctionnels, traces, métriques, alertes et modes opératoires sans transformer les données personnelles en identifiants de corrélation.

Stratégie de test d’un workflow métier à nombreuses exceptions Développement web Tester un workflow complexe sans explosion combinatoire Lire l'article
  • 17 juillet 2026
  • Lecture ~17 min

Testez les états, transitions, invariants, droits, données et reprises qui portent le risque réel, au lieu de multiplier des scénarios impossibles à maintenir. Cette méthode construit une couverture défendable, injecte les pannes utiles et vérifie aussi les compensations, la concurrence et les preuves attendues par le métier.

Migration progressive d’une application Symfony sans interruption du run Développement web Migration Symfony : monter de version sans casser le run Lire l'article
  • 16 juillet 2026
  • Lecture ~14 min

Une montée de version Symfony touche PHP, dépendances, configuration, données, sessions, cache, Messenger, crons et contrats API. Ce guide propose une trajectoire progressive, une baseline de tests, des critères de retour arrière et une matrice go ou no-go pour moderniser l’application sans confondre migration du framework et refonte métier.

Performance et monitoring d’une application métier Développement web Performance et monitoring d’une application métier Lire l'article
  • 20 janvier 2025
  • Lecture ~45 min

La performance d’une application métier se juge sur la tâche accomplie, pas sur une moyenne globale. Reliez latence, erreurs, saturation et signaux métier, puis définissez les alertes qui déclenchent une action. Traces, métriques et journaux deviennent alors un outil de diagnostic, de dégradation maîtrisée et de reprise.