Migrer un projet PHP vieillissant sans disposer d'une suite de tests digne de ce nom, c'est un peu comme désamorcer une bombe les yeux bandés. Pourtant, avec la montée en puissance des outils IA d'assistance au développement, la pression pour moderniser ces bases de code s'intensifie. Encore faut-il poser les bons fondements avant de laisser un agent réécrire quoi que ce soit.
Cet article est une checklist pratique, ordonnée et orientée terrain, inspirée d'un article publié sur dev.to par Andrii Yakovlev. L'objectif : vous donner les actions concrètes à enchaîner pour refactorer du code PHP legacy en toute sécurité.
Étape 1 — Poser un filet de sécurité : les golden-master tests
Avant de toucher une seule ligne de code métier, il faut capturer le comportement actuel du système, même s'il est bancal. C'est le principe des characterization tests (ou golden-master tests) : vous enregistrez les sorties existantes pour pouvoir détecter toute régression future.
Concrètement :
- Identifiez les points d'entrée critiques (contrôleurs, services appelés depuis l'extérieur, scripts CLI).
- Capturez leurs sorties dans des fichiers de référence (HTML, JSON, texte brut).
- Rejouez ces appels en CI et comparez les sorties caractère par caractère.
Ce n'est pas une suite de tests unitaires propre — et ce n'est pas le but. C'est un filet de sécurité qui vous dira quelque chose a changé, à charge pour vous de décider si c'est voulu ou non.
⚠️ Piège classique : ne pas versionner les fichiers de référence. Sans eux dans le dépôt, vos golden tests ne servent à rien en équipe.
Étape 2 — Intégrer l'analyse statique en CI : PHPStan et Psalm
Une fois le filet posé, il est temps d'allumer les lumières. PHPStan et Psalm analysent votre code sans l'exécuter et remontent des centaines de problèmes potentiels : types incohérents, variables non définies, méthodes appelées sur null, etc.
Stratégie d'intégration progressive :
- Lancez PHPStan au niveau 0 (le plus permissif) sur l'ensemble du projet.
- Corrigez les erreurs remontées, ou mettez-les en baseline.
- Montez progressivement le niveau d'analyse (de 0 à 9) à chaque sprint.
- Bloquez la CI si le nombre d'erreurs augmente par rapport à la baseline.
Cette approche évite le syndrome du « on ne peut pas activer l'outil parce qu'il remonte 3 000 erreurs ». Vous progressez sans vous noyer.
Psalm apporte en complément une inférence de types plus agressive et une détection des flux de données dangereux. Les deux outils sont complémentaires ; commencez par PHPStan si vous devez choisir.
Étape 3 — Automatiser les corrections sûres avec Rector
Rector est un outil de refactoring automatisé pour PHP. Il applique des règles de transformation de code vérifiées, reproductibles et testables. C'est lui qui va effectuer le gros du travail mécanique : migration de syntaxe, modernisation des types, suppression de patterns obsolètes.
Exemples de règles utiles en contexte legacy :
- Migration PHP 7.x → 8.x (types union,
match,nullsafe operator). - Suppression des
array()au profit de[]. - Ajout des déclarations de types de retour manquantes.
- Conversion des annotations
@param/@returnen types natifs PHP 8.
Workflow recommandé :
# Dry-run : voir ce que Rector ferait sans modifier les fichiers
vendor/bin/rector process src --dry-run
# Application réelle
vendor/bin/rector process src
Appliquez Rector par petits lots, relancez vos golden tests après chaque passe, et commitez. Ne laissez jamais Rector transformer l'ensemble du projet en une seule fois sans validation intermédiaire.
⚠️ Piège avec l'IA : si vous demandez à un agent de réécrire du code sans golden tests ni analyse statique en amont, vous obtiendrez du code qui a l'air propre mais dont vous ne pouvez pas garantir l'équivalence comportementale. Les hallucinations ne concernent pas que les faits — elles touchent aussi la logique métier.
Étape 4 — Extraire les nouvelles fonctionnalités via le Strangler Pattern
Plutôt que de réécrire le monolithe en une fois (approche vouée à l'échec), le strangler fig pattern consiste à faire cohabiter l'ancien et le nouveau système, en redirigeant progressivement le trafic vers de nouveaux modules.
Dans un contexte PHP/Symfony :
- Exposez une nouvelle API REST ou GraphQL pour les fonctionnalités à moderniser.
- Faites pointer le front (ou les clients internes) vers cette nouvelle API.
- L'ancien code reste actif pour les fonctionnalités non encore migrées.
- Une fois une fonctionnalité migrée et validée en production, retirez l'équivalent legacy.
Cette approche vous permet de livrer de la valeur continuellement sans bloquer les développements métier pendant 6 mois de « grand refactoring ».
La revue humaine reste non négociable
Quel que soit le niveau d'automatisation atteint — Rector, agents IA, pipelines CI — la revue humaine de chaque changement structurel reste essentielle. Les outils automatisés ne comprennent pas le contexte métier. Ils ne savent pas pourquoi ce if imbriqué à quatre niveaux existe, ni quelle contrainte réglementaire il encode.
Mettez en place des règles de revue claires :
- Tout changement généré par IA ou par Rector doit être relu par un développeur qui connaît le domaine.
- Les golden tests doivent passer avant qu'une PR ne soit mergée.
- Les montées de niveau PHPStan sont décidées en équipe, pas appliquées en solitaire.
Conclusion
Moderniser du code PHP legacy n'est pas une question de courage ou de budget — c'est une question de méthode. La séquence est claire : golden-master tests d'abord, analyse statique ensuite, Rector pour le mécanique, strangler pattern pour les nouvelles fonctionnalités, et revue humaine à chaque étape.
L'IA peut accélérer significativement ce travail, à condition que le terrain soit préparé. Un agent qui refactore du code sans tests ni typage, c'est un développeur junior sans expérience et sans garde-fou — rapide, mais risqué.
Chez MulerTech, nous appliquons ces principes sur nos projets Symfony. Si vous avez un legacy PHP qui vous pèse, parlons-en.