Licence CC BY-NC-ND

Lisibilité d'un code source

Comment rendre votre code lisible, quelque soit le langage

Publié :
Auteurs :

Catégories :

Déterminer la qualité d'un code source. Voilà un sujet qui semble bien subjectif. On pourrait croire que cet exercice est impossible à tout jamais, et qu'à part quelques cas extrêmes d'obfuscation, il est impossible d'avoir la vérité sur le sujet. D'autres croient au contraire que certaines pratiques bien connues permettent d'améliorer la lisibilité d'un code source.

Mais entre ceux qui disent qu'un code doit contenir des commentaires et ceux qui disent l'inverse, ceux qui prônent un code linéaire et ceux qui découpent tout ce qui bouge en fonctions, ceux qui… Bref, quelle est la vérité sur le sujet ?

La psychologie cognitive a fourni quelques éléments dans ce domaine. Pour le moment, on ne sait pas grand chose sur la lecture de code source en elle-même. Néanmoins, la psychologie cognitive a beaucoup travaillé sur la compréhension de texte écrit et la compréhension du discours oral. Et ce qu'elle a appris s'applique parfaitement au code source.

Qu'est-ce qui influence la compréhension d'un code source ?

Aussi bizarre que cela puisse paraître, des études expérimentales ont montré que la compréhension d'un programme était fortement corrélée à sa mémorisation : mieux on comprend un programme, mieux on le mémorise. Rien d'étonnant à cette influence de la mémoire : le même phénomène a été observé pour la compréhension de textes ou d'histoires, et un code source est un texte comme un autre.

Les mécanismes cachés derrière la compréhension de texte ou de code source se basent beaucoup sur le fonctionnement de la mémoire. On peut en déduire rapidement que pour rendre un programme facile à lire, il faudra prendre en compte la mémoire du lecteur.

Par mémoire, on pourrait penser à la mémoire à long terme, qui contient nos connaissances et nos souvenirs. Celle-ci est importante, vu qu'elle contient toutes nos connaissances en programmation. Mais ce n'est pas elle qui nous importera le plus dans ce qui suit.

Le code à lire nous parvient sous la forme de texte, capté par notre œil. Le cerveau traite rapidement ces informations avant d'envoyer le résultat dans des mémoires à court terme. Les informations qui sont stockées dans ces mémoires à court terme sont oubliées rapidement : on estime qu'au delà de quelques secondes ou quelques minutes, les informations sont oubliées.

Ces mémoires à court terme sont fortement utilisées quand on souhaite réfléchir, résoudre des problèmes, manipuler des informations, comprendre quelque chose, etc. Dans ces conditions, les mémoires à court terme se chargent de maintenir à l'esprit les informations pertinentes, afin de les traiter.

Nos psychologues supposent la présence de trois grandes mémoires à court terme :

  • le calepin visuo-spatial, qui stocke des informations visuelles ;
  • la boucle phonologique, utilisée dans l'interprétation du langage écrit ou parlé : elle stocke des phonèmes, des sons utilisés pour construire des mots ou des syllabes ;
  • et le tampon exécutif, qui stocke des concepts.

Le tout est contrôlé par un superviseur attentionnel, qui sélectionne les différentes informations dans ces mémoires à court terme, et effectue des traitements dessus.

Ces différentes mémoires à court terme ont des capacités limitées : au delà d'un certain nombre d’informations, elles débordent. En cas de surcharge, la réflexion et la compréhension sont perturbées et deviennent inefficaces. Et c'est d'une importance capitale quand on programme : un bon code est avant tout un code qui ne surcharge pas la mémoire à court terme du lecteur.

Familiarité du code et reconnaissance de motifs

La charge de la mémoire de travail varie beaucoup suivant l'état des connaissances antérieures stockées dans cette mémoire. En effet, toute connaissance déjà présente en mémoire à long terme forme une entité unique dans la mémoire à court terme : ces entités prises comme un tout en mémoire de travail sont appelées des chunks.

