Tests Guard Intentional Workarounds
Problème
Vous observez une ligne de log en production qui semble clairement erronée — une fonction appelée alors que sa précondition semble déjà satisfaite, une tentative qui se déclenche alors que la première semblait réussir, un état défini à la valeur qu'il détient déjà. L'instinct crie « c'est un no-op redondant, je vais le supprimer ». Vous le supprimez, exécutez les tests locaux (qui peuvent même passer si la couverture est fine), poussez vers CI, et un test échoue avec un titre qui décrit exactement le comportement que vous venez de supprimer. Le commentaire de ce test explique un bug subtil de plateforme ou une race condition qui rendait cet appel « redondant » nécessaire comme workaround.
La ligne de log à laquelle vous avez réagi n'était pas la preuve d'un bug. C'était la preuve que le workaround s'exécutait correctement pour le scénario exact qu'il était conçu pour gérer.
Contexte / Conditions de déclenchement
Appliquez la prudence de cette compétence quand AU MOINS une de ces conditions est vraie :
- Appel redondant en apparence : Vous êtes sur le point d'envelopper un appel dans
if (!already_X)ou de le supprimer purement et simplement parce que les logs montrent qu'il se déclenche quandXest déjà vrai. - État impossible dans les logs : Les logs montrent une combinaison d'états qui semble logiquement impossible ou contradictoire (
playing=true+positionMs=0pendant des secondes,isLoading=false+ données encore vides, etc.). - « Pourquoi quelqu'un écrirait-il ça ? » : En lisant le code, vous ne pouvez imaginer aucune raison pour une vérification défensive, un await supplémentaire, ou un appel secondaire.
- Simplifier une condition : Une branche semble morte parce que vous croyez que la condition ne peut pas être atteinte, ou que les deux branches vous semblent identiques.
- Recevoir un test échouant : CI retourne un échec avec un titre de test qui nomme exactement le comportement que votre changement a supprimé ou modifié.
Solution
Avant de toucher au code « redondant », exécutez cette vérification en cinq étapes :
Étape 1 : Grep pour les tests qui affirment le comportement exact
Cherchez dans la suite de tests le nom de la fonction, l'état que vous pensez redondant, et tout synonyme :
# Exemple : envisager de supprimer player.play() quand le rebuffer se termine
grep -rn "player.play" test/ | grep -iE "already|even when|still|twice|redundant"
grep -rn "rebuffer.*play\|playing.*true.*play" test/
Cherchez spécifiquement les noms de tests contenant des phrases comme :
"even when X"/"even if X""still calls Y when Z""twice on Y"/"idempotent""after Y"(particulièrement"after seek","after error","after reconnect")"recovers from"/"nudges"/"resumes"- Le mot d'état spécifique de votre log (
stalled,frozen,orphan,race)
Étape 2 : Lire le commentaire du test, pas seulement l'assertion
Si vous trouvez un test correspondant, lisez le commentaire au-dessus de l'assertion, pas seulement l'appel expect(). Le commentaire explique presque toujours pourquoi le comportement par ailleurs redondant est requis. Cherchez des phrases comme :
- « mpv/iOS/Chrome/Safari peut X même quand Y »
- « nudge », « kick », « wake up », « unstick »
- « workaround for », « due to », « race with »
- Références de bug tracker, SHA de commit, numéros de PR
Étape 3 : Corréler avec votre log de production
Relisez la ligne de log qui a incité la « correction » à la lumière du commentaire du test. L'état du log correspond-il au scénario de bug que le test décrit ?
- Si le test dit « mpv rapporte playing=true en stall à positionMs=0 » et votre log montre
playing=true, positionMs=0, vous regardez le bug dont le workaround existe pour gérer. Le workaround fonctionne. - Si l'état du log ne correspond pas au scénario déclaré du test, vous avez peut-être découvert un nouveau bug OU une branche véritablement redondante. Procédez avec prudence.
Étape 4 : Préserver le comportement, améliorer l'observabilité
Si le workaround supporte une charge, ne le supprimez pas. Au lieu de cela :
- Ajoutez ou améliorez le message de log pour que le futur-vous comprenne l'intention (
nudge_stalled_decoderest plus clair queredundant_play). - Ajoutez un commentaire de code référençant le test qui protège ce comportement (
// Voir test : "rebuffer recovery calls play() even when playing=true"). - Si le volume est bruyant, envisagez de réduire le niveau de log ou de l'échantillonner, mais conservez le comportement.
Étape 5 : Si CI vous a déjà attrapé, revertez et apprenez
Si vous avez déjà poussé et que CI a surfacé le test protégeant :
- Ne « réparez » pas le test pour correspondre au nouveau comportement. Le test est la spec.
- Revertez juste le changement de comportement — conservez toute amélioration sans rapport (logging, commentaires, etc.).
- Lisez attentivement le commentaire du test et mettez à jour votre modèle mental du système.
- Committez la revert avec un message qui capture la leçon pour que le prochain ingénieur lisant git blame comprenne pourquoi l'appel « redondant » est là.
Vérification
Après avoir appliqué cette compétence, vous devriez être capable de répondre :
- [ ] Y a-t-il un test dont le nom décrit le comportement que je suis sur le point de supprimer ?
- [ ] Si oui, son commentaire explique-t-il un bug/race/quirk de plateforme qui le justifie ?
- [ ] Le log de production auquel je réagis correspond-il au scénario de ce bug ?
- [ ] Peux-je expliquer en une phrase pourquoi le comportement « redondant » existe, avec assez de détail pour qu'un reviewer soit d'accord ?
Si vous ne pouvez pas répondre aux quatre, vous ne savez pas encore assez pour supprimer le code.
Exemple
Scénario (réel, Flutter + media_kit video player) :
Log de production depuis iOS :
STUTTER_DEBUG rebuffer_auto_play index=48 positionMs=0 playing=true
Instinct : « Pourquoi appeler player.play() quand player.state.playing est déjà vrai ? C'est un no-op au mieux. Laissez-moi ajouter if (!player.state.playing). »
Mauvaise correction appliquée. CI échoue :
- [FAILED] VideoFeedController post-seek rebuffer recovery
rebuffer recovery calls play() even when player reports playing
En lisant le test (qui était déjà dans le repo) :
test(
'rebuffer recovery calls play() even when player reports playing',
() async {
// ...
// Simuler que rebuffer se termine pendant que le player rapporte playing=true.
// mpv peut staller (pas de rendu de frame) même quand playing=true après un
// seek, donc nous appelons toujours play() pour nudge le decoder.
when(() => setup.state.playing).thenReturn(true);
// ...
verify(setup.player.play).called(greaterThanOrEqualTo(1));
},
);
Le commentaire du test documente le bug exact : le playing=true de mpv ne garantit pas la sortie de frames après un seek ou un hoquet réseau. L'appel play() « redondant » est spécifiquement pour débloquer le decoder dans cet état. Le log de production montrant positionMs=0, playing=true pendant des secondes est le bug se produisant en production — et le workaround est la raison pour laquelle les vidéos se récupèrent au lieu de rester gelées.
Réponse correcte : Revertez la garde if (!playing). Gardez-le appelant play() inconditionnellement. Si le log est bruyant, renommez le tag de log de rebuffer_auto_play à decoder_nudge pour refléter l'objectif réel, et ajoutez un commentaire source pointant vers le test.
Notes
- Cette compétence concerne le code que vous n'avez pas écrit. Votre propre code récent est peu susceptible d'avoir un workaround caché que vous avez oublié.
- Particulièrement commun dans : lecture vidéo/audio, chemins retry réseau, quirks de navigateur, workarounds GPU/driver, race conditions sur filesystem, idempotence de systèmes distribués, pipelines animation/layout.
- Un test dont le nom contient la phrase "even when" ou "even if" est le signal le plus fort unique que vous regardez un workaround protégé.
- Inversement, si vous ajoutez un workaround, écrivez le test avec un nom qui décrit littéralement le comportement surprenant, pour que le prochain ingénieur (ou le futur-vous) reçoive l'avertissement que vous auriez souhaité avoir.
- Relation :
superpowers:verification-before-completion(ne revendiquez pas qu'une correction fonctionne jusqu'à ce que vous ayez vraiment exécuté le test qui protège le comportement) etsimplify(qui devrait aussi respecter cette règle — ne simplifiez pas le code qu'un test affirme explicitement).
Références
- Session originale : Divine Mobile PR #2737, iOS video stutter debugging, 2026-04-05.
- Compétence connexe :
mock-call-count-retry-fallback— pour le cas inverse où ajouter de la logique retry casse les décomptes d'appels de test.