Pourquoi les bugs de code Windsurf échappent à la relecture
La relecture humaine ne va pas au-delà du nombre de lignes qu'une personne peut garder en tête. Une modification large et mécanique la met en échec d'une manière bien précise : chaque bloc de diff est plausible pris isolément, donc l'attention se relâche. Vous validez le douzième fichier avec moins de rigueur que le premier, et le défaut se trouve presque toujours à la fin.
Le défaut est d'ailleurs rarement dans ce qui a changé. Il est dans ce qui n'a pas changé. Un renommage qui a oublié un site d'appel, un nouveau champ obligatoire que trois constructeurs ne transmettent pas, un handler mis à jour partout sauf dans le chemin d'erreur.
Les trois formes de défaillance d'une modification large
Passes incomplètes
Onze sites d'appel mis à jour, un oublié, et il se trouve sur un chemin que personne n'emprunte tous les jours.
Le typage ne le détecte souvent pas, parce que le site oublié était déjà faiblement typé.
Dérive des hypothèses
Deux fichiers finissent par ne plus s'accorder sur le sens d'une valeur, par exemple sur un timestamp exprimé en secondes ou en millisecondes.
Chaque fichier est cohérent en interne, donc rien ne paraît faux pris isolément.
Changement de comportement silencieux
Un refactoring conserve l'interface et modifie discrètement un ordre, des valeurs par défaut ou un chemin d'erreur.
Les tests écrits pour l'ancienne implémentation passent toujours, parce qu'ils vérifient l'interface.
Ces trois formes ont un point commun : on les trouve en utilisant l'application, pas en la lisant. C'est ce qui les rend coûteuses.
Vérifiez le comportement, pas le diff
Face à une modification large, la question utile n'est pas « chaque édition est-elle correcte ? » mais « le produit fait-il toujours ce qu'il faisait, plus la nouveauté ? ». C'est une question de comportement, et on y répond en faisant passer l'application déployée par les parcours que la modification a pu toucher.
La couverture n'a pas besoin d'être exhaustive pour être utile. Une modification qui renomme un champ sur tout le parcours de paiement demande que ce parcours soit exécuté une fois de bout en bout, ce qui vaut mieux que de relire douze diffs avec attention.
Installez la skill de vérification dans l'éditeur pour que l'agent puisse lancer ce contrôle lui-même au lieu de vous attendre.
Terminal
npm install -g @testsprite/testsprite-cli
testsprite setup
Le tableau de bord réalise la même configuration si vous préférez ne rien installer. Le reste de la surface CLI se trouve dans le dépôt CLI.
Donnez l'échec à l'agent, pas votre résumé
Quand quelque chose casse, la boucle de correction ne se referme que si les preuves sont exploitables. Ce qui a été tenté, ce que l'application a fait, l'endroit où les deux divergent. Avec cela en main, l'agent peut retourner dans les mêmes douze fichiers avec une cible précise. Avec une capture d'écran, il devine, et vous voilà de nouveau à tout décrire.
Lire un diff large sans se raconter d'histoires
Il existe une façon honnête de relire une modification de douze fichiers, et ce n'est pas d'en lire chaque bloc. C'est de décider, avant même d'ouvrir le diff, ce qui devrait être vrai pour que cette modification soit correcte, puis de ne vérifier que ces points.
Pour un renommage, il s'agit de savoir si tous les sites d'appel ont suivi, ce à quoi le compilateur répond en grande partie, et si quelque chose construit le nom dynamiquement, ce à quoi il ne répond pas. Pour un nouveau champ obligatoire, il s'agit de tous les chemins qui construisent l'objet, y compris les tests et les fixtures qui continueront silencieusement d'en transmettre une version incomplète. Pour un refactoring qui conserve une interface, il s'agit de l'ordre et des chemins d'erreur, car c'est précisément ce qu'un refactoring change en silence.
Trois questions par modification valent mieux que le survol de quatre cents lignes, et il vous reste la liste honnête de ce qu'il faut encore vérifier en exécutant le produit.
Choisissez les parcours selon leur rayon d'impact, pas selon leur importance
Le réflexe est de couvrir la fonctionnalité la plus importante. Pour une modification large, la meilleure question est de savoir quels parcours la modification a pu toucher, classés selon la discrétion avec laquelle ils échoueraient.
Un parcours de paiement est important, et il est aussi surveillé. S'il casse, vous le saurez dans l'heure. Les dangereux sont les parcours qui comptent mais qui servent une fois par semaine plutôt qu'une fois par heure : un export, une invitation, une page de paramètres, un récapitulatif programmé. C'est là qu'une passe incomplète reste tranquille pendant un mois.
Après une modification large, commencez donc par exercer les chemins discrets. Ils coûtent peu à couvrir et ce sont ceux que personne d'autre ne vérifie.
Lancez-le sur chaque pull request
Les modifications larges sont précisément celles que l'on est tenté de fusionner vite, parce que le diff est ennuyeux. C'est l'argument qui plaide pour un contrôle automatique plutôt qu'un contrôle dont il faut se souvenir.
Deux options, et le choix tient surtout à qui gère le pipeline. Connecter la GitHub App depuis le tableau de bord ne demande aucune modification de votre dépôt, puisqu'elle réagit au déploiement que votre build émet déjà. Ajouter une étape GitHub Actions place le contrôle dans le dépôt, où il est relu comme le reste du build.
Vérifier une modification large sans la lire
TestSprite répond à la question qu'un diff large ne peut pas trancher : le produit fait-il toujours ce qu'il faisait, plus la nouveauté ? Il ouvre l'application déployée, parcourt les flux que la modification a pu toucher et rend compte du comportement plutôt que de la structure.
C'est la bonne forme de réponse à ce problème. Passes incomplètes, hypothèses qui dérivent et changements de comportement discrets sont tous invisibles dans le code source et évidents à l'usage. La couverture est générée à partir de votre produit plutôt qu'écrite à la main, si bien que les parcours discrets, l'export, l'invitation et la page de paramètres, sont inclus sans que personne ait à y penser.
Quand quelque chose casse, l'échec revient sous la forme d'un ensemble unique sur lequel votre agent peut agir, et c'est ce qui compte le plus ici : le pointer vers la divergence précise vaut mieux que lui demander de réexaminer douze fichiers.
Pourquoi le typage ne détecte-t-il pas ces problèmes ?
Il détecte une bonne part des passes incomplètes, et presque aucune dérive d'hypothèse ni aucun changement de comportement silencieux, parce que ces deux-là sont corrects du point de vue des types par construction.
Faut-il simplement demander des modifications plus petites ?
Cela aide, et cela vous prive d'une bonne partie de l'intérêt d'un éditeur agentique. Une étape de vérification vous permet de conserver cette largeur tout en sachant si le résultat fonctionne.
Est-ce que cela fonctionne quand la modification ne touche que le backend ?
Oui, via une couverture au niveau de l'API plutôt que par l'interface. Le principe reste le même : exercer le comportement plutôt que lire la modification.
Combien de temps prend un contrôle de ce type ?
Assez longtemps pour que vous ne le lanciez pas à chaque frappe, assez peu pour tenir dans une pull request. C'est là qu'est sa place.
Et si l'agent a déjà lancé les tests unitaires ?
C'est utile, mais les tests unitaires ont en général été écrits sur la base des hypothèses que la modification vient justement de changer. Ils confirment la cohérence interne plutôt que la justesse externe.
À douze fichiers, lire le diff n'est pas un contrôle.
Les bugs de code Windsurf se cachent dans les passes incomplètes, les hypothèses qui dérivent et les changements de comportement discrets. Vérifiez le produit en fonctionnement plutôt que le diff, restituez les échecs sous forme de preuves, et faites tourner le contrôle sur chaque pull request.