Menu

Revue de code avec l'IA : prompts et méthode

Un bon prompt de revue de code dit quoi chercher, demande une gravité pour chaque problème, exige une entrée concrète qui le déclenche et un diff, et demande d'ignorer le style. Ensuite, vous vérifiez chaque problème, car certains seront faux.

Chaque prompt de cette page se modifie, puis s'ouvre dans ChatGPT, Claude ou une autre application d'IA.

Un prompt de revue de code doit dire au modèle quoi chercher, quelle est la gravité de chaque problème et quelle preuve fournir. "Relis ce code" obtient une liste aimable de suggestions où une faille de sécurité se glisse entre un conseil de docstring et une préférence de nommage. Un prompt de revue avec une liste de contrôle, des niveaux de gravité et une demande de diffs obtient un rapport exploitable, un problème à la fois.

De "relis ça" à une liste de contrôle avec gravité

Le code des deux onglets contient deux vrais bugs. Comparez la façon dont chaque réponse les présente.

Tu peux relire ce code ? def get_user(conn, username): cur = conn.cursor() cur.execute(f"SELECT id, email FROM users WHERE name = '{username}'") row = cur.fetchone() return {"id": row[0], "email": row[1]}
Try it
Example replyReplies vary between models and runs.

Dans l'ensemble, la fonction est courte et facile à suivre. Quelques suggestions :

  • Ajoutez des annotations de type, par exemple username: str, pour clarifier la signature.
  • Ajoutez une docstring qui explique ce que renvoie la fonction.
  • Utilisez des requêtes paramétrées. Construire du SQL avec des f-strings peut mener à une injection SQL.
  • Gérez les utilisateurs absents. fetchone() peut renvoyer None.
  • Nommage : fetch_user décrirait peut-être mieux l'accès à la base de données.

La première réponse a trouvé les deux bugs, et c'est justement le problème quand on juge une revue à ce qu'elle mentionne. L'injection arrive en troisième position sur cinq, atténuée en "peut mener à", sans correction, à côté d'un conseil de docstring. Un lecteur qui la survole ajouterait des annotations de type et passerait à autre chose.

Le second prompt a changé quatre choses :

  • Le périmètre. "Uniquement les bugs, la sécurité et la gestion des erreurs. Ignore le style" supprime le bruit. Le style relève d'un linter et d'un formateur, plus rapides et jamais en désaccord avec eux-mêmes.
  • La gravité. Elle oblige le modèle à hiérarchiser, et une liste hiérarchisée vous dit par où commencer.
  • Une entrée déclenchante. "x' OR '1'='1" transforme un risque vague en démonstration que vous pouvez exécuter. C'est aussi votre meilleur filtre contre les faux positifs, comme le montre la dernière section.
  • Les diffs. Un petit diff par problème peut être appliqué ou refusé séparément. Une fonction réécrite mélange chaque correction avec des changements que personne n'a demandés.

"S'il n'y a rien à un niveau de gravité, n'invente rien" compte plus qu'il n'y paraît. Quand on lui demande une revue, un modèle a tendance à produire des constats même quand il y a peu à trouver, et les plus faibles gonflent la liste. Une autorisation explicite de ne rien signaler à un niveau réduit ce remplissage.

Le contexte dont le relecteur a besoin

Un relecteur humain sait à quoi sert le code et d'où viennent ses entrées. Le modèle ne sait que ce que vous collez. "username vient directement d'un formulaire de connexion" est la phrase qui fait de l'injection SQL un problème de gravité haute au lieu d'un risque théorique. Le contexte utile pour une revue :

  • D'où viennent les entrées : des utilisateurs, un autre service interne, un fichier de configuration que vous contrôlez.
  • Ce qui appelle le code et ce qu'il attend en retour.
  • L'environnement d'exécution : version du langage, framework, pilote de base de données. La correction de l'injection ci-dessus utilise ? parce que sqlite3 utilise ce marqueur ; d'autres pilotes utilisent %s.
  • Ce qui compte le plus pour cette modification : l'exactitude, la sécurité, la performance, ou les trois.

Un prompt de revue réutilisable

Ce bloc transforme la liste de contrôle en modèle. Changez le langage, l'axe et le code, et gardez les règles. Désactiver la partie contraintes est un moyen rapide de voir combien de bruit revient sans elle.

Modèle de revue de code
Fill in
Parts
Tu es un relecteur JavaScript senior. Tu signales les vrais problèmes et rien d'autre.
Relis le code ci-dessous, en te concentrant sur les bugs et la gestion des erreurs.
Ce code tourne dans le navigateur. `ids` est un tableau d'identifiants numériques venant de notre propre API.
async function loadAll(ids) { const results = []; ids.forEach(async (id) => { const res = await fetch(`/api/items/${id}`); results.push(await res.json()); }); return results; }
Pour chaque problème : la gravité (haute, moyenne ou basse), la ligne, une entrée ou une situation qui le déclenche, et un diff minimal. Le plus grave en premier.
Aucun commentaire sur le style, le nommage ou la mise en forme. Si tu n'es pas sûr qu'un problème soit réel, dis-le et indique ce qui le confirmerait. Si tu ne trouves rien, dis "Aucun problème".
Try it
Example replyReplies vary between models and runs.

