La code review à l'ère de l'IA : on ne relit plus du code, on valide des décisions
La revue de code a longtemps eu une fonction assez claire. Quelqu’un écrivait du code, lentement, ligne par ligne, et quelqu’un d’autre le relisait pour attraper ce qui avait échappé : une erreur de logique, un cas non géré, une convention non respectée, une dette qu’on installait sans la voir. La lenteur même de l’écriture garantissait une forme de réflexion. On ne tapait pas trois cents lignes sans y penser un minimum, et la revue venait vérifier cette pensée, pas la remplacer.
Cette garantie a disparu, presque sans qu’on s’en aperçoive. Aujourd’hui, trois cents lignes apparaissent en quelques secondes, cohérentes, idiomatiques, plausibles. Et la revue de code, telle qu’on la pratiquait, n’est plus du tout adaptée à ce qu’elle doit désormais examiner. Le rituel est resté identique : une pull request, des commentaires, une approbation. Mais l’objet qui passe dans ce rituel a changé de nature, et faire comme si de rien n’était revient à valider de plus en plus de code avec une vigilance qui sert de moins en moins.
Le contenu de la PR a changé de nature
Une pull request rédigée à la main porte la trace du raisonnement de son auteur. On voit où il a hésité, ce qu’il a simplifié, les endroits qu’il a peaufinés et ceux qu’il a expédiés. La revue dialogue avec une pensée humaine, faillible mais présente, et ce dialogue est l’essentiel : on ne corrige pas que des lignes, on confronte deux compréhensions du problème.
Une PR générée par un agent ne porte pas cette trace. Le code est lisse partout, y compris là où il aurait fallu hésiter. C’est le prolongement direct de ce que j’observais à propos du paradoxe de la vitesse : l’IA produit une surface uniforme qui ne distingue pas le passage facile du passage délicat. Le reviewer perd un signal qu’il utilisait sans même le savoir. Il ne peut plus se fier au relief du texte pour repérer où regarder, parce qu’il n’y a plus de relief. Tout a la même texture impeccable.
Pire, cette uniformité endort activement. Du code propre inspire confiance, c’est un réflexe ancré et difficile à désactiver. On relit plus vite, et moins attentivement, ce qui paraît soigné. Or la qualité visuelle du code généré n’a aucun rapport avec sa justesse. Un agent écrit avec exactement la même assurance une fonction correcte et une fonction qui ignore silencieusement le cas qui comptait. C’est le même phénomène que celui que je décrivais à propos du legacy : l’IA traduit sans broncher les 80 % faciles et oublie les 20 % qui font tout le métier, et rien dans l’apparence du résultat ne signale lesquels sont lesquels.
Relire chaque ligne ne tient plus
La première réaction, face à cette situation, est de vouloir relire plus, plus attentivement, chaque ligne, comme avant mais en mieux. C’est une impasse, pour une raison purement mécanique. Le volume de code généré dépasse ce qu’une revue ligne à ligne peut absorber. Si vous tentez de relire la sortie d’un agent comme vous relisiez le travail d’un collègue qui tapait à la main, deux choses peuvent arriver, toutes deux mauvaises. Soit vous êtes débordé et la revue devient le goulot d’étranglement de toute l’équipe. Soit vous tamponnez sans vraiment lire, ce qui revient à supprimer la revue tout en gardant son rituel, le pire des deux mondes.
Il faut accepter un déplacement de fond. Relire chaque ligne avait du sens quand chaque ligne coûtait un effort humain, parce que cet effort était lui-même un filtre. Maintenant que les lignes sont quasi gratuites à produire, leur relecture exhaustive est devenue à la fois impossible à l’échelle et insuffisante sur le fond. La revue doit monter d’un cran. Elle ne porte plus sur le texte, mais sur les décisions que ce texte incarne.
Ce déplacement n’est pas un renoncement. C’est un recentrage. On arrête de dépenser l’attention humaine, qui est rare, sur ce qu’un outil automatique attrape très bien, pour la concentrer là où aucun outil ne remplace le jugement.
Ce qu’on vérifie désormais
Quand je relis une PR largement générée, je ne me demande plus d’abord « ce code est-il correct ». Je me demande « quelles décisions ont été prises ici, et sont-elles les bonnes ». Ce sont des questions différentes, et la seconde est nettement plus difficile à déléguer à un linter.
Voici, concrètement, ce que je traque en priorité.
L’intention a-t-elle été comprise
Le code fait souvent quelque chose de cohérent, mais pas forcément ce qui était demandé. Un agent comble les ambiguïtés du ticket par des hypothèses, et il les comble toujours, parce qu’il ne peut pas s’arrêter pour poser une question. Ces hypothèses sont invisibles dans le diff : rien ne signale « ici, j’ai supposé que les montants étaient hors taxes ». Je vérifie donc en priorité l’adéquation au besoin réel, en relisant le ticket en parallèle du code, pas la cohérence interne du code, qui est presque toujours acquise et donc peu informative.
Les frontières ont-elles été respectées
Un agent ignore souvent l’architecture implicite du projet, celle qui n’est écrite nulle part mais que toute l’équipe connaît. Il va appeler une couche depuis une autre qui ne devrait pas la connaître, dupliquer une logique qui existait déjà ailleurs sous un autre nom, introduire une dépendance qui casse l’isolation qu’on défendait. Le code marche, les tests passent, et la structure se dégrade d’un cran. C’est une dette qui ne se voit pas en exécution, seulement à la lecture, et seulement par quelqu’un qui connaît la carte du système.
Les cas limites ont-ils été pensés ou évités
C’est le point le plus subtil, et le plus dangereux. Du code généré gère brillamment le cas nominal et survole le reste. Je cherche donc systématiquement ce qui se passe sur l’entrée vide, l’échec réseau, l’accès concurrent, la valeur nulle, le débordement, la collection qui contient un seul élément ou un million. Non pas parce que l’agent serait incompétent, mais parce qu’il optimise pour le plausible, et que le plausible exclut presque toujours le rare. Or les incidents de production vivent précisément dans le rare.
Ce qui a été supprimé
Les outils de diff mettent en valeur les ajouts, en vert, bien visibles. On lit beaucoup moins bien les suppressions. Or un agent qui réécrit un fichier peut faire disparaître une garde, un log, un commentaire qui valait un avertissement, une condition qui semblait incohérente mais qui ne l’était pas. Cette ligne « bizarre » qu’il a nettoyée au passage était peut-être la seule chose qui empêchait un incident, le vestige d’un bug corrigé il y a trois ans. Je relis les suppressions avec autant d’attention que les ajouts, parfois plus.
Le reviewer devient un valideur d’architecture
Ce déplacement change le profil de qui doit relire, et c’est une conséquence qu’on sous-estime. Attraper une faute de syntaxe ou un nom mal choisi ne demande pas d’expérience particulière. Juger qu’une décision d’architecture est mauvaise, que l’intention a été mal interprétée, qu’un cas critique a été escamoté, cela demande exactement la compétence d’un architecte ou d’un développeur chevronné. La revue, longtemps perçue comme une tâche un peu ingrate qu’on déléguait volontiers aux plus juniors pour les faire monter en compétence, redevient un acte de conception à part entière.
Cela a une conséquence d’organisation directe. On ne peut pas confier la revue d’un flot de code généré à quelqu’un qui n’a pas le recul pour évaluer des décisions. Le risque n’est plus qu’il laisse passer une coquille, ce serait sans gravité. Le risque est qu’il valide une orientation qui coûtera cher dans six mois, parce qu’elle était bien écrite et qu’il a regardé le texte plutôt que le choix. Une équipe qui génère beaucoup de code a besoin de plus de jugement senior en revue, pas de moins, alors même que la tentation est de croire l’inverse puisque « l’IA fait le travail ».
Garder la main sur ce qui compte
Je ne plaide pas pour relire moins. Je plaide pour relire autre chose. Le temps qu’on ne passe plus à traquer des broutilles, qu’un outil automatique attrape de toute façon mieux que nous, on le réinvestit là où l’IA est faible et où l’humain reste seul juge : l’adéquation au besoin réel, le respect de la structure, le traitement de ce qui sort du cas nominal, le sens de ce qui a disparu.
La revue de code n’est pas en train de disparaître sous l’IA. Elle est en train de retrouver sa vraie fonction, qu’on avait fini par confondre avec une chasse aux fautes parce que c’était la partie la plus facile à objectiver. Relire, ce n’a jamais été vérifier que le code compile ni qu’il respecte le style. C’est vérifier que quelqu’un a réfléchi, et que cette réflexion tient. La seule différence, désormais, c’est que ce quelqu’un doit être le reviewer. Parce que l’auteur, lui, ne réfléchit pas. Il génère.
// À lire ensuite