2 points par GN⁺ 2025-03-12 | 1 commentaires | Partager sur WhatsApp
  • En examinant récemment une codebase, l’auteur a constaté une fatigue mentale malgré la qualité du code
    • Cela tenait moins à la complexité du code qu’à sa lisibilité
  • Il en a tiré 8 motifs pour améliorer la lisibilité du code

Métriques de lisibilité du code et métriques alternatives de complexité

  • Il n’existe pas de métrique universelle et largement utilisée pour mesurer la lisibilité du code
  • On ne trouve que des articles académiques peu utilisés en pratique ou des avis personnels
  • Plutôt que de créer une nouvelle métrique, l’accent est mis sur des motifs visuels que tout le monde peut facilement discuter
  • Conditions importantes pour une métrique de complexité :
    • Elle doit fonctionner sur des snippets de code source ou des fonctions individuelles
    • Elle doit se concentrer sur la manière d’écrire le code plutôt que sur la complexité algorithmique
    • Elle ne doit pas se focaliser sur les éléments de style (noms de variables, espaces, indentation, etc.)

Métrique de complexité de Halstead

  • Une métrique de complexité du code développée par Maurice Halstead dans les années 1970
  • Elle permet de quantifier la façon dont le code est écrit, indépendamment du langage et de la plateforme
  • Elle calcule la longueur, le volume et la difficulté d’un programme à partir du nombre d’opérateurs et d’opérandes
  • Principales mesures :
    • nombre d’opérateurs distincts (n1)
    • nombre d’opérandes distincts (n2)
    • nombre total d’opérateurs (N1)
    • nombre total d’opérandes (N2)
  • Plus le code utilise d’opérateurs et d’opérandes, plus sa complexité augmente
  • Comme la définition des opérateurs et des opérandes n’est pas claire dans tous les langages, il est important d’utiliser des outils cohérents

Enseignements tirés de la complexité de Halstead

  • Les fonctions courtes avec peu de variables sont plus lisibles
  • Il vaut mieux limiter l’usage des opérateurs spécifiques à un langage ou du sucre syntaxique (syntactic sugar)
  • Les chaînes de programmation fonctionnelle (map/reduce/filter, etc.) deviennent moins lisibles lorsqu’elles s’allongent trop

Complexité cognitive (Cognitive Complexity)

  • Une métrique de complexité développée par SonarSource
  • Une tentative de mesurer plus précisément la difficulté de lecture du code
  • Trois principes majeurs :
    1. Les constructions abrégées (shorthand constructs) réduisent la difficulté de lecture
    2. Les ruptures dans un flux non linéaire augmentent la difficulté
    3. Les flux de contrôle imbriqués augmentent la difficulté

Enseignements tirés de la complexité cognitive

  • Les constructions abrégées sont concises, mais comportent un risque potentiel de bugs
  • Les conditions et opérateurs logiques nuisent à la lisibilité lorsqu’ils sont utilisés à l’excès
  • La gestion des exceptions est l’une des principales causes de complexité du code
  • goto doit en général être évité, mais peut être utile dans certains cas
  • Il est préférable de réduire autant que possible les structures de contrôle imbriquées

Forme des fonctions, motifs et variables

  • La « forme » visuelle d’une fonction joue un rôle important dans la lisibilité du code
  • Trois principes pour améliorer la lisibilité :
    1. Utiliser des noms de variables clairs et spécifiques
    • Éviter le masquage de variables (shadowing)
    • Utiliser des noms visuellement distincts (éviter des noms proches comme i et j)
    1. Réduire la durée de vie (liveness) des variables
    • Plus la portée d’utilisation d’une variable est courte, mieux c’est
    • Les variables conservées longtemps au-delà des frontières d’une fonction augmentent la complexité
    1. Réutiliser des motifs de code familiers
    • Maintenir des motifs de code cohérents améliore la lisibilité
    • Privilégier les motifs déjà connus plutôt que de nouvelles approches

