Écrire un code lisibleDans le dialogue d’impression, choisissez « Enregistrer au format PDF » comme destination.
Retour

Génie logiciel · C4 Qualité du code · Chapitre 1 · 5 h

Écrire un code lisible

Conventions de nommage et d'indentation ; découpage en fonctions courtes ; commentaires utiles contre commentaires bruyants ; documentation ; lecture croisée entre binômes.

Le bloc III a fixé quoi construire. Le bloc IV s'attaque au comment — la qualité du code lui-même. Et il commence par ce qui paraît le plus modeste et se révèle le plus rentable : écrire un code lisible.

Le chapitre 1 l'a chiffré : la maintenance domine le coût d'un logiciel, et sa moitié se passe à comprendre du code existant avant d'oser le modifier. Autrement dit, un code se lit bien plus souvent qu'il ne s'écrit — par vos coéquipiers, par vous-même dans six mois, redevenu étranger à ce que vous aviez écrit. Écrire pour le lecteur, et non pour la machine seule, est le premier réflexe du génie logiciel.

Nommer, le geste qui compte le plus

Le nom est la première chose que lit un humain, et de loin la plus importante. Un bon nom dit l'intention ; il épargne au lecteur de reconstituer ce que le code fait.

✗  function f(a, b, c) { ... }✓  function prixAnnuel(prixMensuel, estEtudiant, anciennete) { ... }

f, a, x obligent à relire tout le corps pour deviner leur rôle, à chaque lecture. prixAnnuel, estEtudiant se comprennent d'un coup d'œil. Quelques principes :

Nommer est un travail — pas une formalité. Le temps passé à trouver le bon nom est rendu au centuple à la relecture.

Les nombres magiques et les conventions

Un nombre magique est une valeur posée en dur, sans explication : x = x * 0.85. Que signifie 0.85 ? Une remise de 15 % ? Un taux de change ? Le lecteur ne peut pas savoir, et si la règle change, il faut retrouver toutes les occurrences. On le remplace par une constante nommée :

✗  prix = prix * 0.85;✓  const REMISE_ETUDIANT = 0.85;   // -15 %   prix = prix * REMISE_ETUDIANT;

