Code de qualité, comment bien coder ?

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.

PS2 : Je ne parle pas de SOLID dans cet article car j’en ai fait un billet dédié ici : SOLID – Comprendre les 5 principes en 10 minutes.

Faire un lien vers : Les caractéristiques d’un bon codeLes 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 »

Faire un lien vers : Nommage des classes et des fonctionsNommage 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 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..

Faire un lien vers : Type ton codeType 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 abstractions / interfaces quand il y en a besoin, cela évite de dupliquer le code et d’avoir des if / else à rallonge
if vs héritage
Gagner en lisibilité et en qualité de code avec des `extends` ou des `implements`

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.

Faire un lien vers : Constantes et fichiers de configurationConstantes 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

Faire un lien vers : Évite les trucs trop fancysÉ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 de 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

Faire un lien vers : Gestion des erreursGestion 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)

Faire un lien vers : Ce qu’il faut retenir des principes de développementCe 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 toute chose, il faut du temps.

Faire un lien vers : SRP (Single Responsibility Principle)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.

Faire un lien vers : KIS (Keep It Simple)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)

Faire un lien vers : DRY (Don’t Repeat Yourself)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 similaires 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

Faire un lien vers : YAGNI (You Aren’t Gonna Need It)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.

Faire un lien vers : SOC (Separation of Concerns)SOC (Separation of Concerns)

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

C’est toujours mieux que de tout faire et mal ! 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.

Faire un lien vers : ACID (Atomicité, Cohérence, Isolation et Durabilité)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 bel 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é.

Faire un lien vers : Linters et coding standardsLinters 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, Adapter etc)

Faire un lien vers : Nettoyage du codeNettoyage 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 lisibles 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
for vs while
Si ton for a un `break`, c’est que ça devrait plutôt être un while.

Faire un lien vers : Comment bien commenter son codeComment 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. */ primate $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…)

Faire un lien vers : Découpage et longueurDé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.

Faire un lien vers : ConclusionConclusion

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’aura 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.

2 commentaires

  1. Avatar de Maxime

    Bonjour,

    Merci pour cet article fort intéressant.

    Peux tu stp expliquer ce point :
    => « Sers-toi des interfaces quand il y en a besoin, cela évite de dupliquer le code et d’avoir des if / else à rallonge »

    Et donner un exemple pour celui-là :
    => Si ton for a un break , c’est sûrement un while

    Merci d’avance

    • Avatar de Alex

      Hello Maxime,

      Avec un peu de retard, je viens de mettre à jour l’article avec deux captures d’écrans !

      N’hésite pas si tu as d’autres questions.

      Alex

👩‍💻 Réagir à cet article 👨‍💻

Merci d'avoir partagé ton histoire avec nous !