Refactoring PHP & Modernisation d’Applications Legacy
La plupart des applications PHP n’ont pas besoin d’être réécrites – elles ont besoin d’une stratégie de modernisation claire. En tant qu'expert PHP et Symfony, j’accompagne les équipes produit dans le refactoring progressif de leurs bases de code : extraction de la logique métier des contrôleurs, introduction de use cases, découpage en bounded contexts, mise en place de fondations solides pour les développements futurs. Sans réécriture complète. Une amélioration mesurable et alignée sur votre roadmap.
Mon approche du refactoring
Les projets PHP legacy partagent des patterns communs : fort couplage, absence de tests, responsabilités mélangées, logique métier enfouie dans les contrôleurs. Le défi n’est pas d’identifier les problèmes – c’est d’avancer sans casser ce qui fonctionne.
Ma séquence habituelle :
- Smoke tests fonctionnels – protéger les parcours critiques avant de toucher quoi que ce soit
- Extraire la logique métier – la sortir des contrôleurs vers des use cases explicites (couche applicative)
- Identifier les bounded contexts – appliquer les principes DDD pour définir des frontières de modules cohérentes
- Introduire les abstractions – ports et adapters pour éviter que les détails d’implémentation ne fuient dans le domaine
- Découvrir et affiner – de nouveaux contextes émergent à mesure que le modèle se clarifie ; les contextes précédents se dégraissent
- Enrichir le modèle de domaine – remplacer les modèles anémiques par de vrais domain events, integration events, clients inter-contextes
J’utilise également des outils d’analyse assistés par IA (dont Claude Code) pour accélérer la phase de cartographie et la détection de patterns sur de grandes bases de code.
Cette séquence décrit la Stratégie A – l’extraction progressive. Pour les bases de code où le risque est trop élevé pour travailler directement dans le code existant, la Stratégie B adopte une approche fondamentalement différente (voir ci-dessous).
Pourquoi découper un problème complexe en problèmes plus petits change tout
Le principe fondamental derrière les bounded contexts est simple : un problème trop grand pour être raisonné clairement devient gérable quand on le découpe en problèmes plus petits et bien définis. Chaque bounded context a une responsabilité claire, son propre vocabulaire et sa propre équipe responsable. Un développeur qui rejoint le projet peut comprendre un seul contexte sans avoir besoin de tenir tout le système dans sa tête.
Ce n’est pas qu’un argument d’architecture logicielle – cela a un impact direct sur la façon dont on travaille avec les outils d’IA modernes.
L’effet fenêtre de contexte avec l’IA
Un monolithe legacy de 500 000 lignes de code est impossible à injecter en totalité dans un assistant IA. Même avec de grandes fenêtres de contexte, le modèle perd en cohérence, rate les subtilités du domaine et produit des suggestions génériques qui ne reflètent pas les vraies règles métier.
Une fois la base de code découpée en bounded contexts de 15 000 à 30 000 lignes chacun, la situation change complètement. Chaque contexte tient confortablement dans la fenêtre de travail d’une IA. Des outils comme Claude Code peuvent alors :
- Comprendre le modèle de domaine d’un contexte spécifique avec précision
- Suggérer des refactorings cohérents avec le vocabulaire du domaine existant
- Générer des tests qui correspondent aux vraies règles métier, pas des stubs génériques
- Détecter les incohérences et les dérives de nommage à l’intérieur du contexte
- Accélérer la cartographie du prochain contexte à extraire
En pratique, ce qui demandait des semaines d’analyse peut se faire en jours. Le découpage en bounded contexts n’est pas seulement une bonne architecture – c’est ce qui rend le développement assisté par IA véritablement efficace sur des bases de code legacy complexes.
Le découpage par composant rend tout ça concret
Découper en bounded contexts ne produit ses effets que si la structure du code les reflète. L’organisation classique par couche technique, celle que Symfony propose par défaut, regroupe le code par nature technique :
Pointer un outil IA sur src/Repository/ lui donne un mélange de logique de persistance de tous
les domaines. Le contexte est incohérent – le modèle ne peut pas raisonner sur un domaine métier précis
car les fichiers ne reflètent pas les frontières du domaine.
Le découpage par composant change la donne :
Chaque bounded context est un package autonome. Pointer Claude Code sur
src/Identity/ lui donne un contexte cohérent et délimité – toute la stack d’une
capacité métier, rien d’autre. C’est ce qui rend l’assistance IA précise plutôt
que générique. Et comme effet de bord, cela prépare aussi une extraction en microservice ultérieure
sans chirurgie : la frontière est déjà tracée dans le système de fichiers.
Quelle stratégie pour votre projet ?
Toutes les bases de code legacy ne se ressemblent pas. La bonne stratégie dépend du niveau de risque du code existant, de la taille de l’équipe et du degré de compréhension du domaine métier en amont.
Stratégie A – Extraction progressive
Le monolithe continue de tourner tout au long du processus. Module par module, des bounded contexts propres sont
extraits en parallèle de la livraison de features – livraison continue dès le sprint 1.
La logique métier sort des contrôleurs, des use cases émergent, le modèle de domaine s’enrichit. Le code
legacy se réduit progressivement. La contrepartie : on travaille à l’intérieur de code legacy tout en
l’améliorant, ce qui demande de la rigueur.
Idéal pour : contrainte de livraison continue, équipe réduite, besoin de résultats visibles immédiatement.
Stratégie B – Bubble context
Le code legacy est isolé dans une « bulle » protégée par une anti-corruption layer (ACL).
Les nouveaux développements et les modules refactorisés sont construits proprement à l’extérieur, en ne
communiquant avec le legacy qu’au travers de l’ACL. La bulle se vide progressivement au fur et à mesure
que les responsabilités sont transférées vers l’extérieur. La contrepartie : il y a une phase
d’investissement initial pour poser l’ACL correctement – pendant cette période, le rythme de
livraison ralentit. Une fois l’ACL en place, une livraison continue reprend.
Idéal pour : legacy très risqué ou intouchable, équipe plus large, domaine métier bien compris en amont, portage long terme par une équipe interne.
Il n’y a pas de réponse universelle. La première conversation que j’ai avec une équipe avant de toucher au code, c’est celle-là : quelle stratégie correspond à votre situation spécifique – et être honnête sur ce que chacune coûte.
Missions de refactoring courantes
- Migration PHP 5/7 vers PHP 8+
- Upgrade Symfony 2/3/4 vers la dernière version (pas uniquement LTS)
- Introduction des tests automatisés sur des bases de code non testées
- Extraction de services et d’APIs depuis des applications monolithiques
- Remplacement des modèles anémiques par de vraies entités, value objects et domain events
- Découplage de la logique métier du framework et de l’infrastructure
Patterns clés
Appliqués de façon sélective selon ce que la base de code nécessite, pas comme une checklist dogmatique.
- Clean code – en suivant les principes de Robert C. Martin (Uncle Bob) : des fonctions qui font une seule chose, des noms qui révèlent l’intention, aucun effet de bord, la règle du boy scout (« laissez le code plus propre que vous ne l’avez trouvé »). Combinés aux principes SOLID, ces fondamentaux transforment une base legacy en code lisible, testable et sûr à faire évoluer.
- Architecture hexagonale – séparer ce que fait le métier de la façon dont il se connecte au monde extérieur (base de données, HTTP, email…). Le domaine n’a aucune connaissance des détails techniques.
- DDD bounded contexts – découper un grand domaine en modules autonomes, chacun avec son propre vocabulaire. Un « utilisateur » dans la facturation ne désigne pas la même chose qu’un « utilisateur » dans la gestion d’identité.
- CQRS – séparer le code qui modifie l’état (commandes) du code qui le lit (requêtes). Simplifie chaque côté et rend l’intention explicite.
- Strangler fig – remplacer un composant legacy progressivement, sans jamais arrêter le système. Le nouveau code pousse autour de l’ancien jusqu’à ce que l’ancien puisse être retiré.
- Méthode Mikado – cartographier l’arbre de dépendances d’un refactoring, puis résoudre chaque nœud séquentiellement pour minimiser les impacts sur le système existant.