8 motifs pour améliorer la lisibilité du code

  1. Réduire le nombre de lignes/opérateurs/opérandes – de petites fonctions et peu de variables améliorent la lisibilité
  2. Éviter les nouvelles approches – conserver des motifs familiers dans la codebase
  3. Regrouper – isoler les longues chaînes de fonctions, itérateurs, etc. dans des fonctions auxiliaires
  4. Simplifier les conditions – garder des conditions courtes et limiter le mélange d’opérateurs logiques
  5. Limiter goto – ne l’utiliser que de façon restreinte, si nécessaire, pour la gestion d’erreurs
  6. Minimiser l’imbrication – réduire la logique imbriquée et, si besoin, l’extraire dans des fonctions
  7. Utiliser des noms de variables explicites – employer des noms spécifiques et non redondants
  8. Raccourcir la durée de vie des variables – les garder brèves dans une fonction et éviter qu’elles ne traversent les frontières de fonction

Conclusion

  • La lisibilité du code est un élément important de la qualité du code
  • Halstead et la Cognitive Complexity permettent de quantifier les problèmes de lisibilité et d’indiquer des pistes d’amélioration
  • Un code concis et clair facilite la maintenance et réduit le risque de bugs
  • Écrire un code optimal consiste à privilégier la simplicité, la cohérence et la clarté

1 commentaires

 
GN⁺ 2025-03-12
Avis Hacker News
  • Enchaîner des structures de programmation fonctionnelle comme map, reduce et filter est concis, mais les longues chaînes ont tendance à nuire à la lisibilité

    • Ce n’est pas ce que l’article sous-entendait
    • Cela ressemble à une plainte classique consistant à dire que c’est mauvais parce qu’on n’y est pas habitué
    • Avec un peu d’habitude, c’est plus facile à lire et à écrire que d’autres approches
    • Il est important d’apprendre les bases de la programmation fonctionnelle
    • Pas besoin d’expliquer les monades, mais il faudrait au moins être assez familier pour ne pas critiquer map et filter au hasard
  • Un aspect important du bon code est qualitatif et littéraire

    • Cela peut être inconfortable pour les programmeurs et universitaires ayant une pensée mathématique
    • On peut aimer Dostoïevski et Wodehouse, mais leurs écrits sont très différents
    • Il faut du temps pour comprendre le style d’une base de code
  • Le problème le plus fatigant quand on lit du code, c’est la mutabilité

    • La capacité à ne « fixer » une variable qu’une seule fois est un grand cadeau
    • Le processus de compréhension d’une méthode devrait croître de façon monotone de 0 % à 100 %
    • Les GOTO sont nuisibles parce qu’il est difficile de connaître l’état des variables mutables
  • Les petites fonctions et un nombre réduit de variables sont généralement plus faciles à lire

    • L’accent mis sur la « lisibilité » est biaisé vers la micro-lisibilité
    • Cela rend le code excessivement fragmenté
    • Les langages de la famille APL se situent à l’extrême opposé
  • TypeScript rend le code difficile à lire

    • Cela va si le modèle de données reste « atomique »
    • Si l’on dépend de l’inférence de types, il devient difficile de remonter les champs jusqu’à leur emplacement d’origine
  • La fonction getOddness4 introduit une asymétrie

    • La fonction getOddness2 offre un choix symétrique
  • L’article est intéressant, mais peu satisfaisant

    • Je ne suis pas d’accord avec l’idée d’éviter les opérateurs spécifiques à un langage ou le sucre syntaxique
    • Des structures comme map, reduce et filter, bien utilisées, remplacent d’autres opérateurs et réduisent le « volume »
  • La tentative de définir la lisibilité mérite des éloges

    • Avec des tests sur beaucoup de personnes, on pourrait trouver les véritables dimensions de la lisibilité
  • La complexité du code s’exprime par la taille de l’arbre syntaxique

    • Une réduction de la complexité locale n’a pas un grand effet sur la complexité globale
  • Pour les longues chaînes de fonctions ou les callbacks, il vaut mieux les diviser en petits groupes et utiliser des variables bien nommées

    • Les deux versions sont équivalentes en termes d’efficacité
    • La différence se situe au niveau du compilateur