Accueil / Articles

JavaScript lisible à travers des exemples : Dix refacturations avant et après

Dix petites réorganisations de code en JavaScript et React, allant de la nomenclature et des objets de paramètres à la gestion des erreurs et au formatage, qui facilitent la lecture et la modification du code par le développeur suivant.

Éditeur : REACTAPP.TOP7 min de lecture1583 mots

Sources: 10 Clean Code Principles I Wish I Knew When I Started Coding

Faire fonctionner une fonctionnalité constitue la première moitié du travail. La seconde moitié consiste à s’assurer qu’un collègue, ou vous-même dans six mois, puisse lire ce code, le modifier en toute sécurité sans le mal interpréter. Cet article présente dix exemples concrets de refacturations avant et après en JavaScript et React, chacun accompagné des raisons qui les justifient ainsi que du point où les conseils cessent d’être utiles.

1. Donnez aux valeurs des noms en fonction de ce qu’elles représentent

Des noms courts et obscurs obligent le lecteur à chercher ailleurs des informations. Dans cet extrait, rien ne vous indique ce que représente d ou quelle propriété contient a.

const d = 30;
const x = users.filter(u => u.a);

En renommant les deux, l’intention est devenue visible là où elles sont utilisées. Le nombre désigne maintenant la durée d’un essai :

const trialPeriodDays = 30;

Et le filtre se lit désormais comme une phrase décrivant les utilisateurs actifs :

const activeUsers = users.filter(user => user.isActive);

Un bon nom indique ce que représente la valeur ; des noms génériques tels que data ne disent presque rien.

const data = getData();

Un nom qui exprime le concept du domaine est bien plus informatif :

const customerOrders = getCustomerOrders();

Si vous ressentez le besoin d’ajouter un commentaire pour expliquer une variable, c’est généralement un signe que le nom devrait plutôt contenir cette information.

2. Donner à chaque fonction une seule tâche

Une fonction qui valide les entrées, reformate les données, écrit dans une base de données, envoie des e-mails et enregistre les activités est difficile à suivre et encore plus difficile à tester, car chaque test doit gérer toutes ces tâches en même temps. Le schéma ci-dessous montre une telle fonction avec chaque responsabilité intégrée.

function createUser(user) {
  // validate user
  // format data
  // save to database
  // send email
  // log activity
}

En la divisant en étapes nommées, on transforme la fonction de niveau supérieur en un résumé lisible. Elle commence par la validation :

function createUser(user) {
  validateUser(user);

Et cela se poursuit avec le formatage, l’enregistrement et l’e-mail de bienvenue, chacun étant confié à sa propre fonction :

  const formattedUser = formatUser(user);  saveUser(formattedUser);  sendWelcomeEmail(formattedUser);
}

Désormais, createUser() ressemble à une liste d’étapes, et chaque fonction auxiliaire peut être testée de manière isolée. Le compromis réside dans l’indirection : celui qui a besoin des détails doit se référer à chaque fonction auxiliaire, ce qui exige que leurs noms soient suffisamment précis pour que cela ne soit presque jamais nécessaire.

3. Éliminer les doublons ayant la même raison de modification

Une logique répétée est un candidat à l’extraction. Ici, la même formule fiscale apparaît trois fois.

const total1 = price1 + price1 * 0.15;
const total2 = price2 + price2 * 0.15;
const total3 = price3 + price3 * 0.15;

En la mettant dans une fonction, on donne à cette règle un nom et un emplacement unique :

function calculatePriceWithTax(price) {
  return price + price * 0.15;
}

Chaque point d’appel n’a alors plus qu’à appliquer cette règle :

const total1 = calculatePriceWithTax(price1);
const total2 = calculatePriceWithTax(price2);
const total3 = calculatePriceWithTax(price3);

Si le taux passe de 15 % à 18 %, il suffit de modifier une seule ligne au lieu de fouiller dans tout le projet. L’important est d’éliminer les doublons réellement significatifs, et non chaque paire de lignes qui se ressemble par hasard. Deux morceaux de code identiques aujourd’hui mais qui évolueront pour des raisons différentes sont mieux gardés séparés ; les fusionner lie des éléments qui devraient évoluer indépendamment. L’article sur quand le code partagé doit exister explore en profondeur cet équilibre.

4. Remplacer les longues listes de paramètres positionnels par un objet

Les arguments positionnels cachent leur signification au point d’appel. En regardant cet appel, on ne peut pas savoir ce que signifient 25 ou true sans ouvrir la définition de la fonction.

createUser(
  "Shayan",
  "shayan@example.com",
  25,
  "developer",
  true,
  "Pakistan"
);

Le fait de passer un seul objet doté de propriétés nommées rend chaque valeur auto-descriptive :

createUser({
  name: "Shayan",
  email: "shayan@example.com",
  age: 25,
  role: "developer",
  isActive: true,
  country: "Pakistan"
});

Ce style est déjà la norme dans les payloads d’API, les objets de configuration et les props React, ce qui le rend naturel dans les applications plus complexes.

5. Préférer un code évident à un code astucieux