1. Haute : la fonction retourne avant la fin de toute requête (ligne 3) forEach n'attend pas les callbacks async. loadAll renvoie immédiatement le tableau results vide, et les push arrivent plus tard. Tout appelant reçoit [].

2. Moyenne : les requêtes échouées sont traitées comme des données (ligne 5) Une réponse 404 ou 500 ne lève pas d'exception dans fetch. Un corps d'erreur JSON est analysé et ajouté comme s'il s'agissait d'un élément, et un corps non JSON fait lever res.json() dans un callback que personne n'attend.

Correction des deux, qui garde aussi les résultats dans le même ordre que ids :

 async function loadAll(ids) {
-  const results = [];
-  ids.forEach(async (id) => {
-    const res = await fetch(`/api/items/${id}`);
-    results.push(await res.json());
-  });
-  return results;
+  return Promise.all(
+    ids.map(async (id) => {
+      const res = await fetch(`/api/items/${id}`);
+      if (!res.ok) throw new Error(`Item ${id}: HTTP ${res.status}`);
+      return res.json();
+    })
+  );
 }

La ligne de rôle fixe un niveau d'exigence ("les vrais problèmes et rien d'autre"), et c'est là que le prompting par rôle aide le plus. Si un programme lit la revue, par exemple un script qui publie les constats en commentaires sur une pull request, demandez du JSON aux champs fixes plutôt que de la prose ; sortie structurée montre comment.

Gérer les faux positifs

Certains constats seront faux. Le modèle ne peut pas voir l'appelant qui valide l'entrée, la contrainte de base de données qui rend un cas impossible, ni la raison pour laquelle une ligne d'apparence étrange est voulue. Il peut aussi se tromper sur le comportement d'une bibliothèque ; hallucinations de l'IA explique pourquoi une affirmation assurée n'est pas une affirmation vérifiée.

Testez chaque constat avec son entrée déclenchante. Si vous ne pouvez produire aucune entrée qui atteint le problème dans votre système, contestez en apportant le contexte manquant au lieu d'accepter la correction :

Contester un constat
Tu as signalé cette fonction parce que `cents` pourrait être undefined ou une chaîne : function formatPrice(cents) { return `$${(cents / 100).toFixed(2)}`; } Elle n'est appelée que depuis `renderCart`, qui retourne immédiatement sauf si `Number.isInteger(cents)` est vrai. Est-ce toujours un problème ? Sinon, retire le constat.
Try it
Example replyReplies vary between models and runs.

Avec cette vérification en place, non : formatPrice ne reçoit jamais qu'un entier, donc le constat ne s'applique pas et je le retire.

Une chose que la vérification ne couvre pas : les entiers négatifs passent Number.isInteger, et formatPrice(-500) renvoie "$-5.00". Si des remboursements ou des remises peuvent atteindre cette fonction, vous voudrez peut-être "-$5.00" à la place. Sinon, il n'y a rien à changer.

Un modèle à qui l'on montre une nouvelle preuve doit soit retirer le constat, soit expliquer pourquoi il tient toujours au vu de cette preuve. Les deux sont utiles. Un modèle qui se range simplement à chaque contestation ne fait pas de revue : jugez la réponse à ses raisons, pas au fait qu'elle soit d'accord. Pour du code écrit par le modèle lui-même, lancez la revue dans une nouvelle conversation, pour qu'elle ne soit pas lue à travers le raisonnement qui a produit le code. Les mêmes habitudes de périmètre, de contexte et de tests améliorent le code dès le départ ; voir prompts pour coder.

Questions fréquentes

Quel est un bon prompt pour une revue de code ?

Nommez ce qu'il faut examiner (bugs, sécurité, gestion des erreurs), demandez une gravité pour chaque problème et exigez une entrée concrète qui déclenche le problème ainsi qu'un diff qui le corrige. Dites au modèle d'ignorer la mise en forme et le nommage sauf demande contraire. Donnez-lui le contexte qu'aurait un relecteur humain : à quoi sert le code et ce qui l'appelle.

L'IA peut-elle remplacer une revue de code humaine ?

Non, mais c'est une première passe utile. Elle est rapide et repère bien les bugs classiques comme un None non géré, un await oublié ou du SQL construit à partir de chaînes. Elle ne connaît ni les règles de votre produit, ni le reste de la base de code, ni les raisons d'une décision, et certains de ses constats seront faux. Utilisez-la avant une revue humaine, pas à sa place.

Pourquoi une revue de code par l'IA signale-t-elle des problèmes qui n'existent pas ?

Le modèle ne voit que le code que vous avez collé. Si une valeur est validée par l'appelant, ou si un cas ne peut jamais se produire dans votre système, le modèle ne peut pas le savoir et signale quand même le risque. Demander une entrée concrète qui fait échouer le code en filtre beaucoup : un constat sans entrée réaliste qui le déclenche est en général un faux positif.

Faut-il demander à l'IA de réécrire mon code pendant une revue ?

Demandez de petits diffs, un par problème, plutôt qu'un fichier réécrit. Une réécriture complète mélange les corrections avec des changements que personne n'a demandés, et il faudrait aussi relire la réécriture. Les diffs vous permettent d'accepter ou de refuser chaque problème séparément.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER