Arrêtons d’écrire du code nauséabond

On a tous un jour bossé sur du code mal écrit, tellement mal écrit que nos yeux se sont subitement mis à crier avant de verser des larmes de sang. Pourtant, en respectant quelques règles de bases on peut éviter d’arriver à ce genre de situation.

Je ne vais pas t’expliquer (oui je te tutoie aujourd’hui) les raisons qui poussent un développeur à coder avec le cul, mais plutôt t’apprendre à poser ton derrière sur une chaise et à utiliser ta tête (enlève ta tête du clavier…) en appliquant quelques bonnes pratiques.

Pourquoi écrire du code propre ?

OK on est mal barré. Tu te poses aussi la question de pourquoi tu prends une douche ? Oui ? La qualité de ton code c’est le dernier de tes soucis dans ce cas-là.

Je vais quand même répondre rapidement même si c’est évident. Écrire du code propre va permettre d’éviter une dette technique qui risque de mettre en péril le projet sur lequel tu travailles ou même pire ta boîte.

Cela va permettre également une meilleure maintenabilité et évolution de ton application, mais tu auras également plus de plaisir à bosser dessus plutôt qu’à passer ton temps à nettoyer la 💩. Accessoirement ça évitera à tes collègues de te ligoter à ta chaise et de te jeter dans les escaliers.

Comment écrire du code propre ?

Il existe certaines règles permettant d’écrire du code robuste et lisible, des règles parfois extrêmement logiques, des règles qui sont censées être connues de tous et pourtant d’irréductibles développeurs résistent encore et toujours à les appliquer.

Analyse ton problème

Écrire du code propre, ça commence bien avant l’écriture de la toute première ligne. Éloigne-toi de ton ordinateur, et va chercher une feuille et un crayon. Pose-toi dans un endroit calme et analyse le problème à résoudre. Fais des schémas, réfléchis aux données dont tu as besoin, aux interactions avec les autres parties du système, aux algorithmes que tu vas pouvoir utiliser. Tu peux même appliquer des méthodes de modélisations et d’analyses (MERISE, UML). Bref, fais ce que tu veux du moment que ça t’aide à comprendre le problème à résoudre avant de te plonger tête baissée dans ton IDE favori.

Cette étape d’analyse est essentielle, elle va te donner une bonne vision de la solution à ton problème. On travaille déjà assez sous pression donc inutile de s’en rajouter en découvrant quelques jours avant la livraison que notre solution est merdique. Bref, je ne vais pas parler pendant des heures là-dessus, il y a d’autres articles beaucoup plus intéressants à ce sujet sur les internets.

T’écris des énigmes ou du code ?

S’il y a bien un truc qui me rend fou c’est de tomber sur du code incompréhensible qui me donne l’impression de résoudre une énigme avec comme seule récompense une bonne migraine.

Nomme correctement tes variables

Le code doit être bien organisé et limpide. Tout cela passe en premier par un bon nommage de tes variables.

Voyons le code suivant :

Laisse-moi quelques secondes, je vais tenter de deviner à quoi correspond cette variable.

Non, franchement je ne vois pas. Je suis obligé d’aller voir plus loin dans le code à quoi correspond la variable d :

OK donc la variable d correspond au nombre de millisecondes avant l’expiration du token. Bien sûr c’était évident !

Pourquoi ne pas correctement nommer la variable d ?

C’est mieux, mais expiresIn est exprimé en jours ? En secondes ? En millisecondes ?

Renommons donc la variable :

Quoi ? Le nom de ma variable est trop long ? Il ne faut pas avoir peur d’utiliser des noms explicites même si ceux-ci peuvent paraître longs, le code y gagne en compréhension.

Et n’oublie pas le nom de tes fonctions !

Le nommage des variables est important, mais celui des fonctions l’est tout autant. Voyons cela avec le code suivant :

On devine que la fonction permet de renvoyer un nombre aléatoire compris entre a et b, mais on est obligé d’aller voir le code de celle-ci pour le comprendre.

Rendons le nom de la fonction un peu plus explicite :

