Efficience IT
·Formation

Migration Symfony : guide pour mettre à jour un projet sereinement

Par Efficience IT

Comprendre le cycle de release Symfony

Symfony suit un calendrier de publication prévisible qui structure toute stratégie de migration. Les versions mineures sortent deux fois par an, en mai et en novembre. Les versions majeures arrivent environ tous les deux ans. Chaque version mineure bénéficie de huit mois de corrections de bugs et de quatorze mois de correctifs de sécurité. Les versions LTS prolongent ce support à trois ans de corrections et quatre ans de sécurité.

Cette régularité a une conséquence directe : le coût d'une migration est proportionnel au temps écoulé depuis la dernière mise à jour. Un projet maintenu à jour sur chaque mineure absorbe des changements incrémentaux. Un projet figé sur Symfony 4.4 depuis 2019 affronte l'accumulation de trois versions majeures d'un coup.

Le mécanisme des dépréciations

Le contrat de Symfony est clair : aucune fonctionnalité n'est supprimée sans avoir été dépréciée au moins une version majeure à l'avance. Concrètement, une classe marquée @deprecated en 5.2 reste fonctionnelle dans toute la branche 5.x. Elle disparaît en 6.0.

La dernière mineure avant une majeure (5.4 avant 6.0, 6.4 avant 7.0) joue un rôle pivot. Elle contient exactement les mêmes fonctionnalités que la majeure suivante, mais conserve la rétrocompatibilité. Migrer vers 5.4 et résoudre toutes les dépréciations revient à préparer le terrain pour un passage à 6.0 quasi transparent.

Le Profiler de Symfony expose un panneau dédié aux dépréciations. Exécuter la suite de tests avec la variable SYMFONY_DEPRECATIONS_HELPER=max[direct]=0 force l'échec au moindre appel déprécié direct, transformant chaque dépréciation en signal immédiat.

Préparer le terrain : audit et couverture de tests

Évaluer la couverture existante

Avant de toucher à composer.json, mesurez votre couverture de tests. Non pas le pourcentage global qui rassure à tort, mais la couverture sur les chemins critiques : authentification, paiement, workflows métier, intégrations tierces. Un projet avec 70 % de couverture globale mais 0 % sur le contrôleur de paiement est un projet à risque.

PHPUnit avec l'option --coverage-html génère un rapport exploitable. Identifiez les zones non couvertes qui touchent à des services utilisant des API Symfony susceptibles de changer. Concentrez l'effort de test sur ces zones avant de démarrer la migration.

Behat ou Panther complètent cette couverture en validant les parcours utilisateurs de bout en bout. Un scénario Behat qui déroule le tunnel de commande détectera une régression que des tests unitaires isolés manqueront.

Construire la matrice de compatibilité des bundles

Chaque bundle tiers constitue un point de friction potentiel. Dressez un inventaire exhaustif depuis composer.lock et vérifiez pour chacun : la version cible de Symfony est-elle supportée ? Le bundle est-il toujours maintenu ? Existe-t-il une branche ou un tag compatible ?

Les bundles abandonnés représentent le risque principal. Un bundle sans mise à jour depuis deux ans et qui repose sur des classes internes de Symfony cassera probablement à la prochaine majeure. Identifiez les alternatives ou prévoyez un fork interne. Pour les bundles internes à votre organisation, planifiez leur mise à jour en parallèle du projet principal.

Automatiser avec Rector et PHPStan

Rector : le levier de productivité

Rector applique des règles de transformation de code automatisées. Pour une migration Symfony, le set symfony de Rector réécrit les appels dépréciés, met à jour les signatures de méthodes et adapte les configurations. Sur un projet de taille moyenne, Rector automatise entre 60 et 80 % des modifications nécessaires.

L'approche recommandée consiste à exécuter Rector set par set, pas tout d'un bloc. Lancez d'abord les règles de renommage de classes, validez avec la suite de tests, commitez. Puis les règles de configuration, validez, commitez. Cette granularité isole chaque catégorie de changement et simplifie le debug en cas de régression.

Rector ne remplace pas la compréhension du changement. Certaines transformations nécessitent un contexte métier que l'outil ne possède pas. Relisez systématiquement les diffs générés, en particulier sur les services qui manipulent des événements Symfony ou des extensions du compilateur de conteneur.

PHPStan : le filet de sécurité statique

PHPStan au niveau 6 minimum détecte les incompatibilités de types, les appels à des méthodes supprimées et les erreurs d'interface. Après chaque passe de Rector, lancez PHPStan pour attraper ce que l'outil de transformation a manqué ou mal transformé.

