Le conseil de « bon goût » de Brian Kernighan montre comment un code lisible fait gagner du temps, réduit les bugs et aide les équipes réelles à avancer plus vite que des astuces intelligentes.

Le nom de Brian Kernighan apparaît partout où de nombreux développeurs l’utilisent sans y penser : outils Unix classiques, écosystème C, et des décennies d’écrits qui ont appris aux gens à expliquer les programmes clairement. Que vous vous rappeliez The C Programming Language (avec Dennis Ritchie), The Unix Programming Environment, ou ses essais et conférences, le fil commun est une insistance sur des idées simples exprimées proprement.
Le meilleur conseil de Kernighan ne dépend pas de la syntaxe C ni des conventions Unix. Il porte sur la manière dont les humains lisent : nous recherchons la structure, nous nous fions aux noms, nous inférons l’intention, et nous sommes perdus quand le code cache le sens derrière des astuces. C’est pourquoi le « goût » pour la lisibilité compte toujours quand vous écrivez en TypeScript, Python, Go, Java ou Rust.
Les langages changent. Les outils s’améliorent. Les équipes livrent toujours des fonctionnalités sous pression, et la plupart du code est maintenu par quelqu’un d’autre que l’auteur original (souvent vous du futur). La clarté est le multiplicateur qui rend tout cela viable.
Ce n’est pas un éloge du « hero coding » ni un appel à mémoriser des règles rétro. C’est un guide pratique des habitudes qui rendent le code de tous les jours plus facile à manipuler :
L’influence de Kernighan importe parce qu’elle pointe vers un objectif simple et convivial pour l’équipe : écrire du code qui communique. Quand le code se lit comme une explication claire, vous passez moins de temps à le décoder et plus de temps à l’améliorer.
Le « bon goût » pour un code lisible n’est pas une affaire de style personnel, de patterns sophistiqués ou de compresser une solution en moins de lignes. C’est l’habitude de choisir l’option la plus simple et la plus claire qui communique de manière fiable l’intention.
Une solution de bon goût répond à une question basique pour le lecteur suivant : Que cherche à faire ce code, et pourquoi le fait-il de cette façon ? Si cette réponse exige des acrobaties mentales, des hypothèses cachées, ou de décoder des astuces, le code coûte du temps à l’équipe.
La plupart du code est lu bien plus souvent qu’il n’est écrit. Le « bon goût » traite la lecture comme l’activité principale :
C’est pourquoi la lisibilité n’est pas seulement esthétique (indentation, largeur de ligne, ou snake_case). Ces éléments aident, mais le « bon goût » concerne surtout la facilité de raisonnement : noms clairs, flux de contrôle évident, et structure prévisible.
Une erreur courante est d’optimiser pour la brièveté au lieu de la clarté. Parfois, le code le plus clair est un peu plus long parce qu’il explicite les étapes.
Par exemple, comparez deux approches :
La deuxième version peut ajouter des lignes, mais réduit la charge cognitive nécessaire pour vérifier la correction. Elle rend aussi les bugs plus faciles à isoler et les changements plus sûrs à appliquer.
Le bon goût, c’est savoir quand arrêter d’« améliorer » une solution par la malice et, à la place, rendre l’intention manifeste. Si un collègue peut comprendre le code sans vous demander une visite guidée, vous avez bien choisi.
Le code malin donne souvent l’impression d’une victoire sur le moment : moins de lignes, une astuce élégante, un effet « wow » dans le diff. Dans une vraie équipe, cette débrouillardise devient une facture récurrente — payée en temps d’intégration, en temps de revue, et en hésitation chaque fois que quelqu’un doit toucher le code.
L’intégration ralentit. Les nouveaux coéquipiers doivent non seulement apprendre le produit ; ils doivent aussi apprendre votre dialecte privé de raccourcis. Si comprendre une fonction exige de décoder des opérateurs astucieux ou des conventions implicites, les gens éviteront de la modifier — ou le feront avec peur.
Les revues s’allongent et deviennent moins fiables. Les reviewers dépensent de l’énergie à prouver que l’astuce est correcte plutôt qu’à évaluer si le comportement correspond à l’intention. Pire, le code malin est plus difficile à simuler mentalement, si bien que les reviewers ratent des cas limites qu’ils auraient attrapés dans une version simple.
La débrouillardise se cumule pendant :
Quelques récidivistes :
17, 0.618, -1) qui codent des règles que personne ne se rappelle.&& / ||) qui reposent sur la connaissance de règles d’évaluation subtiles.L’idée de Kernighan sur le « goût » réapparaît ici : la clarté n’est pas écrire plus ; c’est rendre l’intention évidente. Si une version « intelligente » économise 20 secondes aujourd’hui mais coûte 20 minutes à chaque lecteur futur, elle n’est pas intelligente — elle est chère.
Le « goût » de Kernighan se manifeste souvent dans de petites décisions répétables. Vous n’avez pas besoin d’un grand refactor pour rendre le code plus vivable — les petites victoires de clarté se cumulent chaque fois que quelqu’un parcourt un fichier, cherche un comportement ou corrige un bug sous pression.
Un bon nom réduit le besoin de commentaires et rend les erreurs plus difficiles à masquer.
Visez des noms révélant l’intention qui correspondent au vocabulaire de l’équipe :
invoiceTotalCents à sum.Si un nom vous oblige à le décoder, il fait l’inverse de son travail.
La plupart de la lecture est un balayage. Un espacement et une structure cohérents aident l’œil à trouver l’essentiel : limites de fonctions, conditionnels et le « happy path ».
Quelques habitudes pratiques :
Quand la logique devient tricky, la lisibilité s’améliore généralement en rendant les décisions explicites.
Comparez ces deux styles :
// Harder to scan
if (user \u0026\u0026 user.active \u0026\u0026 !user.isBanned \u0026\u0026 (role === 'admin' || role === 'owner')) {
allow();
}
// Clearer
if (!user) return deny('missing user');
if (!user.active) return deny('inactive');
if (user.isBanned) return deny('banned');
if (role !== 'admin' \u0026\u0026 role !== 'owner') return deny('insufficient role');
allow();
La seconde version est plus longue, mais elle se lit comme une checklist — et il est plus simple de l’étendre sans casser quoi que ce soit.
Ce sont des choix « petits », mais c’est l’art quotidien du code maintenable : des noms honnêtes, un formatage qui guide le lecteur, et un flux de contrôle qui évite les acrobaties mentales.
Le style de clarté de Kernighan apparaît surtout dans la façon de découper le travail en fonctions et modules. Un lecteur doit pouvoir parcourir la structure, deviner ce que fait chaque pièce, et être majoritairement dans le vrai avant de lire les détails.
Visez des fonctions qui font exactement une chose à un seul « niveau de zoom ». Quand une fonction mélange validation, règles métier, formatage et I/O, le lecteur doit garder plusieurs fils en tête.
Un test rapide : si vous vous surprenez à écrire des commentaires comme “// now do X” dans une fonction, X est souvent un bon candidat pour une fonction séparée avec un nom clair.
Les longues listes de paramètres sont une taxe de complexité cachée : chaque site d’appel devient un mini-fichier de configuration.
Si plusieurs paramètres voyagent toujours ensemble, regroupez-les de manière réfléchie. Les objets d’options (ou petites structures de données) peuvent rendre les points d’appel explicites — si vous gardez le groupe cohérent et évitez de tout fourrer dans un sac « misc ».
Privilégiez aussi le passage de concepts du domaine plutôt que des primitifs. UserId vaut mieux que string, et DateRange vaut mieux que (start, end) quand ces valeurs ont des règles.
Les modules sont des promesses : « tout ce dont vous avez besoin pour ce concept est ici, et le reste est ailleurs. » Gardez les modules assez petits pour pouvoir tenir leur but en tête, et concevez des frontières qui minimisent les effets de bord.
Habitudes pratiques :
Quand vous avez besoin d’un état partagé, nommez-le honnêtement et documentez ses invariants. La clarté n’est pas éviter la complexité — c’est la placer là où le lecteur s’y attend. Pour en savoir plus sur le maintien de ces frontières lors des changements, voir /blog/refactoring-as-a-habit.
Le « goût » de Kernighan se manifeste dans la manière de commenter : l’objectif n’est pas d’annoter chaque ligne, mais de réduire la confusion future. Le meilleur commentaire est celui qui empêche une mauvaise hypothèse — surtout quand le code est correct mais surprenant.
Un commentaire qui répète le code (« incrémente i ») ajoute du bruit et apprend aux lecteurs à ignorer les commentaires. Les commentaires utiles expliquent l’intention, les compromis ou les contraintes qui ne sont pas évidents dans la syntaxe.
# Bad: says what the code already says
retry_count += 1
# Good: explains why the retry is bounded
retry_count += 1 # Avoids throttling bans on repeated failures
Si vous êtes tenté d’écrire des commentaires « quoi », c’est souvent le signe que le code devrait être plus clair (meilleurs noms, fonction plus petite, flux de contrôle plus simple). Laissez le code porter les faits ; laissez les commentaires porter le raisonnement.
Rien n’affecte la confiance plus vite qu’un commentaire obsolète. Si un commentaire est optionnel, il dérivera avec le temps ; s’il est faux, il devient une source active de bugs.
Une habitude pratique : traitez la mise à jour des commentaires comme faisant partie du changement, pas comme un « bonus ». Lors des revues, il est légitime de demander : Est-ce que ce commentaire correspond encore au comportement ? Sinon, mettez-le à jour ou supprimez-le. « Pas de commentaire » vaut mieux qu’un « mauvais commentaire ».
Les commentaires inline servent pour les surprises locales. Les guidances plus larges doivent aller dans des docstrings, README ou notes développeur — surtout pour :
Un bon docstring indique comment utiliser correctement la fonction et quelles erreurs attendre, sans narrer l’implémentation. Une courte note dans /docs ou README peut capturer l’histoire « pourquoi nous avons fait ainsi » pour qu’elle survive aux refactors.
La victoire discrète : moins de commentaires, mais chacun doit le mériter.
La plupart du code semble « correct » sur le happy path. Le vrai test du goût, c’est ce qui se passe quand les entrées manquent, les services timeoutent, ou un utilisateur fait quelque chose d’inattendu. Sous stress, le code malin a tendance à cacher la vérité. Le code clair rend l’échec évident — et récupérable.
Les messages d’erreur font partie de votre produit et de votre flux de debug. Rédigez-les comme si la personne suivante qui les lirait était fatiguée et en astreinte.
Incluez :
Si vous avez du logging, ajoutez du contexte structuré (comme requestId, userId, ou invoiceId) pour rendre le message exploitable sans fouiller d’autres données.
Il y a une tentation à « tout gérer » avec un one-liner malin ou un catch-all générique. Le bon goût, c’est de choisir les quelques cas limites qui comptent et de les rendre visibles.
Par exemple, une branche explicite pour « entrée vide » ou « non trouvé » se lit souvent mieux qu’une chaîne de transformations qui produit implicitement null au milieu. Quand un cas spécial est important, nommez-le et placez-le en évidence.
Mélanger les formes de retour (parfois un objet, parfois une string, parfois false) contraint le lecteur à maintenir un arbre de décisions mental. Préférez des patterns qui restent cohérents :
Une gestion claire des échecs réduit la surprise — et la surprise est là où naissent bugs et alertes nocturnes.
La clarté ne concerne pas seulement ce que vous vouliez dire en écrivant le code. Elle concerne ce que la prochaine personne s’attend à voir en ouvrant un fichier à 16h55. La cohérence transforme la « lecture du code » en reconnaissance de patterns — moins de surprises, moins de malentendus, moins de débats qui recommencent à chaque sprint.
Un bon guide d’équipe est court, spécifique et pragmatique. Il n’essaie pas d’encoder chaque préférence ; il tranche les questions récurrentes : conventions de nommage, structure des fichiers, patterns de gestion d’erreur, et ce que signifie « terminé » pour les tests.
La vraie valeur est sociale : il empêche la même discussion de se rejouer à chaque nouvelle PR. Quand quelque chose est écrit, les revues basculent de « je préfère X » à « nous avons convenu de X (et voici pourquoi) ». Gardez-le vivant et facile à trouver — de nombreuses équipes le placent dans le repo (par exemple, /docs/style-guide.md) pour qu’il soit proche du code.
Utilisez formatters et linters pour tout ce qui est mesurable et ennuyeux :
Cela libère les humains pour se concentrer sur le sens : nommage, forme des API, cas limites, et correspondance du code avec l’intention.
Les règles manuelles restent importantes quand elles décrivent des choix de conception — par ex. « Préférez les retours précoces pour réduire l’imbrication » ou « Une entrée publique par module ». Les outils ne peuvent pas juger pleinement ces aspects.
Parfois la complexité se justifie : budget de performance serré, contraintes embarquées, concurrence délicate, ou comportement spécifique à une plateforme. L’accord devrait être : les exceptions sont permises, mais explicites.
Une norme simple aide : documenter le compromis dans un court commentaire, ajouter un micro-benchmark ou une mesure quand la performance est invoquée, et isoler le code complexe derrière une interface claire pour que la majeure partie du code reste lisible.
Une bonne revue devrait ressembler moins à une inspection et davantage à une leçon courte et ciblée sur le « bon goût ». Kernighan n’affirme pas que le code malin soit diabolique — il dit que la débrouillardise est coûteuse quand d’autres doivent vivre avec. Les revues sont l’endroit où ces compromis deviennent visibles et où l’équipe peut choisir la clarté volontairement.
Commencez par vous demander : « Un collègue peut-il comprendre ceci en une passe ? » Cela signifie généralement regarder les noms, la structure, les tests et le comportement avant de plonger dans les micro-optimisations.
Si le code est correct mais difficile à lire, traitez la lisibilité comme un vrai défaut. Suggérez de renommer des variables pour refléter l’intention, d’extraire des fonctions longues, de simplifier le flux de contrôle, ou d’ajouter un petit test montrant le comportement attendu. Une revue qui détecte « ça marche, mais je ne vois pas pourquoi » évite des semaines de confusion future.
Un ordre pratique qui fonctionne bien :
Les revues dérapent quand le feedback ressemble à un concours de points. Plutôt que « Pourquoi as-tu fait ça ? », essayez :
Les questions invitent à la collaboration et font souvent surgir des contraintes que vous ignoriez. Les suggestions indiquent une direction sans impliquer d’incompétence. Ce ton est la façon dont le « goût » se répand dans une équipe.
Si vous voulez une lisibilité cohérente, ne comptez pas sur l’humeur du reviewer. Ajoutez quelques « checks de clarté » à votre template de revue et à la définition de fait. Gardez-les courts et précis :
Avec le temps, cela transforme les revues de code de la police à l’enseignement du jugement — exactement la discipline quotidienne que Kernighan prônait.
Les outils LLM peuvent produire du code fonctionnel rapidement, mais « ça marche » n’est pas la barre indiquée par Kernighan — ça communique l’est. Si votre équipe utilise un workflow de génération (par ex. construire des fonctionnalités via chat et itérer sur du code généré), traitez la lisibilité comme un critère d’acceptation de première classe.
Sur des plateformes comme Koder.ai, où vous pouvez générer des frontends React, des backends Go et des apps Flutter depuis une conversation (et exporter le code source ensuite), les mêmes habitudes de goût s’appliquent :
La vitesse est vraiment utile quand le résultat reste facile à relire, maintenir et étendre par des humains.
La clarté n’est pas un état que l’on « atteint » une fois. Le code reste lisible seulement si vous le poussez régulièrement vers une langue plus simple à mesure que les exigences changent. La sensibilité de Kernighan s’intègre ici : préférez des améliorations régulières et compréhensibles plutôt que des réécritures héroïques ou des one-liners « intelligents » qui impressionnent aujourd’hui et embrouillent le mois prochain.
Le refactor le plus sûr est ennuyeux : de petits changements qui conservent un comportement identique. Si vous avez des tests, exécutez-les après chaque étape. Sinon, ajoutez quelques vérifications ciblées autour de la zone que vous touchez — pensez-les comme des garde-fous temporaires pour améliorer la structure sans peur.
Un rythme pratique :
Les petits commits rendent aussi la revue plus simple : les collègues peuvent juger l’intention, pas chercher des effets de bord.
Vous n’avez pas à purger toutes les constructions « malines » en une fois. Quand vous touchez le code pour une fonctionnalité ou un bug, échangez les raccourcis astucieux contre des équivalents simples :
C’est ainsi que la clarté gagne dans les équipes réelles : un hotspot amélioré à la fois, exactement là où les gens travaillent déjà.
Toutes les améliorations ne sont pas urgentes. Une règle utile : refactorez maintenant quand le code change activement, est fréquemment mal compris, ou est susceptible de causer des bugs. Planifiez plus tard quand il est stable et isolé.
Rendez la dette de refactor visible : laissez un court TODO avec le contexte, ou ajoutez un ticket qui décrit la douleur (« difficile d’ajouter de nouveaux moyens de paiement ; la fonction fait 5 jobs »). Ainsi, vous décidez délibérément plutôt que de laisser un code confus devenir la taxe permanente de l’équipe.
Si vous voulez que le « bon goût » apparaisse régulièrement, facilitez sa pratique. Voici une checklist légère à réutiliser en planification, codage et revue — assez courte pour se souvenir, assez précise pour agir.
Avant : process(data) fait validation, parsing, sauvegarde et logging en un seul endroit.
Après : Scindez en validateInput, parseOrder, saveOrder, logResult. La fonction principale devient un plan lisible.
Avant : if not valid then return false répété cinq fois.
Après : Une section de garde upfront (ou une fonction de validation) qui retourne une liste claire de problèmes.
Avant : x, tmp, flag2, doThing().
Après : retryCount, draftInvoice, isEligibleForRefund, sendReminderEmail().
Avant : Une boucle avec trois cas spéciaux cachés au milieu.
Après : Gérez d’abord les cas spéciaux (ou extrayez des helpers), puis exécutez la boucle simple.
Choisissez une amélioration à adopter cette semaine : « pas de nouvelles abréviations », « happy path first », « extraire un helper par PR », ou « chaque message d’erreur inclut les étapes suivantes ». Suivez-la pendant sept jours, puis gardez ce qui a réellement facilité la lecture.
L’influence de Kernighan porte moins sur le langage C que sur un principe durable : le code est un moyen de communication.
Les langages et les frameworks évoluent, mais les équipes ont toujours besoin de code facile à parcourir, à comprendre, à relire et à déboguer — surtout des mois plus tard et sous pression temporelle.
Le « bon goût » consiste à choisir systématiquement l’option la plus simple et la plus claire qui communique l’intention.
Un test utile : un collègue peut-il répondre « que fait ceci, et pourquoi c’est fait ainsi ? » sans décoder des astuces ou s’appuyer sur des hypothèses cachées.
Parce que la plupart du code est lu bien plus souvent qu’il n’est écrit.
Optimiser pour les lecteurs réduit le temps d’intégration, les frictions en revue et le risque de modifications erronées — notamment quand le mainteneur est le « vous du futur » avec moins de contexte.
La « taxe de la débrouillardise » se manifeste comme :
Si la version astucieuse fait gagner quelques secondes maintenant mais coûte des minutes à chaque fois qu’on la touche, c’est une perte nette.
Parmi les coupables fréquents :
Ces constructions masquent l’état intermédiaire et rendent les cas limites plus faciles à manquer en revue.
Lorsque cela réduit la charge cognitive.
Rendre les étapes explicites avec des variables nommées (par ex. valider → normaliser → calculer) facilite la vérification de la justesse, simplifie le débogage et rend les modifications futures plus sûres — même si cela ajoute quelques lignes.
Visez :
invoiceTotalCents plutôt que sum)Si un nom vous force à le décoder, il ne fait pas son travail : il doit réduire le besoin de commentaires supplémentaires.
Privilégiez un branchement simple et gardez le « happy path » visible.
Tactiques utiles :
Commentez le pourquoi, pas le quoi.
Les bons commentaires capturent l’intention, les compromis, les contraintes ou les invariants non évidents. Évitez de narrer un code déjà clair, et considérez la mise à jour des commentaires comme faisant partie du changement — les commentaires périmés sont pires que pas de commentaire.
Servez-vous des outils pour les règles mécaniques (formatage, imports, pièges évidents) et réservez la revue humaine au sens.
Un guide de style léger contribue en tranchant les décisions récurrentes (noms, structure, gestion d’erreur) afin que les revues portent sur la clarté et le comportement, pas sur les préférences personnelles.
Quand vous faites des exceptions pour performance ou contraintes, documentez le compromis et isolez la complexité derrière une interface propre.