Code de qualité, comment bien coder ?

  Temps de lecture : 10 min.
Créer du code de qualité grâce aux principes du clean code. Bien coder et avoir un code propre en respectant les bonnes pratiques.

La plupart des développeurs n’écrivent pas du code très qualitatif, ce n’est un secret pour personne.

Par manque de formation ou « à cause » d’un budget serré, on se retrouve avec une dette technique incroyable 6 mois après le début du projet.

En tant que développeur, on se doit d’écrire du code de qualité.

C’est pour cela que j’ai décidé de rassembler dans cet article les principales erreurs de développement que je vois en code review.

PS : Ce n’est pas un énième article sur le clean code même si de fait, j’en parle également. C’est surtout un recueil de mon expérience et des mauvaises pratiques que j’ai pu voir en tant que lead developer.

Les caractéristiques d’un bon code

Il y a des caractéristiques communes à tout code de qualité.

  • Le code est expressif, ses actions sont claires, tout le monde sait ce qu’il fait
  • Cela semble évident de comprendre comment il fonctionne
  • Il est élégant, on peut dire qu’un bon code est un « beau code »

Nommage des classes et des fonctions

Le nommage est très important, bien que ce soit un exercice très difficile de trouver le bon nom à donner à une action.

  • Il faut essayer d’aller à l’essentiel lorsque que l’on nomme ses variables
  • Exprimer son type et son intérêt au premier coup d’œil
  • Ne pas avoir peur de faire des noms un peu plus long (HTTP_KO => HTTP_INTERNAL_SERVER_ERROR)
  • N’utilise surtout pas d’acronyme métier (isMOS => isMasterOfService)
  • Évite le « franglais » (shouldBeLisible => shouldBeReadable)
  • Se mettre d’accord sur la traduction pour éviter les synonymes qui disent la même chose (getAllUsers(), fetchAllUsers(), retrieveAllUsers())
  • Utilise des noms métiers dans le code
  • Les mots simples sont à privilégier
  • Code en anglais si possible, les commentaires peuvent être en français si le projet est uniquement français
  • Utiliser les conventions de nommage du projet en cours, même si elles ne sont pas terribles
  • Les variables d’une lettre ne doivent jamais exister (sauf pour les boucles ou les projections)
  • Nomme les entités de la même manière en Front et en Back (évite d’avoir Employee en front et User en back par exemple, ça perd tout le monde)

Tu peux mesurer la qualité d’un code à sa facilité de compréhension et notamment grâce au nommage..

Type ton code

Le typehint des variables et des fonctions est super important pour la compréhension, la lisibilité générale et l’autocomplétion des IDEs.

Javascript et PHP n’ont pas souhaité typer leur code au début de leur histoire. PHP l’a désormais intégré et Typescript est apparu.

  • Typehint ton code dès que c’est possible
  • Pas besoin de typer ce qui est déjà logique /** @var array */ $users = array();
  • Utilise des énumérations pour typer les entrées / sorties des objets (return 1; => return User.NO_MORE_USER)
  • Sers-toi des interfaces quand il y en a besoin, cela évite de dupliquer le code et d’avoir des if / else à rallonge

Le typage n’est pas forcément synonyme de qualité du code, néanmoins cela contribue grandement à limiter le risque d’erreur et à rendre le code intelligible.

Constantes et fichiers de configuration

Tu peux stocker des éléments constants de deux manières différentes.

  • Utilise des constantes quand tu souhaites utiliser l’élément partout dans ton code (Response.INTERNAL_SERVER_ERROR)
  • Utilise un fichier de configuration quand l’action dans ton code va dépendre d’une configuration (webservices.name_of_company.users = 'https://...';)
  • Le fichier de configuration permet également de changer les informations lors d’un changement d’environnement
  • Une constante est globale et immutable pour tout le projet

Évite les trucs trop fancys

La programmation fonctionnelle a permis de récupérer les sorties avec des chaînes, ce qui est très pratique !

Mais si tu en abuses, les développeurs passant après toi ne comprendront pas ton code.

return data.map((d) => [Object.keys(d)[0], Object.values(d)[0]].map(encodeURIComponent).join('=')).join('&');

  • Évite des chaîner les fonctions à outrance
  • Attention à la performance des opérations de chainage (for ... of ... est bien plus rapide que .forEach())
  • Ne pas utiliser le simple égal dans un if ($user = getAllUser()) ... , car confus et ça peut être mal interprété
  • Toujours suivre les coding standards des technos, cela signifie utiliser la Yoda Condition s’il le faut !
  • Globalement ne pas essayer de faire des trucs complexes, même si on adore ça

Gestion des erreurs

Faut-il remonter l’exception ou utiliser un try / catch ?