Tu penses que c’est bon là ? T’es sûr ? Le nom de la fonction, bien que plus explicite, peut induire en erreur. Pourquoi ? Tout simplement, car celle-ci ne renvoie que des nombres entiers. Pourquoi ne pas mettre directement cette information dans le nom de la fonction ? 

Cette fois-ci on ne risque pas d’induire en erreur les utilisateurs de notre fonction. Tout ce qu’on doit savoir à propos d’elle est compris dans le nom.

Tu veux d’autres conseils ?

Le nommage des variables et des fonctions n’est pas chose aisé. Voici néanmoins quelques conseils en plus de ceux vus précédemment :

  • N’utilise pas d’abréviation : si tu dois nommer une variable, utilise le mot complet. Une abréviation peut induire en erreur ou ne pas être assez explicite :
  • Pour les variables booléennes, préfixe celles-ci par is, can ou has :
  • Éviter les noms trop proches :
  • Utilise des noms prononçables pour tes variables ou tes fonctions, tu ne passeras pas pour un idiot quand tu devras le prononcer devant tes collègues :

Arrête de polluer ton code avec des commentaires inutiles

 

Ah les commentaires c’est un grand débat chez les développeurs. Faut-il en écrire ou au contraire les fuir comme la peste ? Comme dans chaque débat la bonne réponse est « ça dépend ». 

Don’t comment your code, rewrite it !

Généralement, lorsque l’on écrit un commentaire pour expliquer ce que notre code fait c’est que celui-ci est sûrement mauvais. Par exemple :

OK donc si la propriété status de mon objet user vaut 1 c’est que l’utilisateur est connecté… Ce n’est pas très explicite, d’où la présence du commentaire.

Tu vois comment améliorer ça ? Allez je t’aide, pourquoi ne pas créer une méthode isAuthenticated directement dans l’objet user :

Le commentaire est maintenant totalement inutile, le code parle de lui-même. En plus de ça, on a encapsulé la logique directement dans l’objet user en cas de changement du code de status nous n’avons pas à répercuter les modifications dans le reste du code. 

Merci captain obvious

Je pense que le code qui suit parle de lui-même :

Combien de fois j’ai pu voir ce genre de commentaire… faut-il vraiment que j’explique ce qui ne va pas ? Vraiment ? 

Et dans cet exemple tu vois ce qui ne va pas quand même ?

Le code se suffit à lui-même, les commentaires n’apportent aucune information supplémentaire.

It’s a trap !

Lorsque l’on modifie du code, on oublie souvent de mettre à jour les commentaires associés. Laisser ces commentaires obsolètes peut être dangereux et induire en erreur ton toi du futur ou ceux qui vont passer derrière toi. Pense donc à les mettre à jour ou à les supprimer.

Il est possible que dans une version précédente la fonction renvoyait la valeur null si l’utilisateur n’existait pas, mais ce n’est plus le cas maintenant. Le commentaire donne de mauvaises informations pouvant induire en erreur l’utilisateur de la fonction.

Le code fantôme

On est tous fiers du code que l’on écrit, mais il arrive que celui-ci devienne obsolète, car il ne répond plus au besoin. Alors je sais que c’est difficile que t’as vécu de bons moments avec ce code, mais pourquoi tu le mets en commentaire au lieu de le supprimer ?

Dans cet exemple, les commentaires polluent et rendent le code illisible. Tu as sûrement du versionner ton code, donc s’il te manque tu pourras toujours aller lui rendre visite et lui dire à quel point tu l’aimes dans ton outil de versionning favoris. Donc je t’en prie, supprime-le. 

Mais du coup ça existe des commentaires autorisés ?

Comme je l’ai dit, le code doit se suffire à lui-même, les commentaires sont dans la grande majorité des cas inutiles. Mais il y a tout de même des exceptions.

Documente ton code

Les commentaires permettant de générer la documentation de votre code sont bien entendu utiles (jsDoc, phpDoc, etc.). Ces commentaires permettent aussi d’améliorer l’autocomplétion de code de vos IDE.

Attention par contre à bien mettre à jour ceux-ci, si jamais vous êtes amené à modifier votre code.

Avertis ton toi du futur

