« ERROR: Job exceeded maximum time limit » : ce message, tombé un vendredi après-midi sur la pipeline GitLab CI d’un client, ne s’accompagnait d’aucun changement de code suspect dans les derniers commits. La suite PHPUnit, qui tournait habituellement en trois minutes, dépassait désormais les vingt minutes allouées au job.
Le coupable n’était ni un test mal écrit ni une régression de performance applicative, mais une mise à jour de l’image Docker de CI qui avait fait passer Xdebug de la version 2 à la version 3, sans que personne n’ajuste la configuration en conséquence. Contrairement à Xdebug 2, la version 3 active plusieurs modes simultanément par défaut dès qu’elle est chargée, y compris le mode develop qui instrumente chaque appel de fonction pour le débogage pas à pas.
Le symptôme observé en CI
Le ralentissement ne touchait pas uniformément tous les tests : les tests unitaires simples restaient rapides, tandis que les tests d’intégration multipliant les appels à la base de données et aux hooks WordPress devenaient exponentiellement plus lents, certains passant de quelques millisecondes à plusieurs secondes chacun.
- Le fichier
phpunit.xmlactivait la couverture de code via--coverage-clover, ce qui nécessitait effectivement Xdebug ou PCOV. - Mais l’image Docker chargeait Xdebug avec sa configuration par défaut, sans jamais restreindre le mode à ce dont PHPUnit avait réellement besoin.
- Le mode
develop, actif par défaut, ajoute une pile d’appels enrichie à chaque erreur, coûteuse à générer même quand personne ne la consulte.
Diagnostiquer : vérifier le mode Xdebug effectif
La première commande à lancer sur un ralentissement de ce type reste simple, mais rarement le réflexe immédiat :
php -i | grep xdebug.mode

Sur l’environnement de CI en question, la commande retournait xdebug.mode => develop,coverage,debug => develop,coverage,debug, alors que PHPUnit n’avait besoin que du mode coverage pour générer son rapport Clover.
Le correctif : restreindre le mode via une variable d’environnement
Xdebug 3 permet de surcharger sa configuration sans toucher au php.ini de l’image, via la variable d’environnement XDEBUG_MODE, directement dans le job de CI :
variables:
XDEBUG_MODE: coverage
test:
stage: test
script:
- vendor/bin/phpunit --coverage-clover=coverage.xml
Ce simple changement a ramené le temps d’exécution de vingt minutes à un peu moins de quatre, sans aucune modification du code applicatif ni de la suite de tests elle-même.
Comparer Xdebug coverage et PCOV pour aller plus loin
Une fois le mode Xdebug restreint, il reste une marge de gain supplémentaire : PCOV, une extension dédiée uniquement à la couverture de code, sans les fonctionnalités de débogage pas à pas, s’avère généralement plus rapide qu’Xdebug même en mode coverage seul.
| Configuration | Temps mesuré (suite complète) |
|---|---|
| Xdebug 3, tous modes actifs (défaut) | 20 min 40 s |
| Xdebug 3, mode coverage seul | 3 min 50 s |
| PCOV | 2 min 15 s |
| Sans couverture (aucune extension) | 1 min 05 s |
Prévenir la récidive
- Ajouter une vérification explicite du mode Xdebug effectif dans les logs de CI, plutôt que de le découvrir a posteriori.
- Documenter dans le
READMEdu projet le mode attendu, pour éviter qu’une future mise à jour d’image ne réintroduise le problème silencieusement. - Envisager PCOV pour la CI et réserver Xdebug complet uniquement aux environnements de développement local, où le débogage pas à pas est réellement utile.
Une extension de débogage n’a aucune raison de rester active dans son mode le plus coûteux sur un environnement où personne ne pose jamais de point d’arrêt : la CI ne débogue pas, elle vérifie.
En résumé
Le passage à Xdebug 3 a changé un comportement implicite qui avait un coût réel en CI : plusieurs modes actifs simultanément par défaut, là où une seule variable d’environnement suffit à restreindre l’extension à ce dont PHPUnit a réellement besoin. Un simple php -i | grep xdebug.mode aurait permis de repérer le problème en quelques secondes plutôt qu’en creusant tout un après-midi.