DEV Community

Jean-Noël for Younup

Posted on

Level up sur la qualité du code !

Level up sur la qualité du code

Introduction

Vous êtes-vous déjà dit en regardant votre code "Je peux faire mieux !" ?

C'est normal. On peut toujours mieux faire.

Mais la vraie question est : jusqu'où aller ? Quel niveau de qualité faut-il donc atteindre pour les besoins du projet ?

Développeur manga a une idée

À moins d'être vraiment (super) balèze, il faudra repasser plusieurs fois sur un même code pour atteindre un niveau satisfaisant.

À travers toutes mes expériences et tout un panel de langages, j'ai justement pu identifier cinq niveaux de qualité du code. Et croyez-moi, atteindre chacun de ces niveaux n'est jamais une évidence !

Level 1 : le code est fonctionnel

Est-ce qu'on commence par enfoncer une porte ouverte ? Sans aucun doute. Est-ce que ce niveau est systématiquement atteint ? Hélas, non.

La question n'est pas d'avoir un code qui peut ou ne peut pas s'exécuter, encore que dans certains langages comme le Python ou le Javascript, c'est souvent le grand suspens étant donné que l'exécution n'est pas garantie en amont par un compilateur au même titre qu'avec le Rust.

Je parle de répondre effectivement à la problématique de départ, à la specification fonctionnelle (quand elle a la chance d'exister 😬).

Il est vrai, cela nécessite d'avoir une chaîne de définition et développement de produit impeccable : bien identifier le besoin client, bien le traduire, bien le comprendre, et bien l'implémenter. Mais c'est un niveau hyper important à garder à l'esprit dans tous les choix techniques car il constitue l'unique valeur perceptible depuis l'extérieur du projet.

Pour l'anecdote : dans un de mes projets client d'IHM, l'équipe UX & Produit souhaitait que l'on rajoute un nouvelle stratégie de sauvegarde à travers le parcours utilisateur d'un Wizard, car ils avaient identifié un besoin. Sauf qu'en tant que développeurs nous savions que cette nouvelle stratégie était contradictoire avec les contraintes techniques existantes, et présentait même des failles fonctionnelles. Mais c'était pourtant l'objectif, ce qui était demandé et validé par l'équipe produit, et dans le Sprint actuel.

Alors qu'avons-nous fait ?

Totalement à l'improviste, j'ai proposé au responsable UX un petit brainstorming devant un tableau blanc pour présenter nos points de vue respectifs. Et ça a été la discussion la plus enrichissante et créative qu'on ait jamais eue. Ai-je réussi à démontrer que j'avais raison ? Spoiler alert : on avait tort tous les deux. Nos deux stratégies comportaient chacune des failles et si on en avait suivi une seule, on serait tombés systématiquement dans des comportements indéterminés.
Cela nous a permis de recréer une nouvelle spec juste impeccable.

Alors en amont, n'hésitez pas à poser des questions au Product Owner, à décortiquer les problèmes, à essayer de comprendre le pourquoi du comment. Plus vous serez informé et concerné par l'objectif, mieux vous l'implémenterez.

De même, lors de l'implémentation, vérifiez que le code répond bien au besoin et ce dans les conditions du client. Souvenez-vous que même si votre programme marche sur votre PC, ce n'est pas votre PC qui sera vendu au client.

Level 2 : le code est fiable

D'accord, le code répond maintenant au cas nominal. Mais est-ce vraiment le seul cas que l'on va rencontrer ?

D'expérience je peux d'ores et déjà vous répondre "non".

Déjà car la spec fonctionnelle recense rarement tous les cas d'usage du premier coup. Mais aussi (et surtout) car l'ajout de nouveau code introduit statistiquement de nouveaux bugs et comportements inattendus.

On repense aux fameuses dix lignes de code efficaces produites par jour et par développeur ("The Mythical Man Month" - Brooks). En tant que développeur, il faudra tester et corriger son code un bon nombre de fois afin de gérer correctement tous les cas d'erreurs possibles. Votre code doit être à l'épreuve des balles !

Développeur manga ultra déterminé avec une aura

Tout d'abord il faut identifier les situations qui ne sont pas gérées dans le code : un champ qui pourrait être vide ? Une combinaison de valeurs qui donne lieu à une situation inattendue ? Un serveur qui ne répond plus ? La saturation de l'espace disque ?... Le code doit les gérer.

Pour s'aider, on devra aussi implémenter des tests End-to-End, tests unitaires et d'intégration. Ces phases de tests doivent être vues comme de véritables crash-tests : si vous rencontrez des erreurs, c'est gagné ! L'occasion de corriger une faille ! Souvenez-vous que des tests qui n'échouent jamais n'ont aucun intérêt.

Voir aussi le mutation testing.

Level 3 : le code est générique

Je pourrais écrire une flopée d'articles rien que sur ce niveau, car il regroupe en fait de nombreuses qualités en termes d'architecture ou de scalabilité (même si ce dernier aspect est plutôt propre aux projets web).

En fait l'idée de ce niveau est de pouvoir gérer un maximum de cas d'usage avec un minimum de code.
Il faut être capable de lire entre les lignes de l'énoncé, d'être visionnaire !

Dit autrement, demain on doit pouvoir ajouter facilement de nouvelles fonctionnalités avec moins de code que ce qu'il a fallu au départ à la création de l'architecture, voire sans ajouter de code du tout ✌️.

De nombreuses méthodes existent pour y parvenir, comme par exemple :

  • implémenter une architecture SOLID ;
  • ajouter des niveaux d'abstraction sur les objets ;
  • utiliser des templates ;
  • refactoriser les parties de code similaires en fonctions réutilisables ;
  • découper en petits modules indépendants plutôt que tout coder au même endroit ;
  • ...

Avec un code générique, on balaie déjà la plus grande partie d'une dette technique qui pourrait nous retomber dessus dans un avenir souvent plus proche qu'on ne l'imagine.

Attention : piège, un code trop générique peut vite perdre en lisibilité si on ne fait pas attention ! C'est donc un équilibre à trouver.

Level 4 : le code est performant

Ici on parle de consommation de ressources matérielles et de rapidité d'exécution. Ce niveau peut parfois être facultatif, car les ressources de nos machines sont chaque jour un peu plus fournies, et bien souvent si un programme répond déjà au besoin initial, on ne se soucie des problèmes de performances que lorsqu'ils deviennent vraiment gênants.

Toutefois pour éviter d'être débordé à moyen terme, il vaut mieux rester consciencieux à chaque ligne de code que l'on écrit au fur et à mesure : chaque structure de donnée, chaque boucle, chaque requête réseau a une empreinte plus ou moins importante qu'il faut jauger.

Voici quelques généralités applicables dans une grande partie des langages :

  • évitez les copies inutiles de données en utilisant le système de références du langage ;
  • méfiez-vous des conteneurs à taille dynamique (par exemple, string), si possible, réservez une capacité en amont pour éviter les multiples réallocations et copies en interne à force de redimensionnement ;
  • placez toujours les conditions (if) qui ont le plus de chances de se produire en premier ;
  • si applicable, utilisez des fonctions asynchrones pour les opérations longues en attente afin d'éviter les blocages ;
  • spécifiez des types de variables adaptés au besoin (par exemple, un entier sur 64 bits n'est pas nécessaire pour des nombres allant de 0 à 10 000) ;
  • libérez la mémoire et les verrous (mutex) au plus tôt ;
  • réduisez les boucles de traitement successives sur une même liste de données en les regroupant en une seule ;
  • utilisez le mot-clé const car, dans la plupart des langages, il réduit drastiquement les instructions bas niveau générées derrière (assembleur ou autres) ;
  • regroupez les accès disques et les requêtes réseaux ;
  • ...

Gardez aussi à l'esprit le classement général des goulots d'étranglement en termes de temps d'accès (du plus lent au plus rapide) :

  1. accès réseaux ;
  2. disque dur ;
  3. SSD ;
  4. RAM ;
  5. caches CPU (L1-L2-L3) ;
  6. registres CPU.

Bref, il y a vraiment de quoi faire sur ce sujet, et à défaut d'avoir besoin d'un code performant dans le projet, il faut avoir conscience de ces enjeux pendant la conception de l'architecture et l'écriture du code.

Plus de réflexion sur l'optimisation ici.

Level 5 : le code est... simple

J'en avais déjà parlé dans cet article en citant Léonard de Vinci, et ce n'est pas un hasard si cette qualité arrive en dernier : "La simplicité est la sophistication suprême".

Cela peut paraître contradictoire avec le "Level 3 : le code est générique", mais c'est là toute la difficulté de mise en place !

C'est un luxe qu'on ne se permet d'appliquer que trop rarement. Pourtant il s'inscrit aussi dans le cadre de la pérennité du code, et de la facilité à le maintenir, le relire dans le temps !

Cela implique un code explicite, concis, qui dans le meilleur des cas ne nécessite même pas de commentaires pour comprendre ce qu'il fait et pourquoi. Sur ce sujet je ne peux que vous conseiller le livre "Coder proprement" (Robert C. Martin).

Excellente nouvelle : dans la majorité des cas, plus le code est simple et plus il sera fiable et performant, car il réduit le nombre d'étapes et la complexité pour atteindre la même finalité. De quoi passer 3 Levels en 1 ! 😎

Développeur manga satisfait avec le poing levé

Conclusion

Vous l'aurez deviné : atteindre systématiquement les cinq niveaux de qualité de code est rare. D'autant plus que les priorités peuvent varier d'un projet à un autre, et que bien souvent tous les niveaux précédents ont tendance à empiéter sur la simplicité du code.

D'un côté, se soucier de cette simplicité le plus tôt possible apporte un retour sur investissement ultra rentable. Mais comme toujours, les délais de livraison obligent parfois à faire des compromis pour atteindre les objectifs en temps et en heure.

Malgré tout, ces niveaux restent un véritable défi d'amélioration continue et de perfectionnement pour réaliser des projets toujours plus impeccables. À la fois pour les développeurs, et pour le client final.

Et vous, quel niveau atteignez-vous ?

(Mention : toutes les images de cet article ont été générées avec Microsoft Copilot.)

Top comments (0)