Compresser la logique en une seule ligne peut rendre sa lecture plus difficile. La ligne ci-dessous enchaîne des appels optionnels via des noms de propriétés abrégés.

const result = users?.filter(u => u.a)?.map(u => u.n);

Le diviser en deux étapes avec des noms descriptifs rend chaque transformation explicite. Tout d’abord, sélectionner les utilisateurs actifs :

const activeUsers = users.filter(user => user.isActive);

Puis, extraire leurs noms :

const activeUserNames = activeUsers.map(user => user.name);

Cela coûte une ligne supplémentaire, mais un lecteur peut le comprendre d’un coup d’œil. Notez une différence de comportement : la version originale utilisait la chaînage optionnel, ce qui lui permettait de gérer le cas où users était null ou undefined, tandis que la version réécrite provoque une erreur dans ce cas. Si users peut légitimement être absent, gérez-le explicitement (par exemple avec une valeur par défaut de []) plutôt que de le cacher derrière une chaîne de ?.. Le principe directeur est de rendre le code clair, et non impressionnant.

6. Prévoir les échecs, pas seulement le scénario idéal

Les systèmes réels échouent : les API retournent des erreurs, les utilisateurs soumettent des données invalides, les réseaux se coupent, les bases de données deviennent hors ligne. Le code suivant suppose que tout se passe bien.

const response = await fetch("/api/users");
const users = await response.json();

Un détail important est que fetch ne rejette les requêtes que en cas de défaillance réseau ; un code HTTP 404 ou 500 est toujours géré correctement. La version améliorée enveloppe la requête dans un try:

try {
  const response = await fetch("/api/users");

puis vérifie response.ok, lance une exception en cas d’état d’erreur, analyse le corps de la réponse et capture tout problème survenu :

  if (!response.ok) {
    throw new Error("Failed to fetch users");
  }  const users = await response.json();  return users;
} catch (error) {
  console.error("Unable to load users:", error);
}

Cela rend les échecs visibles et bien plus faciles à déboguer. Notez que le bloc catch enregistre l’erreur puis renvoie implicitement undefined, de sorte que les appels ne peuvent pas distinguer une erreur d’un résultat vide. Dans du code réel, il faut soit relancer l’exception après enregistrement, soit renvoyer une valeur explicite que les appels peuvent vérifier, afin que l’erreur soit traitée là où elle peut réellement être gérée.

7. Commenter le pourquoi, pas le quoi

Les commentaires sont utiles ; ceux qui sont redondants ne servent à rien. Ce commentaire réexplique exactement ce que fait cette ligne.

// Increment counter by 1
counter++;

// Delay the request to avoid triggering the API rate limit.
setTimeout(fetchUsers, 1000);

8. Éloignez les règles métier du JSX

Dans les composants React, il est facile d’intégrer directement des filtres et des calculs dans le markup. Ce résultat de rendu applique deux filtres en ligne avant de mapper sur les produits.

<div>
  {products
    .filter(product => product.stock > 0)
    .filter(product => product.price < 100)
    .map(product => (
      <ProductCard product={product} />
    ))}
</div>

const availableProducts = products.filter(
  product => product.stock > 0 && product.price < 100
);

<div>
  {availableProducts.map(product => (
    <ProductCard key={product.id} product={product} />
  ))}
</div>

Remarquez que la version refactorisée ajoute également une propriété key, absente dans la version originale et nécessaire à React pour suivre correctement les éléments de liste.

9. Laissez les outils imposer un formatage cohérent

Le formatage semble anodin tant que la base de code ne mélange pas les styles. Ici, la même variable est déclarée de trois manières par trois personnes : en camelCase,

const userName = "Shayan";

en snake_case,

const user_name = "Shayan";

et en PascalCase sans espaces ni point-virgule :

const UserName="Shayan"

Toutes les trois versions fonctionnent, mais cette incohérence crée des difficultés chaque fois que quelqu’un lit ou cherche dans le code. Des outils tels que Prettier et ESLint éliminent ce problème : Prettier formatte automatiquement, tandis qu’ESLint peut imposer des conventions de nommage et signaler les erreurs fréquentes.

10. Écrivez pour celui qui prendra en charge le code ensuite

Ce principe relie tous les autres. La prochaine personne à ouvrir votre fichier pourrait être un nouveau collègue, un coéquipier ou votre futur moi-même, et votre futur moi a oublié tout ce à quoi vous pensiez aujourd’hui.

des habitudes qui facilitent la modification du code par les développeurs suivants.

En résumé

  • Les noms doivent indiquer ce que représente une valeur.
  • Les fonctions doivent effectuer une seule tâche, et la duplication ne doit être éliminée que lorsqu’il existe une raison commune de modification.
  • Les objets sont préférables aux longues listes de paramètres positionnels.
  • La lisibilité prime sur la concision, mais gardez le comportement identique lors de la réécriture.
  • Gérez explicitement les échecs, et commentez les décisions plutôt que la mécanique.
  • Éloignez les règles métier du markup, et laissez les outils gérer le formatage.