Schémas

Ces chunks peuvent prendre diverses formes, mais ceux qui vont nous intéresser sont les schémas, des motifs qui permettent de repérer des situations familières, analogues à quelque chose de déjà connu. Certains de ces schémas permettent de reconnaitre :

  • les structures syntaxiques usuelles (boucles for, while, if, etc.) ;
  • des morceaux de code courants dont on connaît la fonctionnalité (reconnaître un compteur ou une variable accumulateur) ;
  • des morceaux de code abstraits, débarrassés de leurs détails (pensez aux design patterns : le rôle des classes ou leur nom fait partie des détails non-présents dans le pattern) ;
  • et bien d'autres choses encore.

Par exemple, les design patterns font partie des motifs qu'un programmeur peut exploiter relativement facilement. Quand un programmeur reconnaît l'usage d'un pattern, le programmeur qui lit le code peut rapidement tirer des conclusions utiles sur le fonctionnement du programme.

Comme autre exemple, il n'y a pas 36 façons d'utiliser une variable : celles-ci suivent des "design patterns" d'utilisation. Ces rôles sont variés, mais onze d'entre eux capturent 99% des situations rencontrées dans un code source :

Rôle description
Valeurs fixe Variable initialisée dont le contenu ne change pas.
Stepper Variable dont la valeur change, et dont la suite des valeurs qu'elle prend suit une suite bien précise, prédictible. Peut être vu comme une généralisation d'un compteur, et notamment des compteurs de boucles. On peut aussi citer les variables qui interviennent dans le calcul de la suite de fibonnaci : on peut prédire leur valeur à l'avance.
Most-recent holder Variable qui stocke la dernière valeur rencontrée lors du parcours d'une succession de valeurs.
Most-wanted holder Variable qui stocke la meilleure valeur rencontrée lors d'un parcours d'une succession de valeurs. Par exemple, une variable temporaire utilisée pour stocker le maximum potentiel d'un tableau lors de la recherche du maximum dans un tableau.
Gatherer Variable qui accumule le résultat d'autres variables dans une succession de valeurs. Par exemple, regardez la variable temporaire utilisée dans le calcul de la somme d'un tableau.
Transformation Variable dont la valeur est déterminée par un calcul effectué sur d'autres variables.
Follower Variable qui sert à stocker le contenu qu'avait une autre variable juste avant.
Flag Variable de 1 bit.
Organiser Tableau temporaire qui est utilisé pour réarranger ses éléments après une initialisation.
Temporary Variable qui stocke temporairement une valeur, et dont on n'a pas besoin après son premier accès.
Other Ce qui reste…

Une étude faite par Sajaniemi, Navarro et Prieto (2005) a montré, par un protocole spécial, que les programmeurs se servaient de ces rôles pour catégoriser les utilisations de variables.

Ces motifs sont la base de l'expertise en programmation : un bon programmeur est quelqu'un qui reconnaît des motifs familiers quand il lit un code source. À ce sujet, vous pouvez tenter de lire le document suivant : Expertise for programming : a schema based approach.

La reconnaissance de ces motifs familiers se base sur des indices présents dans le code source, essentiellement sous la forme de noms de variables, la structuration du code via l’indentation, etc. Ces indices vont alors évoquer un motif, ce qui permettra de rapatrier des informations associées à ce motif dans la mémoire de travail. Ces informations permettront alors de faire des déductions sur le fonctionnement du code source.

Ce phénomène peut être exploité quand on programme, en faisant en sorte que le code utilise au mieux ces motifs courants : les constructions du code doivent respecter les idiomes connus, qui sont autant de motifs présents dans la mémoire à long terme des autres programmeurs. De plus, les indices qui évoquent ces schémas doivent être rendus les plus explicites possibles. Il faut donc bien nommer ses variables, structurer le code sur la base d'idiomes, indenter correctement.