C’est toujours compliqué à dire suivant les cas d’utilisation. Voici quelques règles pour que tu puisses y voir plus clair.

  • Ne remonte pas d’exception si tu testes quelque chose dans ta fonction (isUserUnEmployed() doit remonter true ou false)
  • Globalement une fonction pose une question, remonte une exception si elle répond mal à cette question
  • Remonte une exception quand une supposition sur les arguments est erronée (presupposition failure)
  • Les exceptions sont par définition exceptionnelles, évite d’en remonter à chaque fonction
  • Utilise des exceptions quand quelque chose de critique survient ou pour notifier d’un évènement qui ne peut pas être ignoré (si la suite de tes actions dépend d’un appel web service et que celui-ci est injoignable, tu peux remonter un WebServiceNotRespondingException)
  • Crée tes propres exceptions… (InvalidArgumentException => UserCannotBeNullException)

Ce qu’il faut retenir des principes de développement

Les connaître c’est bien, les utiliser, c’est mieux.

Pour ma part je les connais depuis longtemps, mais je ne les applique réellement que depuis quelques années.

Il m'aura fallu créer beaucoup de code pas terrible pour enfin appliquer ces principes...

Comme tout chose, il faut du temps.

SRP (Single Responsibility Principle)

Le principe de responsabilité unique est probablement le plus important.

  • Une fonction n’a qu’une seule action, un seul objectif
  • Elle ne sort pas du scope de son nom, elle fait exactement ce qu’elle dit
  • C’est tout

Il y aurait beaucoup à dire, mais simplement essaye de minimiser les actions de tes fonctions.

KIS (Keep It Simple)

Tout ce qui est compliqué n’a jamais produit de bons résultats.

Louis-Napoléon Bonaparte, Les œuvres de Napoléon III (1854-1869)
  • La solution la plus simple est toujours la meilleure
  • Dès que c’est compliqué essaye de découper au maximum
  • Plus c’est petit, plus c’est facile à comprendre
  • Découpe ton code en fonctionnalités (UserService => UserCreatorService, UserBankingService)

DRY (Don’t Repeat Yourself)

Ça a l’air simple à première vue, mais la limite est parfois obscure…

Imaginons que tu aies deux tableaux. Ces tableaux affichent des informations différentes, mais sont très similaire par leur style et leur template.

Tu pourrais te dire, je vais utiliser une interface pour afficher les objets et gérer les petites différences de style avec des conditions.

À un moment donné, tu te retrouveras avec des if (type === Type.User) et des if (type === Type.Group) dans ton code ; pas vrai ?

À partir de combien de chaque il vaudrait mieux faire deux fichiers séparés ?

Globalement tu dois autant que possible :

  • Partager au maximum le code similaire
  • Dès que tu veux copier/coller du code, extrais-le dans une fonction d’un service
  • N’aie pas peur de découper ton code, au contraire tu le rends modulable

YAGNI – You Aren’t Gonna Need It

Moins connu que les autres, mais tellement d’actualité aujourd’hui.

« Tu n’en auras pas besoin ».

À l'heure où les frameworks JS sont utilisés à tout va, on peut supposer que tous les projets n'ont pas besoin de la stack React + Redux + Socket.io + ...

Quel est le besoin de ton client ? Une landing page avec un formulaire tout simple ?

HTML et CSS devraient suffire, PHP si c’est contribuable ou un autre langage back.

L’idée principale c’est de ne pas avoir 20.000 dépendances.

Typiquement on peut prendre l’exemple de jQuery qui n’est je pense plus vraiment utile dans la plupart des cas.

Pourtant je vois tellement de projets encore avec… C’est toujours une des libs JS les plus populaires en 2020.

Et pourtant, on n’en a pas du tout besoin la plupart du temps.

SOC – Separation of Concerns

« Faire une seule chose, mais la faire bien ».

C’est toujours mieux que de tout faire et mal ! Et contrairement à ce qu’on pense nous les devs, tout bien faire est souvent une chimère.

« Do One Thing And Do It Well. »

La philosophie d’Unix.

Sépare tes fonctionnalités en lot. Dans ces lots crée des parties…

Fais des petites choses, mais fais-les bien.

Cela signifie aussi de créer des fonctionnalités testées et séparées.

ACID (Atomicité, Cohérence, Isolation et Durabilité)

Le principe ACID permet de s’assurer qu’une action a bien été réalisée et que son action soit ce que l’on attendait d’elle.

Ce qu’il faut retenir c’est de ne jamais renvoyer OK si on n’est pas certain l’action a belle et bien été réalisée.

  • Atomicité : 1 ou 0, soit la transaction a été complètement réalisée, soit pas du tout.
    Comme exemple, on peut citer la création d’un utilisateur en base de données. Rendre sa création atomique, c’est s’assurer que l’objet ET tous ses champs ont bien été persistés.
  • Cohérence : Aucune action ne doit rendre les données corrompues ou le système inopérant.
    On crée par exemple des contraintes d’intégrités ou des cascades pour ne pas avoir de relations orphelines du côté de la base de données
  • Isolation : Chaque transaction est isolée et ne dépend pas d’une autre.
    Exemple : Tu peux lancer deux requêtes sur une route api POST /api/users, aucune de ces deux requêtes ne doit dépendre de l’autre.
  • Durabilité : Ne pas renvoyer de code retour correct si on n’est pas certain que l’action a bien été menée à son terme.
    Sur une création d’un utilisateur en base de données, on retourne souvent l’utilisateur nouvellement créé avec son ID comme preuve que ce dernier est bien enregistré.

