Écrire un bundle vous envoie dans le code du framework bien plus souvent qu’écrire une application. Deux de ces incursions ont fini en pull requests contre Symfony, et aucune des deux n’avait de rapport avec ce que je construisais.
La garde qui ne tournait pas sous profilage
Le symptôme venait de mon propre code. Une commande de console gardée par un attribut refusait correctement, sauf si vous ajoutiez --profile, auquel cas elle s’exécutait.
La cause : lorsque le profilage ou le chargement paresseux entre en jeu, la console remet à l’écouteur une commande enveloppée, et l’enveloppe ne porte aucun attribut de l’originale. Un écouteur qui réfléchit sur ce qu’on lui donne ne trouve rien et laisse passer la commande. Tous les tests unitaires passaient, parce qu’ils employaient tous une commande nue que la console ne fournit jamais en vrai.
C’est la panne contre laquelle toute cette famille de code est bâtie : non pas un plantage, mais une garde qui cesse silencieusement de s’appliquer. La corriger de mon côté était facile. Ce qu’il y avait dessous n’était pas à moi.
Ce qu’il y avait dessous
Arrêter une commande à ConsoleEvents::COMMAND, ce que fait exactement une garde, laissait au profileur de console un objet de requête à moitié construit. Le profilage de console n’avait aucune couverture de test sur ce chemin, si bien que la casse était là depuis l’arrivée de la fonctionnalité.
La pull request vise 6.4, la plus ancienne branche maintenue, parce que c’est l’âge du bogue. Trois enseignements sont sortis de son écriture, et le deuxième est celui à retenir :
- Deux des replis défensifs que j’avais écrits étaient inutiles, les propriétés étant posées par le constructeur.
- Les valeurs par défaut ne pouvaient pas vivre dans le composant Console. Une tâche d’intégration continue teste FrameworkBundle contre le Console publié, donc un correctif écrit là aurait rendu le bundle dépendant d’un paquet non publié. L’initialisation est allée dans FrameworkBundle à la place, et Console est resté intact.
- L’objet de requête surchargeait une méthode marquée
@final, ce qui déclenchait une dépréciation sur chaque commande profilée. Personne ne l’avait vue, faute de test sur ce chemin. Un code de sortie n’est pas un statut HTTP, donc le correctif écrit la propriété directement.
Ce qui a été fusionné n’est pas tout à fait ce que je proposais : un mainteneur a déplacé l’initialisation là où le profil est réellement collecté, ce qui est un meilleur endroit que le constructeur de l’enveloppe. La leçon n’est pas neuve mais mérite d’être répétée : une bonne revue n’accepte ni ne refuse, elle déplace le correctif.
La seconde, en trois lignes
Un gabarit du profileur web lisait une variable qu’il ne définissait jamais. Sans drame, et une ligne à corriger, mais cela ne se voit que lorsque vous construisez votre propre panneau de profileur en vous demandant pourquoi le vôtre se comporte autrement.
Ce que cela dit de l’écriture des bundles
Un bundle vit sur les jointures : passes de compilation, priorités d’écouteurs, objets enveloppés, collecteurs de profileur. Ces jointures sont moins fréquentées que l’API tournée vers les applications, donc elles gardent plus de poussière, et la trouver fait partie du travail plutôt que de l’en distraire.
La règle pratique que je retiens des deux : affirmez sur le résultat, pas sur le fait que quelque chose a été appelé, et faites passer le test par le vrai chemin. Un bouchon qui a été appelé ne prouve rien au sujet d’une commande que la console enveloppe avant de la remettre.