Chunks créés lors de la lecture

Si les schémas en mémoire à long terme ont leur importance, lire un code source ne consiste pas seulement à détecter de tels schémas : un programmeur peut aussi fabriquer ses propres chunks.

Lorsqu'un programmeur comprend un code source, celui-ci va regrouper plusieurs lignes de code dans un seul chunk. Évidemment, ces chunks ne contiennent pas les lignes de code exactes, mais mémorisent leur sens, leur fonctionnalité, ou leur utilité dans un programme. La compréhension d'un code source consiste simplement à assembler ces chunks pour former des chunks de plus en plus évolués, capables de contenir tout le code à traiter.

Segmentation du code

Pour éviter de surcharger la mémoire à court terme, il existe une solution : segmenter le code en petites unités. En faisant ainsi, vous allez répartir la charge cognitive dans les segments : chaque segment sera traité en une seule fois, et aura donc une charge cognitive assez faible.

Pour segmenter un code, le mieux est d'utiliser les diverses constructions du langage, l'indentation, le style de formatage du code, etc. Découper son code en chunks, en paragraphes, découper les expressions dans des variables, regrouper les lignes de code en ensembles ayant une fonctionnalité particulière, etc., sont des pratiques qui permettent de réduire la charge cognitive du code facilement.

Découpage visuel

On peut chunker notre code à différents niveaux. Par exemple, la segmentation du code peut se baser sur un critère visuel : cela aide notre calepin visuo-spatial. Cela demande de découper un code en paragraphes séparés par des sauts de lignes, d'utiliser un style d'indentation et un formatage du code crédible. Les paragraphes de code obtenus ainsi sont souvent des morceaux de code à la lecture linéaire (sans sauts), des corps de conditions, des corps de boucles, des fonctions, des classes, des méthodes, des modules, etc.

Mine de rien, c'est le principe qui se cache derrière l'indentation : l’indentation permet de découper le code en segments individuels qui permettent d'identifier les corps de boucles, de conditions, etc.

Autre conseil : paragraphez votre code source. Découpez le en blocs, séparés chacun par un saut de ligne. Ce conseil peut paraître évident, mais sachez qu'il est très efficace : cela marche aussi bien pour du texte que pour du code. Par exemple, n’hésitez pas à passer une ligne pour séparer des lignes de déclaration de variables, passer des lignes pour séparer les corps de boucles conditions, fonction, etc., du reste.

De plus, cela a aussi un autre avantage : cela permet à notre cerveau de mieux gérer le rythme de lecture. Avec des blocs de code séparés par des lignes vides, le cerveau arrive mieux à se repérer, et peut gérer les saccades oculaires (les mouvements de l’œil lors de la lecture) plus finement.

Par exemple, il arrive que les mouvements de l’œil zappent une ligne ou deux. Dans ce cas, nettement moins rare qu'on ne le pense, on doit retrouver la ligne exacte où reprendre la lecture. Cela permet aussi à notre cerveau de mieux repérer le début des lignes de codes, et donc de placer l’œil plus rapidement sur la bonne ligne, évitant de devoir utiliser sa concentration pour cela.

Découpage sémantique

Mais le plus important tient à un découpage sémantique, basé sur le contenu du code, et non sa forme : chaque bloc de code doit avoir une fonction bien précise, effectuer une action particulière, un calcul bien précis, etc.

Pour commencer, il faut savoir quels sont les portions de code source qui doivent subir un tel découpage. Une bonne technique consiste à regarder la densité d'identifieurs : toute fonction ou variable déclarée dans un code source correspond à un concept ou une information qui prendra de la place en mémoire à court terme.

Pour prendre en compte cet état de fait, les chercheurs ont inventé une métrique : la densité d'identifieurs. Cette métrique compte le nombre d'identifieurs utiles dans un code source ou un chunk, et divise le tout par le nombre de lignes du code/chunk. Si la densité d’identificateurs d'un code source est trop élevée, c'est signe qu'il faut segmenter celui-ci plus efficacement.