Linters et coding standards

Les linters te permettent d’avoir un warning dans ton IDE quand ton code peut être amélioré.

Il permet notamment :

  • De détecter les erreurs dans le code source
  • De corriger les problèmes de syntaxe
  • Le respect les bonnes pratiques de ton langage
  • La mise en place des coding standards

Mieux encore, la plupart des linters viennent avec un fixer, tu peux corriger ces erreurs simplement avec un clic droit…

Les linters rendront ton code bien plus lisible.

  • Prends le temps d’installer les linters suivants ton projet (eslint, sass-lint, php-coder-sniffer, wpcs, jslint…)
  • Utilise les conventions données par ton framework (les bonnes pratiques WordPress sont différentes de celles de Symfony)
  • Respecte le « coding style » de chaque langage (comme la PSR-12 pour PHP)
  • Apprends les design patterns les plus utilisés (Singleton, DTO, Adapteretc)

Nettoyage du code

En théorie le clean code stipule que tu es censé laisser le code plus propre en partant qu’il l’était en arrivant.

Cependant la complexité peut rendre la refactorisation du code difficile, attention aux régressions.

La qualité globale du code du projet est à prendre en compte lorsque l’on est développeur, pas seulement la qualité de SON code.

  • Supprime déjà le code commenté et autres « console.log » quand tu en vois
  • Fais attention aux boucles imbriquées, ça peut vite tourner au vinaigre
  • N’abuse pas des ternaires $variable == $x ? ($x == $y ? array_merge($x, $y, $z) : $x) : $y;
  • Si ton for a un break, c’est sûrement un while
  • Rend les choses logiques et lisible après ton passage
  • Commente les éléments que tu as eu du mal à comprendre
  • « Quand y’a un doute, y’a pas de doute ». Si jamais tu « penses » que ce code doit être modifié, c’est sûrement qu’il doit l’être

Comment bien commenter son code

C’est toujours un sujet de débat dans la communauté, certains disent que tout doit être commenté, d’autres disent que rien ne doit l’être.

Est-ce que celui-ci doit l’être ?

/**
* Returns a customer based on its id.
* 
* @param int $id
* @return Customer
*/
public function getCustomerById(int $id): Customer;

En tout cas un code propre passe par l’utilisation des commentaires.

Essayons de prendre le meilleur des deux mondes et de rester pragmatique.

  • Tu n’es pas obligé de commenter la déclaration si ce qu’elle dit est évident /** The day of the month. */ private $dayOfMonth;
  • Commente quand ça devient compliqué ou que tu en ressens le besoin, le développeur suivant te remerciera
  • Attention aux TODOs, ils restent à vie dans le code…
  • Exprime tes intentions via tes commentaires (j’ai dû faire ça, parce que ceci…)

Découpage et longueur

Plus une méthode est unique, plus elle est facile à lire, plus elle sera facile à déboguer.

Elle sera même encore plus performante.

  • N’hésite pas à créer beaucoup de fichiers, c’est comme ça que l’on découpe
  • Encore une fois, essaye de faire en sorte qu’un fichier ait un scope bien précis (UserBasketService ne s’occupe que des opérations dans le panier, UserPaymentService s’occupe uniquement paiement sur la plateforme…)
  • UserService ne veut rien dire, personne sait ce qu’il y a de dedans
  • 300 lignes pour un fichier commence à être beaucoup
  • 140 caractères maximum de largeur, y compris pour le HTML (le scroll horizontal nuit à la lisibilité et à la compréhension de ton code)
  • 30 lignes pour une fonction c’est déjà pas mal
  • Jamais plus de 3 niveaux d’indentation par méthode, utilise des fonctions privées et splite ton code
  • À partir de 5 arguments, essaye de découper le traitement dans ta fonction ou passe plutôt un objet

Avoir un code de qualité passe également par la longueur des différents fichiers.

Ces chiffres sont des indications pour tendre vers un code plus lisible, ils ne sont pas absolus.

Conclusion

Faire du code de qualité est quelque chose de difficile, on a tous notre notion de « code qualitatif ».

Pour écrire un code source de qualité, tu dois écrire du code lisible, propre, compréhensible de tous.

J’aurais voulu parler de l’utilisation des tests dans cet article.

« Mais tester, c’est douter. »

En vérité je n’en ai pas assez mis en place pour pouvoir en parler librement.

Néanmoins les tests sont de très bons outils que tu dois essayer d’utiliser pour rendre ton code encore plus sûr.

D’ailleurs en parlant d’outils, coder sur papier pourrait t’aider à mieux coder !

J’espère que cet article t’auras donné des pistes pour mieux programmer.

Si tu cherches à en savoir davantage sur le clean code, consulte cet article.

Si jamais tu vois des éléments à rajouter à cette liste, n’hésite pas à laisser un commentaire.

❤️ Tu as aimé cet article ?️

J'ai mis un moment à l'écrire... Ce serait top si tu pouvais le partager à la communauté !