Le nombre se comprend et se modifie en un seul endroit. Au-delà, les conventions — de nommage (camelCase, snake_case…), d'indentation, de mise en forme — n'ont pas de valeur en soi, mais une valeur immense partagée : un code où tout le monde suit la même convention se lit sans friction. La règle d'équipe est simple : peu importe la convention, pourvu qu'elle soit unique et respectée par tous. Un formateur automatique (comme Prettier ou l'outil de votre langage) règle la question sans débat.

Quiz · 1 question

Pourquoi remplace-t-on un « nombre magique » comme 0.85 par une constante nommée REMISE_ETUDIANT = 0.85 ?

  • Pour que le code s'exécute plus vitevitesse
  • Pour la lisibilité et la maintenabilité : le nom explique ce que vaut 0.85, et si la règle change on ne modifie qu'un seul endroit au lieu de traquer toutes les occurrencessens + un seul point de modification
  • Parce que les constantes sont obligatoires en programmationobligation

Réponse : Un nombre magique posé en dur n'a aucune vitesse à gagner ou perdre — ce n'est pas la question. Le problème est double : 0.85 ne DIT rien (remise ? taux ? seuil ?), et s'il apparaît à plusieurs endroits, changer la règle oblige à tous les retrouver sans en oublier. Une constante nommée résout les deux : elle explique la valeur (REMISE_ETUDIANT) et centralise la modification en un point unique. Rien ne rend les constantes obligatoires ; c'est un choix de qualité.

Des fonctions courtes, qui font une chose

Une fonction devrait faire une seule chose, et tenir sous les yeux — idéalement quelques lignes, à la rigueur un écran. Une fonction de 200 lignes qui recueille les entrées, calcule, met en forme et affiche est illisible : on ne peut pas la comprendre sans la parcourir en entier, ni la tester par morceaux, ni en réutiliser une partie.

Le remède est le découpage : extraire chaque sous-tâche dans une fonction bien nommée. Le corps de la fonction principale devient alors une suite d'appels qui se lit comme un résumé :

function traiterCommande(commande) {  verifierStock(commande);  const total = calculerTotal(commande);  enregistrer(commande, total);  envoyerConfirmation(commande);}

On comprend ce que fait traiterCommande sans lire le détail de chaque étape. C'est le même principe que le découpage en modules du chapitre 7, appliqué à l'échelle d'une fonction : un niveau d'abstraction à la fois.

Commentaires : utiles contre bruyants

Le bon commentaire n'est pas celui qui abonde, mais celui qui ajoute quelque chose. La règle tient en une phrase : le code dit COMMENT, le commentaire dit POURQUOI.

Un commentaire bruyant répète ce que le code dit déjà :

// on multiplie a par 12let x = a * 12;

Il n'apporte rien, encombre la lecture, et devient mensonger dès que le code change sans qu'on pense à le mettre à jour. Un commentaire utile explique ce que le code ne peut pas dire : une raison, une règle métier non évidente, un choix contre-intuitif, un piège à éviter :

// La fidélité ne s'applique qu'au-delà de 3 ans (règle commerciale, contrat client).if (anciennete > SEUIL_ANCIENNETE) prix -= REDUCTION_FIDELITE;

Le meilleur commentaire est souvent celui qu'on n'écrit pas, parce qu'un bon nom l'a rendu inutile. Réservez les commentaires aux pourquoi que le code ne montre pas.

Documentation et lecture croisée

Deux pratiques complètent la lisibilité.

La documentation du code — au niveau d'une fonction (que fait-elle ? que prend-elle en entrée ? que rend-elle ?) et au niveau du projet (un fichier README : à quoi sert-il, comment le lancer). Elle s'adresse à qui emploie le code sans vouloir en lire le détail. La bonne documentation est au plus près du code et maintenue avec lui, sinon elle diverge et ment.

La lecture croisée entre binômes (revue de code) est le meilleur test de lisibilité qui soit : un coéquipier lit votre code et dit ce qu'il ne comprend pas. Deux vertus. D'abord, si le lecteur ne comprend pas, c'est le code qu'il faut corriger, pas le lecteur. Ensuite, la revue attrape des bugs et diffuse la connaissance dans l'équipe — personne n'est le seul à connaître un morceau. C'est une pratique centrale de votre projet : relisez-vous mutuellement, tôt et souvent.

Quiz · 1 question

Lequel de ces commentaires est « utile » plutôt que « bruyant » ?

  • // incrémente i de 1 → i = i + 1répète le code
  • // La TVA est arrondie à l'inférieur car l'administration fiscale l'exige → tva = Math.floor(...)explique un pourquoi non évident
  • // boucle for → for (let i = 0; ...)répète la structure

Réponse : « incrémente i de 1 » et « boucle for » répètent ce que le code dit déjà : bruyants, ils encombrent et risquent de mentir si le code change. Le commentaire sur la TVA explique un POURQUOI que le code ne peut pas montrer — une contrainte réglementaire qui justifie l'arrondi à l'inférieur. C'est la règle : le code dit COMMENT, le commentaire dit POURQUOI. Sans ce commentaire, un futur développeur pourrait « corriger » l'arrondi et introduire une non-conformité.

À vous

L'exercice résume tout le chapitre en une transformation : prendre une fonction qui marche mais que personne ne comprend — noms opaques, nombres magiques, commentaires bruyants — et la rendre lisible, sans changer son résultat. La vérification l'exige : mêmes sorties sur tous les cas.

Ce dernier point est capital et prépare le chapitre 7 : améliorer la forme d'un code sans en toucher le comportement, c'est du refactoring — et cela ne se fait en confiance que sous la protection de tests.

Exercice de code

Rendez lisible une fonction opaque (noms, nombres magiques, commentaires bruyants) SANS changer son résultat — la vérification l'exige. Distinguez le commentaire utile (le pourquoi) du bruyant (le quoi), et retenez que la lecture croisée entre binômes est le vrai test de lisibilité.

Point de départ

// Cette fonction MARCHE, mais personne ne peut dire ce qu'elle calcule.
// Noms opaques, nombres magiques, commentaires qui répètent le code.
function f(a, b, c) {
  // on multiplie a par 12
  let x = a * 12;
  // si b est vrai on enleve 15 pourcent
  if (b) { x = x * 0.85; }
  // si c est plus grand que 3 on enleve encore 10
  if (c > 3) { x = x - 10; }
  return x;
}

// (Contexte, découvert en interrogeant l'auteur : a = prix mensuel, b = client
//  étudiant, c = nombre d'années d'ancienneté. La fonction calcule le prix
//  annuel après remises.)

// ── À VOUS : réécrire de façon LISIBLE, SANS changer le résultat ────────────
// - des noms qui disent l'intention (fonction ET paramètres) ;
// - les nombres magiques (12, 0.85, 10, 3) remplacés par des constantes nommées ;
// - pas de commentaire qui répète le code ; un commentaire seulement là où le
//   POURQUOI n'est pas évident.
function prixAnnuel(/* à nommer */) {
  return 0; // à réécrire
}

// ── Vérification : MÊME résultat que f, sur tous les cas ────────────────────
const cas = [ [30, false, 0], [30, true, 0], [30, true, 5], [50, false, 4], [50, true, 10] ];
let ok = true;
for (const [a, b, c] of cas) {
  const attendu = f(a, b, c);
  const obtenu = prixAnnuel(a, b, c);   // adapter à votre signature
  const bon = attendu === obtenu;
  ok = ok && bon;
  console.log((bon ? "  ok " : " ✗  ") + "f(" + a + "," + b + "," + c + ") = " + attendu + " / " + obtenu);
}
console.log(ok ? "Comportement préservé." : "Le résultat a changé — ce n'est plus le même code !");

Solution

const MOIS_PAR_AN = 12;
const REMISE_ETUDIANT = 0.85;      // -15 %
const REDUCTION_FIDELITE = 10;     // euros, au-delà du seuil
const SEUIL_ANCIENNETE = 3;        // années

function prixAnnuel(prixMensuel, estEtudiant, anciennete) {
  let prix = prixMensuel * MOIS_PAR_AN;
  if (estEtudiant) prix = prix * REMISE_ETUDIANT;
  // La fidélité ne s'applique qu'au-delà de SEUIL_ANCIENNETE ans (règle
  // commerciale, pas évidente à la lecture) — VOICI un commentaire utile :
  // il dit le POURQUOI, pas le QUOI.
  if (anciennete > SEUIL_ANCIENNETE) prix = prix - REDUCTION_FIDELITE;
  return prix;
}
// Comportement préservé : mêmes résultats que f sur tous les cas.

// ── Ce que l'exercice enseigne ──────────────────────────────────────────────
//
// 1. Un code se LIT bien plus souvent qu'il ne s'écrit (chapitre 1 : la moitié
//    de la maintenance, c'est comprendre). Un nom parlant — prixAnnuel,
//    estEtudiant — épargne au lecteur de RECONSTITUER l'intention. « f », « a »,
//    « x » l'y obligent à chaque lecture.
//
// 2. Les NOMBRES MAGIQUES (12, 0.85, 10, 3) posés en dur sont illisibles et
//    dangereux : que signifie 0.85 ? où changer le seuil si la règle évolue ?
//    Nommés en constantes, ils se comprennent et se modifient en un seul
//    endroit.
//
// 3. COMMENTAIRE UTILE vs BRUYANT : « on multiplie a par 12 » répète le code
//    (bruyant : si le code change, le commentaire ment). Le bon commentaire
//    explique le POURQUOI qu'on ne lit pas dans le code — ici, la règle
//    commerciale du seuil de fidélité. Règle : le code dit COMMENT, le
//    commentaire dit POURQUOI.
//
// 4. Point crucial : améliorer la lisibilité ne doit RIEN changer au
//    comportement. C'est vérifié ici par des tests — et c'est exactement ce
//    que garantira le refactoring du chapitre 7. Rendre lisible n'est pas
//    réécrire au hasard : c'est transformer la forme en préservant le fond.
//
// 5. La LECTURE CROISÉE entre binômes est le meilleur test de lisibilité :
//    si votre coéquipier comprend la fonction sans explication, elle est
//    lisible. Sinon, c'est le code qu'il faut corriger, pas le lecteur.

Ce que la suite en fait

La lisibilité agit à l'échelle d'une ligne, d'une fonction. Le chapitre 7 monte d'un cran : la conception, c'est-à-dire la manière d'organiser le code en modules qui résistent au changement. On y verra la séparation des responsabilités, le couplage et la cohésion, la lutte contre la duplication, la notion de dette technique — et le refactoring, cette amélioration à comportement constant dont l'exercice de ce chapitre vient de donner un premier exemple.

À retenir

Flashcards · 4 cartes

Pourquoi la lisibilité du code est-elle un enjeu majeur, et quel en est le geste le plus important ?
Parce qu'un code se LIT bien plus qu'il ne s'écrit : la moitié de la maintenance (le coût dominant) consiste à comprendre du code existant. Le geste le plus important est le NOMMAGE : un nom parlant (prixAnnuel, estEtudiant) dit l'intention et épargne au lecteur de la reconstituer, là où f, a, x l'obligent à tout relire. Le temps passé à bien nommer est rendu au centuple à la relecture.
Qu'est-ce qu'un nombre magique, et pourquoi le nommer en constante ?
Une valeur posée en dur sans explication (prix * 0.85). Problème double : elle ne DIT rien (0.85 = quoi ?), et si la règle change il faut retrouver toutes ses occurrences. Une constante nommée (REMISE_ETUDIANT = 0.85) explique la valeur et centralise la modification en un seul endroit. Idem pour les conventions (nommage, indentation) : peu importe laquelle, pourvu qu'elle soit unique et respectée par tous.
Quelle est la différence entre un commentaire utile et un commentaire bruyant ?
Le code dit COMMENT, le commentaire dit POURQUOI. Un commentaire BRUYANT répète le code (« incrémente i » sur i = i+1) : il n'ajoute rien et devient mensonger si le code change. Un commentaire UTILE explique ce que le code ne peut pas dire : une raison, une règle métier, un piège (« TVA arrondie à l'inférieur car l'administration l'exige »). Le meilleur commentaire est souvent celui qu'un bon nom a rendu inutile.
Pourquoi découper en fonctions courtes, et à quoi sert la lecture croisée ?
Une fonction doit faire UNE chose et tenir sous les yeux : le corps de la fonction principale devient alors une suite d'appels qui se lit comme un résumé, sans lire le détail. La LECTURE CROISÉE entre binômes est le meilleur test de lisibilité : si le coéquipier ne comprend pas, c'est le code qu'on corrige, pas le lecteur — et la revue attrape des bugs et diffuse la connaissance dans l'équipe.