Cette métrique a été évaluée sur du texte, mais pas sur du code source, mais les conclusions peuvent être transposées à l'écriture de code sans « trop » de problèmes : un code source n'est que du texte, après tout.

On remarquera que cette métrique prend en compte le nombre de lignes, ce qui fait qu'à nombre de concepts égal, augmenter le nombre de ligne diminuera la densité des identifieurs. Il y a bien sur une limite, mais il y a une morale à cela : une ligne de code trop longue est souvent une mauvaise ligne de code. Compresser son code en réduisant le nombre de lignes est donc un bon moyen de flinguer la lisibilité, contrairement à ce que certains croient.

Reste que cette métrique a ses limites. Après un temps très court (quelques secondes, voire beaucoup moins), le contenu de la mémoire à court terme est relié aux informations en mémoire à long terme. Si on a besoin de s'en rappeler plus tard dans la fonction, on pourra éventuellement rapatrier les informations de la mémoire à long terme (cela se rapporte au phénomène de Long-Term Working Memory). C'est très rapide et peu coûteux en "ressources cognitives" : c'est même plus rapide qu'un traitement sur des informations en mémoire à court terme.

Déclarer une centaine de variables dans une fonction n'est donc pas un problème, si la fonction est bien écrite. Pas besoin de stocker chaque variable en mémoire à court terme durant toute l’exécution de la fonction : la mémoire à long terme peut prendre le relais.

La mémoire à court terme n'est donc un point limitant que quand on doit traiter plusieurs choses simultanément pour obtenir un résultat. Par exemple, cela arrive quand on veut combiner plusieurs variables en même temps pour en obtenir une autre. Or, en même temps signifie dans la même ligne de code, dans la même expression.

C'est ce qui fait que certains pensent que découper son code source en lignes de code comprenant au maximum 3/4 variables est une bonne idée. Chunker ce genre de lignes en plusieurs sous-lignes diminue la complexité du code. Cela vient du fait qu'une ligne trop longue a souvent une charge cognitive trop importante. Vu que le cerveau ne peut gérer que 4 concepts à la fois, il est recommandé de concevoir nos lignes de code en ce sens.

Ce conseil s'adapte facilement au cas où nos lignes de code consistent en une expression : diminuer la charge cognitive de vos expressions en les découpant en sous-expressions dont les résultats sont combinés entre eux. Il n'est pas rare que dans certains codes, on trouve des lignes à rallonge dans lesquelles on effectue un calcul arithmétique ou booléen complexe.

Si le nombre d'opérateurs et d'identifieur devient trop gros, il vaut mieux découper l'expression en deux sous-expressions, dont le résultat est stocké dans des variables, variables qui servent ensuite dans une expression finale.

Cela sert quand quand il s'agit de régler :

  • la longueur d'une ligne de code contenant une expression ;
  • la longueur d'une liste de déclaration de variables ;
  • la longueur d'une condition dans un if ou un while ;
  • et tout ce genre de choses.

Cela peut aussi s'appliquer aux arguments d'une fonction : au lieu de saturer la mémoire à court terme avec plein d'arguments, rassemblez un paquet d'arguments dans un chunk. Appliqué en situation réelle, cela consiste à regrouper certains arguments dans une structure / classe.

Tracing

Une métrique importante consiste à évaluer le nombre de liens entre différents morceaux de code. Selon Cant et al (1995), la complexité d'un code source dépend non seulement de son chunking, mais aussi de son tracing. Le tracing correspond tout simplement à la linéarité du code : plus la lecture est linéaire, moins il y a de tracing.

Certains morceaux de code ont fatalement une lecture non-linéaire, qui impose de faire des sauts dans le code. L'exemple le plus typique est l'appel d'une fonction ou d'une méthode. Un autre exemple consiste en une variable déclarée assez loin dans le code. On peut aussi donner l'exemple d'un programmeur qui va lire la documentation pour comprendre quelle est le rôle des arguments d'une fonction.