Il est parfois nécessaire d’avertir ton toi du futur ou tes collègues sur l’importance d’un point qui, en l’absence d’un commentaire, pourrait paraître sans importance.

Je vais te donner un exemple concret. Il y a peu de temps, je bossais sur l’implémentation du protocole Oauth2 sur un projet Node.js, comme souvent je ne réinvente pas la roue et je tombe sur la librairie OAuth2orize. Super, sauf que celle-ci ne prend pas en compte une extension du protocole (PKCE), j’avais deux choix : forker le projet et implémenter ma fonctionnalité ou appliquer la technique du monkey-patching qui consiste à modifier le comportement du code source original sans toucher à celui-ci.

J’ai opté pour la seconde solution, car c’était la plus simple et la plus rapide à mettre en œuvre (et surtout parce que je n’avais pas le choix pour ce projet…).

Bref, j’ai donc créé ce patch et j’ai mis ce commentaire :

Ce commentaire est extrêmement utile puisqu’il informe sur le choix du nom de la fonction qui peut paraître anodin mais qui est essentiel au bon fonctionnement du programme puisque dans cet exemple la librairie OAuth2orize se base sur ce nom de fonction pour lancer le traitement adéquat.

TODO or not TODO

Bon, là c’est pareil c’est un grand débat, doit-on mettre des commentaires TODO dans notre code ou pas ?

Le problème des commentaires TODO c’est que certains ne sont pas du tout justifiés. On les écrit par flemme et on se dit qu’on fera ça plus tard, mais tu le sais très bien tout autant que moi, que plus tard veut dire jamais. Donc quand tu peux faire ce que tu comptes écrire dans ton commentaire TODO, tu le fais tout de suite.

Par contre, il arrive que certains d’entre eux soient justifiés, notamment le cas de tâches ne pouvant pas être réalisé actuellement pour x raisons :

Dans la plupart des cas, les commentaires TODO te sont utiles uniquement quand tu bosses en local, pour te rappeler des tâches à faire avant de commit. Et si comme moi il t’arrive de les oublier, tu peux te créer un hook git qui t’insulte lorsque tu commit ton code. 

Comprends ton code

J’espère que tu comprends chaque ligne de code que tu écris et surtout pourquoi tu les as écrits. C’est primordial de comprendre son code. Si tu ne le comprends pas, c’est que soit ta solution est trop complexe ou soit que tu as copié-collé du code bêtement.

Je ne te dis pas que tu ne peux pas copier-coller du code, mais généralement ce code n’est pas adapté à 100 % à ta solution ou le nom des variables utilisé dans le code, n’a rien à voir avec le contexte de ton application. Bref, pour faire simple si t’es amené à copier-coller du code comprend celui-ci et adapte le à ton besoin ou tu risques de devenir ce que j’appelle un développeur « patchwork »

Ça peut te paraître bête ce que je viens de te dire, mais combien de développeurs sont incapables d’expliquer leur code ?

Ah oui et quand tu utilises une librairie commence par lire la documentation pour comprendre comment elle s’utilise avant de te lancer dans de la magie noire.

Applique les bonnes pratiques

D’autres développeurs ont été confrontés avant toi aux problèmes que tu rencontres et ce n’est pas pour rien qu’il existe de bonnes pratiques. Parmi celles-ci je peux te citer SOLID, DRY, KISS, ou les design patterns. Je ne vais pas t’expliquer en détail toutes ces bonnes pratiques, d’autres l’ont fait avant moi et tu trouveras ton bonheur sur les internets.

Je vais juste te prendre quelques exemples que je rencontre assez régulièrement et qui posent problème.

À vouloir trop en faire…

Je vais te donner un exemple et tu vas essayer de voir ce qui ne va pas :

À première vue rien ne cloche. Mais si je te dis que je veux pouvoir formater une instance de Post en JSON et en XML. Tu me dis pas de soucis et tu ajoutes les méthodes dans la classe Post comme ceci : 

