Comment Conventional Comments a transformé nos code reviews
Dans une équipe de développement, relire le code des autres c’est naturel. Savoir comment formuler ses retours, beaucoup moins.
Dans une équipe de développement, relire le code des autres c’est naturel. Savoir comment formuler ses retours, beaucoup moins.
Chez Indy, nos reviews fonctionnaient bien. Le code était relu, les bugs étaient détectés. Mais la façon dont on formulait nos commentaires créait parfois de la confusion et des allers-retours inutiles. On a donc adopté Conventional Comments, une convention toute simple qui a rendu nos échanges plus clairs et nos reviews plus efficaces.
Voici pourquoi et comment.
Ce qu'on voulait améliorer
Certaines situations revenaient régulièrement dans nos reviews. Pas des problèmes graves, mais des frictions qu’on pensait évitables.
L’intention qui ne passe pas à l’écrit
Un reviewer écrit sur ce bout de code
for (const user of users) {
await stripe.customers.create({ email: user.email });
}
L'appel à Stripe est fait dans la boucle, ça va générer une requête par utilisateur.
Le constat est bon. Le reviewer a vu un vrai sujet. Mais l’auteur de la PR ne sait pas quoi en faire. C’est un bug à corriger avant le merge ? Une suggestion d’optimisation pour plus tard ? Juste une observation ? Ça finit en « Tu veux que je change quelque chose ou c’est juste une remarque ? »
Le problème n’est pas la qualité du commentaire. C’est que l’écrit ne transmet pas l’intention aussi facilement qu’on le croit. Notre commentaire peut être lu de plusieurs façons différentes.
Le filtre de l’expérience
Dans beaucoup d’équipes, un développeur junior va naturellement moins commenter les PR d’un senior. Ce n’est pas qu’il n’a rien à dire, c’est qu’il ne sait pas comment le dire. Est-ce que c’est pertinent ? Est-ce que ça va paraître naïf ? Sans cadre clair, le doute l’emporte et il laisse passer.
De l’autre côté, un senior qui écrit un commentaire direct ne cherche pas à fermer la discussion. C’est juste sa manière d’aller à l’essentiel. Mais quand il n’y a pas de langage partagé, le même commentaire peut être lu comme un retour constructif ou comme une fin de non-recevoir.
Pas de hiérarchie dans les commentaires
Prenons deux commentaires issus d’une vraie review :
Il manque une ligne vide avant le return.
Le Promise.all() ici lance les trois appels en parallèle, mais le deuxième a besoin du résultat du premier pour fonctionner.
Ces deux commentaires vivent côte à côte dans la même review. L’un est un détail de formatting, l’autre est un vrai bug. Mais rien ne les distingue. L’auteur de la PR les traite avec le même niveau d’attention, alors que l’un mériterait clairement plus de focus que l’autre.
Le principe de Conventional Comments
Conventional Comments est une convention légère pour structurer les commentaires de review. Pas un outil à installer, pas un process à déployer. Juste un format d’écriture que l’équipe décide d’adopter ensemble.
Chaque commentaire suit ce format :
<label> [decorations]: <subject> [discussion]
Nous avons quatre éléments, dont deux optionnels :
- Label : le type de commentaire (issue, suggestion, nitpick…). C’est lui qui porte l’intention. En un mot, le lecteur sait à quoi s’attendre.
- Decoration (optionnelle) : Utile quand le label seul ne suffit.
- Sujet : le message principal, en une ligne. Ce qu’on veut dire.
- Discussion (optionnelle) : le contexte, le raisonnement, une piste de solution. Le « pourquoi » derrière le commentaire.
L’idée est simple : à l’écrit, le ton est difficile à deviner. Le label le remplace, il dit explicitement ce que le reviewer attend.
Les labels qu’on utilise
| Label | Ce que ça veut dire |
|---|---|
| issues | Un vrai problème : bug, risque, régression. À traiter. |
| suggestion | Une vraie amélioration : meilleur pattern, refacto, optimisation. Le code marche sans, mais il serait mieux avec. |
| question | On ne comprend pas un choix, on demande une clarification. |
| nitpick | Un détail de style ou de préférence. Non bloquant par définition. |
| praise | Quelque chose de bien fait qu’on veut souligner. |
| thought | Une réflexion, une idée. Pas une demande de changement. |
| todo | Un petit changement nécessaire mais trivial pouvant être effectué plus tard. |
| chore | Une tâche mécanique avant le merge (lancer un script, mettre à jour un fichier). |
| note | Une info utile pour le lecteur, sans action attendue. |
On n’utilisera pas tous les labels tout le temps. L’important, c’est que chaque équipe trouve le sous-ensemble qui lui convient.
Les décorations
Les décorations se placent entre parenthèses après le label. Elles ajoutent du contexte sans changer le type de commentaire. Il y en a deux familles.
- Le niveau de blocage : est-ce que ça doit être traité avant le merge ?
issue (blocking): Cette mutation n'est pas idempotente.
suggestion (non-blocking): On pourrait extraire cette logique dans un composable dédié.
suggestion (if-minor): Renommer cette variable, seulement si le changement reste trivial.
- Le domaine concerné : de quoi on parle exactement ?
issue (security): Le token est stocké en clair dans le localStorage.
suggestion (performance): Cette requête SQL fait un full scan, un index sur user_id réglerait le problème.
nitpick (documentation): Le JSDoc de cette fonction ne mentionne pas le cas d'erreur.
issue (accessibility): Ce bouton n'a pas de label accessible, un lecteur d'écran ne saura pas à quoi il sert.
On peut aussi combiner les deux : issue (blocking, security): quand c’est à la fois critique et lié à la sécurité. L’idée c’est pas de tout décorer systématiquement, mais d’ajouter une précision quand elle aide le lecteur.
En pratique
Reprenons d’abord le commentaire sur l’appel Stripe qu’on a vu plus haut :
L'appel à Stripe est fait dans la boucle, ça va générer une requête par utilisateur.
Avec Conventional Comments, ça devient :
suggestion (non-blocking): L'appel à Stripe est fait dans la boucle, ça va générer une requête par utilisateur.
Même commentaire, mais maintenant tout est plus clair : c’est une suggestion, c’est pas bloquant, et il y a une piste concrète. L’aller-retour disparaît.
Voici d’autres exemples du quotidien.
Un vrai problème à traiter (issue)
const user = await getUser(id);
const orders = await getOrders(user.id);
issue (blocking): Si getUser renvoie null, la ligne d'après crash sur user.id.
On ne comprend pas un choix (question)
const MAX_RETRIES = 10;
question: Pourquoi 10 ? C'est basé sur un cas réel ou c'est arbitraire ?
Un détail (nitpick)
const d = new Date(user.createdAt);
nitpick: "d" est cryptique, "createdDate" serait plus clair.
Du code bien fait qu’on souligne (praise)
const schema = z.discriminatedUnion("type", [invoiceSchema, creditNoteSchema]);
praise: Bon usage du discriminatedUnion, c'est le bon pattern ici.
Une réflexion (thought)
thought: On a deux façons de gérer les erreurs dans le repo, ça vaudrait le coup d'en discuter en équipe pour converger.
Ce que ça a changé chez nous
Les PR avancent plus vite
Avant, l’auteur d’une PR devait relire chaque commentaire pour deviner s’il était bloquant ou non. Trois commentaires ambigus pouvaient bloquer un merge, le temps de clarifier. Maintenant, quand on voit trois nitpick et zéro issue, on sait que la PR est prête. On traite les détails si besoin sans que ça bloque le merge.
Les juniors commentent plus facilement
Avant les labels, nos développeurs moins expérimentés postaient rarement des commentaires sur les PR des seniors. Depuis, on voit des « question: pourquoi on n’utilise pas X ici ? » apparaître régulièrement. Le label porte l’intention à leur place, c’est une question, pas une critique.
Le senior le sait, le junior le sait, tout le monde est à l’aise. Un thought permet aussi de partager une réflexion sans s’engager sur une demande de changement. C’est un espace que beaucoup de gens dans l’équipe n’osaient pas prendre avant.
Et c’est loin d’être anecdotique. Un développeur qui ose commenter, poser des questions, challenger un choix technique, c’est un développeur qui progresse. Cette légitimité à s’exprimer dans une review, c’est un levier de montée en compétence qu’il faut encourager, pas laisser au hasard.
Les échanges s’enlisent moins
Un nitpick: dit explicitement « c’est un détail, fais-en ce que tu veux ». L’auteur ne se sent pas sommé de tout corriger, le reviewer ne se sent pas ignoré quand sa remarque n’est pas suivie.
Au fond, les labels enlèvent l’émotion de l’équation. Particulièrement en français, l’écrit véhicule mal les intentions, une phrase neutre peut sonner sèche, une suggestion peut ressembler à un ordre, le label neutralise ça. Le retour devient plus simple à formuler, et plus simple à recevoir.
Et quand un vrai désaccord apparaît, la meilleure chose à faire reste d’en discuter de vive voix.
Le praise change la culture
On le dit rarement, mais souligner ce qui est bien fait a un vrai impact. Un praise fait plaisir à recevoir et rappelle que la review n’est pas là uniquement pour pointer les problèmes.
Ça contribue à une culture où les gens ont envie de relire le code des autres et où la review est aussi un outil de partage de connaissances, pas seulement de détection de bugs.
On s’oblige à réfléchir
Le simple fait de choisir un label force à clarifier sa propre pensée. Est-ce que c’est vraiment une issue ou juste un nitpick ? Est-ce que c’est blocking ou non-blocking ? Cette micro-réflexion produit des commentaires plus précis et plus justes.
Le format force le reviewer à expliciter son niveau de préoccupation, c’est bénéfique des deux côtés.
Comment on l’a adopté
On n’a pas fait de grande annonce ni imposé un nouveau process. Quelques personnes ont commencé à utiliser les labels dans leurs reviews. Les autres ont vu le format, ont compris l’intérêt, et ont suivi naturellement.
Si vous voulez faire pareil, voici ce qui a marché pour nous.
Montrer l’exemple
Plutôt qu’imposer une règle, utilisez les labels dans vos propres reviews. L’intérêt se voit immédiatement quand quelqu’un reçoit par exemple un nitpick pour la première fois, il comprend tout de suite la valeur du format.
Outiller l’équipe
Le plus gros frein à l’adoption, c’est la friction. Si taper un label prend plus de temps qu’écrire un commentaire libre, personne ne le fera.
Sur GitHub, les saved replies permettent de pré-remplir vos labels en un clic.
Petit bonus maison, un dev de l’équipe a développé Conventional Shields, une extension Chrome, Firefox et Raycast qui ajoute des badges visuels directement dans l’interface GitHub pour rendre les labels encore plus lisibles.
Ne pas être rigide
Si quelqu’un écrit nit au lieu de nitpick, l’intention est là. L’objectif c’est de mieux communiquer, pas de respecter un format à la lettre. Pareil, si un commentaire n’a pas de label mais qu’il est clair et constructif, personne ne va le refuser.
Faire une review en mob les premières semaines
Les premiers jours, faites une ou deux reviews à plusieurs. Quelqu’un partage son écran, on commente ensemble, on discute du choix de label. Ça aligne tout le monde et ça évite que chacun interprète les labels à sa manière.
Pour conclure
Conventional Comments ne résout pas tous les sujets liés aux code reviews. Ça ne remplace pas :
- une culture de feedback saine
- la discipline de faire des PR de taille raisonnable
- le réflexe de passer en visio quand un désaccord s’enlise à l’écrit
- la relecture de son propre code avant de l’envoyer en review
Mais ça apporte quelque chose de précieux : rendre explicite ce qui était implicite. L’intention du reviewer, la priorité du commentaire, le niveau d’exigence attendu, tout est clair dès la première ligne. Et ça change la dynamique.
La review devient un vrai espace de collaboration et d’apprentissage, où chacun peut s’exprimer avec confiance, quel que soit son niveau d’expérience.
Parce qu’au fond, une bonne code review, ce n’est pas qu’une question de code. C’est surtout une question de communication.