Dans ces situations, le relecteur du code doit arrêter sa lecture du code, et chercher dans la page ou dans d'autres modules une occurrence de cette variable/déclaration de fonction, ou aller lire la documentation de la fonction. Ce genre de situation demande donc d'aller voir ailleurs si la variable/fonction/documentation y est. C'est ce qu'on appelle une situation de tracing.

Effets

Et c'est un très bon indicateur de complexité : plus un code force ce tracing, pire c'est ! Diverses études expérimentales ont montré que les défauts de code, et notamment les bugs, sont assez proches des endroits où l'on doit faire du tracing.

La preuve de cet état de fait est venue d'expériences réalisées en 1992 par Boehm-davis et ses collègues. Leur étude a demandé à des groupes de programmeurs professionnels et novices de juger la compréhensibilité de 9 programmes. Parmi ceux-ci, trois étaient écrit avec une fonction main globale, bien monolithique, et quelques fonctions assez grosses, trois autres étaient réalisés en OO sans héritage, et les trois restants étaient réalisés avec une architecture OO avec héritage. Quelque soit la catégorie de programmeurs, plus un programme était écrit linéairement, sans tracing, plus il était jugé compréhensible et lisible par les cobayes. Les mesures de temps mis pour modifier une fonctionnalité de ces programmes ont confirmé que plus un code était linéaire, plus la modification demandée était rapide.

En 1994, des expériences complémentaires de Cant et ses collègues ont montré que les différentes mesures de dépendances entre classes et modules étaient fortement corrélées à des difficultés à comprendre la fonctionnalité d'un code source.

Dunsmore et ses collègues, dans leur article daté de l'année 2000, ont effectué des mesures sur la corrélation entre délocalisation d'un morceau de code, et erreur de compréhension dans le contexte d'appel de ce code. Erreurs de compréhension qui finissent souvent en bugs.

L'article « Validating OO design metrics on a commercial java application », cité plus haut, montre que plus une classe est réutilisée dans le code, et plus celle-ci a de chances de comporter des bugs : plus on doit tracer vers celle-ci, plus les dégâts se font sentir. Cet effet a aussi été montré par El-eman et al, en 2001.

Pour quitter un peu le domaine du développement, sachez que ce phénomène de tracing apparaît aussi lors de la visite d'un hyperlien. Et là encore, on s’aperçoit que l'effet de tracing joue. Mieux : plus la profondeur d’imbrication de tracing est élevée, pire c'est.

D'ailleurs, ce principe de tracing est utilisé dans le domaine de l'interaction homme-machine, sous le nom de Principle of Minimization of information access cost.

Raisons

On peut se demander pourquoi ce tracing est une catastrophe. Et il y a plusieurs raisons à cela, qui impliquent toute la mémoire à court terme.

Split Attention Effect

Le tracing est signe qu'un morceau de code utile pour la compréhension locale d'un autre morceau de code est délocalisé dans une autre unité de code. Comprendre le code dans sa totalité demande donc de rassembler les informations de plusieurs morceaux de code séparés en un seul chunk dans la mémoire à court terme.

Si les deux morceaux de code se suivent, pas de problèmes : l'assemblage se fait rapidement, sans trop d'efforts. Mais si les deux sont relativement séparés, la mémoire à court terme va devoir maintenir les informations du premier morceau de code lu, pour comprendre le suivant, ce qui impose une forte charge cognitive.

Cet effet n'est pas spécifique à la lecture de code source, et a été observé dans de nombreuses situations : par exemple, si deux informations doivent être reliées ensemble pour être comprises, le fait de séparer des informations au lieu de les mettre à la suite va imposer une plus grande charge cognitive. Même chose pour un schéma d'un dessin : mettre une légende fonctionne nettement moins bien que d'intégrer le texte dans l'image.