Génial ! Et si je te dis maintenant je veux le format CSV… tu vois d’où vient le problème ? La classe Post a trop de responsabilités, elle s’occupe à la fois de représenter un article et de gérer son formatage. À chaque nouvelle demande, tu es obligé de modifier ta classe et ça, c’est le mal car tu peux introduire des bugs ou rendre ta classe illisible. La bonne solution serait de séparer le formatage de la classe en créant une autre classe ou une fonction :

De cette façon, nous n’avons plus à modifier notre classe Post la logique de formatage est externe à celle-ci.

C’est pareil pour tes fonctions, celles-ci doivent généralement faire qu’une seule chose. Si ce n’est pas le cas, découpe-les en plusieurs petites fonctions.

Il y a plusieurs avantages à découper ses fonctions en plusieurs petites fonctions :

  • Diminution de la complexité du code ;
  • Améliore la compréhension du code;
  • Facilite la maintenance;
  • Facilite l’écriture des tests;
  • Facilite la réutilisabilité.

Si tu veux un bon exercice, tu peux aller voir un middleware Express que j’ai écrit et qui permet d’authentifier les requêtes via une clé d’API. Jette un coup d’œil sur le fichier parser.js. qui exporte une fonction permettant d’extraire la signature de la requête qui se trouve dans un en-tête HTTP.

Si tu as bien suivi tout ce que j’ai dit jusque-là tu remarqueras qu’il y a plusieurs soucis dans ce fichier :

  • Certains commentaires sont totalement inutiles;
  • La fonction est trop longue et fait beaucoup trop de choses. Certaines parties de code n’ont rien à faire dans une fonction censée uniquement parser un en-tête HTTP;
  • Certaines variables pourraient être mieux nommées.

Quoi ? Arrête de me juger, je n’ai jamais dit que j’étais parfait… Si tu veux, tu peux t’amuser à nettoyer ce code et m’en faire part par email ou même faire une pull request. Et je te vois venir ! Non je ne te fais pas travailler à ma place.

Injecte tes dépendances

Je t’ai déjà expliqué dans un autre article l’avantage de l’injection de dépendances, je ne vais donc pas me répéter ici. Mais dès que tu identifies un comportement qui est amené à changer de manière dynamique ou plus tard lors d’évolutions apportées à ton application, n’hésites surtout pas à utiliser l’injection de dépendances. Ça permettra de découpler ton code et ça c’est cool !

Masque la complexité des librairies tierces

Je vais te donner un conseil, mais je sais que beaucoup de personnes ne sont pas forcément d’accord avec celui-ci.

Personnellement, j’ai pris l’habitude pour les gros projets sur lesquels je suis amené à travailler, de masquer le fait que j’utilise des librairies tierces.

Je m’explique, en te donnant un exemple très simple. J’ai développé il y a peu un service qui communique avec d’autres services via une file de messages. Le souci, c’est qu’au début du développement, on n’avait pas encore statué sur la file de messages qu’on allait utiliser.

Moi de mon côté je n’en avais rien à faire de savoir quelle file de messages on allait utiliser. Tout ce dont j’avais besoin c’était de pouvoir envoyer un message dans celle-ci.

Donc je n’ai pas attendu, et j’ai créé la classe suivante :

Si t’as l’habitude de faire de la programmation orientée objet, tu dois tout de suite te dire que je suis en train de définir un contrat et que la classe MessageQueue doit avoir une méthode sendMessage et que dans l’idéal je devrais utiliser une interface. Et tu as vu juste, mais comme tu le sais Javascript ne possède pas d’interface, mais tu peux toujours magouiller pour en simuler une.

Alors pourquoi je fais ça ? Parce que comme je l’ai dit, je m’en fous dans le reste de mon code de savoir que j’utilise Kafka ou RabbitMQ. Cette complexité est encapsulée dans ma classe MessageQueue et si à l’avenir on décide de changer de technologie, j’aurais juste à implémenter ma classe MessageQueue que j’injecterais là où j’en ai besoin.

Je fais également ça, car il arrive parfois lors de la mise à jour des librairies tierces que certains changements majeurs rendent le code existant non fonctionnel ce qui implique de devoir effectuer des mises à jour partout où ces librairies sont utilisées. En les encapsulant, j’ai la maîtrise sur ses changements qui n’impacteront que le code qui encapsule ses librairies.

