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.

Refactoring PHP legacy : avant (monolithe couplé) et après (bounded contexts)

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 :

  1. Smoke tests fonctionnels – protéger les parcours critiques avant de toucher quoi que ce soit
  2. Extraire la logique métier – la sortir des contrôleurs vers des use cases explicites (couche applicative)
  3. Identifier les bounded contexts – appliquer les principes DDD pour définir des frontières de modules cohérentes
  4. Introduire les abstractions – ports et adapters pour éviter que les détails d’implémentation ne fuient dans le domaine
  5. Découvrir et affiner – de nouveaux contextes émergent à mesure que le modèle se clarifie ; les contextes précédents se dégraissent
  6. 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 :

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 :

Structure par couche technique (convention Symfony par défaut, mais pas idéale) src/ 📁 Controller/ ← tous domaines mélangés 📁 Repository/ ← tous domaines mélangés 📁 Service/ ← tous domaines mélangés

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 :

Structure par composant src/ 📂 Identity/ 📁 Infrastructure/Persistence/DoctrineUserRepository.php 📁 Infrastructure/Http/RegisterUserController.php 📁 Application/UseCase/RegisterUser.php 📄 Domain/User.php 📂 Orders/ 📁 Infrastructure/ 📁 Application/ 📁 Domain/

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.

Deux stratégies de refactoring : extraction progressive vs bubble context

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

Patterns clés

Appliqués de façon sélective selon ce que la base de code nécessite, pas comme une checklist dogmatique.

Clients qui m’ont fait confiance

Sanofi
IAD Immobilier
Leboncoin
Air France

💬 Parlons de votre base de code legacy

← Retour à l'accueil