On parle de split attention effect : si deux informations doivent être traitées en même temps pour comprendre le code, alors celles-ci doivent être rassemblées l'une à la suite de l'autre.

Task Switching

Lorsqu'un programmer "trace" quelque chose, son superviseur attentionnel va devoir changer de tâche, et faire ce qu'il faut pour retrouver le code source à lire : l'attention est déportée sur la recherche de la fonction/variable/documentation de destination. Seule une très faible partie de l'attention servira à maintenir le contenu du code précédent en mémoire à court terme, qui sera oublié. Dans ces conditions, un gros tracing sera à éviter, alors qu'un petit tracing (quelques lignes en haut ou en bas) jouera peu.

Charge cognitive en trop

Ensuite, le programmeur devra fatalement revenir en arrière une fois la fonction lue. Cela demande de se souvenir de l'endroit où reprendre la lecture, et utilise donc de la place en mémoire à court terme. À chaque fois que l'on trace quelque chose, la charge en mémoire à court terme augmente, diminuant les performances.

Plus un code contient de composants imbriqués, pire est la situation. En général, un humain atteint une limite de performance au-delà de 3 à 4 niveaux d'imbrication, que ce soit pour le niveau d'imbrication d'un code source, le nombre de niveaux d’héritage dans un code source, voire lors de lecture de liens hypertextes.

Solutions

Les différentes raisons qui font que ce tracing survient sont multiples. Il doit certainement exister d'autres situations dans lesquelles le tracing intervient. En attendant, gardez bien à l'esprit : plus un code est linéaire, lisible séquentiellement, mieux c'est.

Toutes les formes de sauts dans le code sont donc des sources de tracing . Cela nous dit donc que le tracing provient :

  • des fonctions, surtout si elles sont petites et nombreuses ;
  • de l'héritage et la composition dans les applications orientées objet ;
  • des exceptions « longue portée » ;
  • des conditions, gotos, breaks, et continue, et tout ce qui ressemble de prêt ou de loin à un branchement ;
  • etc.

Alors certes, tous les cas de tracing ne se valent pas. Il faut bien dire que les conditions, les breaks et autres continue, ne sont pas une source de tracing vraiment problématique. Un goto très local ne pose pas de problèmes : le temps mis à tracer est faible, ce qui fait que le tracing a peu de chance de vider la mémoire à court terme. Par contre, tout ce qui est fort peu local risque de poser des problèmes.

La source de tracing la plus critique est indubitablement l’utilisation de petites fonctions. De nos jours, il est très bien vu de créer des tas de fonctions très petites, censées améliorer la lisibilité et la maintenance. Certains pensent qu'une bonne fonction est une fonction courte, et il faut dire que cela permet d'augmenter le chunking du code. Seul problème : cela augmente le tracing, ce qui finit par sur-compenser rapidement les gains du chunking.

Alors bien sûr, il faut que l'on doive aller lire le contenu de la fonction pour subir les effets du tracing. Mais cela arrive plus souvent que prévu. Selon diverses analyses statistiques, quand un programmeur programme, il passe environ 33% de son temps à rechercher des informations dans la documentation. À ce temps, il faudrait ajouter la lecture de code par tracing, qui est à ce jour, inconnue.

Dès lors que l'on ne connait pas une fonction par cœur, on peut subir les effets du tracing. Pour être franc, les seules fonctions que l'on connait par cœur, ce sont :

  • les fonctions de la bibliothèque standard du langage ;
  • les fonctions de l'application que l'on a déjà utilisées souvent et que l'on a mémorisées suffisamment bien.
  • les fonctions dont le nom seul nous permet de savoir ce qu'elles font.

Le premier cas n'est pas concerné par le tracing, du moins si on se souvient bien de ces fonctions. Après, il arrive malheureusement qu'on oublie certaines fonctions de base du langage, et qu'on doive aller lire la doc (mais non, je n'ai pas une mauvaise mémoire…).