Respecte des conventions de nommages

Il est important, afin d’uniformiser tes développements, que tu respectes des conventions de codage.

Il existe, en Javascript, plusieurs conventions de codage parmi les plus connues :

Libre à toi d’utiliser la convention que tu souhaites, tu peux même si ça te fait plaisir d’utiliser ta propre convention. L’important c’est de garder une cohérence dans l’ensemble de ton projet.

Pour t’assurer du respect de ces conventions, tu as des outils comme ESlint qui s’intègrent parfaitement dans la plupart des IDE du marché et qui sont très simples à mettre en place.

Pour finir…

Je t’ai donné quelques conseils pour rendre ton code plus propre. Il s’agit de conseils de base, tu peux ne pas être d’accord avec tout ce que j’ai dit. Les règles sont faites pour être transgressées, mais dans ce cas-là il faut vraiment que tu aies une bonne raison et surtout que tu saches pourquoi tu les transgresses.

Je pourrais te donner plein d’autres conseils, mais je vais plutôt t’orienter vers deux ressources qui vont changer la vision que t’as de ton code. Le premier, c’est un bouquin ou plutôt LE bouquin que tu dois absolument lire au moins une fois. Ce bouquin c’est « Clean code« , tu trouveras tout ce que j’ai dit dans cet article de manière beaucoup plus approfondie. Les exemples sont en Java, mais tu peux appliquer les conseils pour n’importe quel langage.

Quoi ? Tout le monde conseille ce bouquin et tu veux autre chose ? Ok allez je suis sympa. Je te conseille le site « Refactoring guru« . C’est une vraie mine d’or ce site. Tu vas y trouver plein de conseils pour comprendre ce qui cloche dans ton code et comment t’y prendre pour le nettoyer. Et en bonus tu as même une partie dédiée aux designs patterns.

J’aurais pu également te parler des tests ou encore de la gestion des erreurs, mais je ferais sûrement des articles dédiés.

Je vais terminer sur une phrase que tu devras te répéter à chaque fois que tu commences à bosser : « Laisse le code plus propre que tu ne l’as trouvé »


Annonces partenaire

Je suis lead developer dans une boîte spécialisée dans l'univers du streaming/gaming, et en parallèle, je m'éclate en tant que freelance. Passionné par l'écosystème JavaScript, je suis un inconditionnel de Node.js depuis 2011. J'adore échanger sur les nouvelles tendances et partager mon expérience avec les autres développeurs. Si vous avez envie de papoter, n'hésitez pas à me retrouver sur Twitter, m'envoyer un petit email ou même laisser un commentaire.

1 commentaire

  1. Comme bonne pratique, je conseille la lecture de l’article https://dave.cheney.net/practical-go/presentations/qcon-china.html. C’est écrit pour le langage Go, mais la majorité des conseils sont applicables pour d’autres langages.
    Notamment sur la question des commentaires : « Good code is its own best documentation. As you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?’ Improve the code and then document it to make it even clearer.  »
    Cela est souvent (mal) interprété comme disant qu’un code clair/évident n’a pas besoin de commentaire. J’ai même entendu une personne me dire que si un autre développeur n’était pas capable de lire son code (sans commentaire), c’est qu’il n’était pas digne de travailler dessus. Le conseil (auquel j’adhère) est en réalité qu’il est préférable de réécrire un code qui serait incompréhensible s’il n’était pas accompagné par un commentaire, mais _ni_de_supprimer_ _ni_de_refuser_d’ajouter_ un commentaire une fois le code suffisamment compréhensible. Comme je le dis à mes développeurs, le code est à destination de la machine qui va l’interpréter ; le commentaire est à destination de l’humain pour indiquer vos intentions. Penser que le code retranscrit exactement ce que vous vouliez faire sous-entend que vous n’écrivez jamais de bug et qu’une revue de code est inutile …
    On n’écrit pas un commentaire n’importe comment non plus … je te laisse le soin de découvrir tout cela dans l’article.
    Bonne continuation et bravo pour les articles !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.