L'extension phpstan-symfony apporte la connaissance du conteneur de services au moteur d'analyse. Elle détecte les services mal typés, les paramètres manquants et les routes invalides. Cette extension devient particulièrement précieuse lors d'une migration où la configuration du conteneur évolue.

Stratégie de migration par phases

Phase 1 : stabilisation sur la dernière mineure

Montez d'abord sur la dernière mineure de votre branche majeure actuelle. Si vous êtes sur Symfony 5.2, passez à 5.4. Résolvez toutes les dépréciations signalées. Cette phase est la moins risquée car la rétrocompatibilité est garantie au sein d'une même branche majeure.

Phase 2 : saut de version majeure

Une fois toutes les dépréciations éliminées en 5.4, le passage à 6.0 ne devrait introduire que des changements liés aux éléments dépréciés effectivement supprimés. Ajustez les contraintes dans composer.json, exécutez composer update et résolvez les conflits de dépendances un par un. Composer produit des messages d'erreur explicites qui guident la résolution.

Vérifiez la version minimale de PHP requise. Symfony 6.0 exige PHP 8.0, Symfony 7.0 exige PHP 8.2. Un saut de version majeure Symfony implique souvent un saut de version PHP en parallèle, avec ses propres implications.

Phase 3 : validation et montée en version continue

Exécutez la suite de tests complète. Organisez une recette avec les utilisateurs métier. Comparez les métriques de performance avant et après : temps de réponse, consommation mémoire, nombre de requêtes SQL, taux d'erreur. Blackfire ou le Profiler Symfony identifient les goulots d'étranglement introduits par la nouvelle version.

Penser à l'échelle : migrations architecturales

Le Strangler Fig appliqué à Symfony

Pour les projets massifs où un big bang est trop risqué, le pattern Strangler Fig offre une alternative. Le principe : faire cohabiter l'ancien et le nouveau derrière un reverse proxy ou un load balancer, puis migrer route par route.

Concrètement, vous déployez une nouvelle instance Symfony 7 à côté de l'application Symfony 4 existante. Le proxy route /api/v2/* vers la nouvelle instance, tout le reste vers l'ancienne. Chaque endpoint migré bascule progressivement. L'ancienne application rétrécit jusqu'à disparaître.

Cette approche exige une couche de partage de session (Redis, JWT stateless) et une stratégie de base de données cohérente entre les deux instances. Elle se justifie sur des applications critiques où l'indisponibilité n'est pas acceptable.

Opérer en dual-stack

Le dual-stack va plus loin que le Strangler Fig en maintenant deux versions en production simultanément pendant une période définie. Le trafic est réparti par feature flags ou par population d'utilisateurs. Les métriques de la nouvelle version sont comparées en conditions réelles avant le basculement complet.

Cette stratégie convient aux plateformes à fort trafic où les tests de charge en environnement de staging ne reproduisent pas fidèlement les conditions de production. Le coût opérationnel est élevé (double infrastructure, double monitoring), mais le risque métier est réduit au minimum.

Évaluation des risques et arbitrage

Toute migration majeure mérite une analyse de risques formalisée. Trois axes structurent cette évaluation :

Risque technique : nombre de dépréciations non résolues, pourcentage de code couvert par les tests, nombre de bundles tiers non compatibles, écart de version PHP requis.

Risque organisationnel : disponibilité de l'équipe, niveau de connaissance Symfony dans l'équipe, existence de documentation interne sur l'architecture, capacité à geler les fonctionnalités pendant la migration.

Risque métier : périodes critiques à éviter (Black Friday, clôture comptable), SLA à respecter, tolérance à l'indisponibilité, impact d'une régression sur le chiffre d'affaires.

Un projet avec une dette technique modérée, une bonne couverture de tests et une équipe expérimentée migrera en place avec la stratégie par phases. Un projet legacy critique avec peu de tests et des bundles abandonnés justifiera un Strangler Fig ou une réécriture partielle.

Conduire le changement dans l'organisation

Les migrations majeures ne sont pas que techniques. Un saut de Symfony 4 à Symfony 7 implique de former l'équipe aux attributs PHP 8, au système de configuration modernisé, aux nouvelles pratiques de sécurité. Prévoyez des sessions de montée en compétence avant le démarrage, pas pendant.

Communiquez un calendrier réaliste aux parties prenantes. Une migration majeure sur un projet de taille moyenne prend entre deux et six semaines selon la dette accumulée. Intégrez une marge pour les imprévus, en particulier sur les bundles tiers dont la compatibilité n'est pas garantie le jour J.

Pour aller plus loin