Dans le second cas parmi ceux mentionnés au-dessus, l'oubli joue contre nous. On oublie très facilement les fonctions déjà vues auparavant. Cela empêche de les réutiliser, mais aussi de comprendre ce qu'elles font quand on les recroise. Alors, certes, il se peut qu'on n’oublie pas tout, mais il suffit d'oublier un paramètre pour devoir tracer d'une façon ou une autre. Rien que devoir lire une doc, c'est du tracing. Et seules les fonctions réutilisées très souvent sont mémorisées suffisamment convenablement pour qu'on s'en souvienne. Il est à noter que plus une fonction est chunkée, plus elle passe facilement en mémoire, limitant légèrement ce genre de problèmes.

Quand au troisième cas, même chose : il n’empêche que l'on peut devoir lire la documentation de la fonction, pour comprendre le rôle d'un argument, corriger un bug, etc. Et dans ce cas-là, pas de choix : il faut tracer ! Le seul moyen que je connais pour éviter cela, c'est de stocker les arguments d'une fonction dans une variable, et de la nommer correctement. À noter que cela peut aider à chunker convenablement le code.

Par exemple (vécu récemment), je suis déjà tombé sur une fonction de ce style :

1
2
3
4
5
6
f( 
un calcul compliqué avec 5 opérateurs sur des variables,   
encore un calcul compliqué avec 5 opérateurs sur des variables, 
encore un calcul compliqué avec 5 opérateurs sur des variables, 
encore un calcul compliqué avec 5 opérateurs sur des variables 
) 

Le tout a été remplacé par : f (x, y, width, height) !

Autre raison : l'utilisation de certaines pratiques de conception OO. L'utilisation de ces principes force les développeurs à créer un grand nombre de petites classes, chacune très simple, mais chacune ayant une fonctionnalité très précise, avec une encapsulation très importante. Par exemple, Chidamber et Kemerer ont montré (1994) que la majorité des classes contenaient moins de 10 méthodes dans les programmes qu'ils ont testés. Méthodes ayant chacune peu de lignes, évidemment.

L'usage de l'héritage est aussi fortement corrélé à une utilisation du tracing. Plus l'arbre d'héritage est profond, plus les effets se font sentir. Des mesures sur des codes sources d'applications commerciales, résumées dans l'article « Validating OO design metrics on a commercial java application » ont d'ailleurs trouvé une forte corrélation entre la profondeur d'un arbre d'héritage, et le nombre de bugs lors de l’utilisation de classes situées au bas de l'arbre. Par contre, il n'y a avait aucune corrélation entre nombre de bugs dans ces classes et nombre d'enfants. Un bon arbre d'héritage est donc un arbre large, plus que profond.


8 commentaires

Très bon article ! Merci bien. :)

Tu aurais pu parler du langage formel. Cette branche de recherche s'apparente de près à l'étude des comportements syntaxiques d'un programme. Et même si le tracing est important, le formalisme adopté pour représenter la structure est indispensable dans la mémorisation. On ne code pas du Lisp de la même façon que du C. Dans l'avenir, on verra apparaître les programmations quantiques et ce ne sera plus du tout la même manière de "penser".

Tant de choses, tant de vies, tant de possibilités.

+3 -0

Très intéressant, même si je me rends compte que dans une certaine mesure, on le fait tous, comme quoi, on peut instinctivement trouver des manières qui fonctionnent

Néanmoins, si j'avais eu ce sujet sous la main il y a quelques mois… :D

Petite coquille dans le sous-titre : « quel que soit le langage », et non « quelque soit le langage ». On pourrait formuler autrement en « Comment rendre lisible votre code dans tout langage, quel qu'il soit » (lien)

Édité

Evitez qu'on vous dise de les lire : FAQ PHP et Symfony 2Tutoriel WAMP • Cliquez 👍 pour dire merci • Marquez vos sujets résolus

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte