Don’t make software design such a big deal that you’re in danger of not doing it – page 23, chapitre II
La complexité d’un programme informatique dépend de la manière dont il est organisé et découpé, de la manière dont ces différentes parties sont couplées entre elles, et combien elles sont en cohésion les unes avec les autres.
La cohésion correspond au degré d’adéquation entre les différents éléments d’un même module. En terme de programmation, ceci correspond au fait que ces éléments évoluent ensemble et forment un tout cohérent. Le couplage correspond à la nécessité pour un module de disposer d’informations appartenant à un autre module1.
Le couplage sera donc impacté par la nécessité de modifier un élément alors que nous avions prévu d’en modifier un autre - bien que cela puisse être nécessaire (une fonction pouvant être appelée depuis un autre module, en modifier la signature nécessitera évidemment de modifier également tous les appels y faisant référence), il peut aussi s’agir de dette technique, de mauvaises décisions passées ou d’une architecture pas assez ouverte.
Ces deux concepts (couplage et cohésion) agissent sur la manière dont notre cerveau sera à même de traiter la complexité d’une architecture - et il est donc dans l’intérêt de tout le monde de faire en sorte que la nôtre puisse être abordée de la manière la plus simple possible.
Le développement logiciel est un processus humain : il ne s’agit pas juste de créer un ensemble d’instructions à destination d’un ordinateur, mais plutôt que les intentions prévues à destination d’un ordinateur, puissent être comprises par d’autres personnes.
Valeurs, options et changements #
Les logiciels apportent de la valeur de deux manières :
- Ce qu’il fait aujourd’hui,
- Ce qu’il sera en mesure de faire demain.
Les options (et particulièrement les greffons et possibilités d’extensions) constituent la magie économique d’un développement. Ce qui interfère avec les options sont les changements de structure ou d’organisation : un employé qui quitte l’entreprise, une distance (de compréhension) avec les utilisateurs ou le coût d’un changement à appliquer. En cas de situations chaotiques, créer des options sera votre meilleur allié : cela vous permettra de proposer une base concrète, et d’attendre que la situation évolue avant de pouvoir prendre une décision.
Nettoyage de code #
“Nettoyer” du code au même moment que d’y ajouter des fonctionnalités entraine un risque de collision, surtout dans le cas d’interactions entre plusieurs personnes ou équipes.
Le problème aussi est qu’au plus nous nettoyons du code, au plus nous aurons envie d’en retirer des morceaux, et au plus le risque de collisions augmente. En même temps, au plus du code est propre, au moins les revues (de code) nécessitent du temps. Pour résumer ceci :
Au plus nous passons du temps à nettoyer du code, au moins il sera nécessaire d’y passer du temps.
Dans les équipes disposant déjà d’une bonne culture, le nettoyage d’un morceau de code ne nécessite aucune revue. Mais avant d’arriver à ceci, il sera nécessaire d’avoir réaliser des mois d’expérimentation, de pratiques et de revues.
Il faut noter que la loi de Pareto s’applique également dans le cas du nettoyage de code : 80% des modifications seront appliquées dans 20% des fichiers.
Plusieurs pistes de refactoring sont proposées :
- Guard clauses,
- Dead code,
- Ordre de cohésion,
- Auto-explication des variables,
- Ordres d’extractions,
- One big pile,
- Nécessité d’expliquer des commentaires,
- Suppression de commentaires récurrents.
Guard clauses #
Le fait de n’avoir qu’un seul return
dans une fonction est un héritage du langage Fortran.
Nous pouvons cependant interrompre volontairement (après une inversion de condition, par exemple) le déroulement d’un programme pour en améliorer sa lisibilité.
Ceci permet de court-circuité le cerveau, qui en est ainsi aidé dans sa compréhension.
Si on voit ceci :
if (condition)
if (not other condition)
...some code...
Il est possible d’en améliorer facilement la lisibilité en plaçant une guard clause et en inversant les conditions. Il sera ainsi plus facile (et plus compréhensible pour le cerveau) de lire ceci, car il n’aura aucune combinaison à réaliser - dès que la première condition aura pu être éjectée, il pourra se concentrer sur la suivante sans avoir à se soucier d’interférences :
if (not condition)
return
if (other condition)
return
...some code...
Dead code #
Si du code n’est jamais utilisé, jamais exécuté et ne sert plus à rien pour le moment, supprimez-le. S’il est nécessaire de le retrouver, cela pourra être fait au travers du contrôle de versions ou de le redévelopper. Mais dans l’immédiat, supprimez-le.
Ordre de cohésion #
Faites en sorte que les éléments qui dépendent les uns des autres soient proches (physiquement) les uns des autres.
Ceci en facilite la lecture et l’accessibilité, et permet de se créer facilement une carte mentale de l’emplacement de certains éléments.
Le langage C#
permet de créer des espaces de noms indépendants de l’emplacement physique où se trouve le fichier, tandis qu’en Python, ces espaces de noms sont explicitement liés à l’emplacement sur disque : si nous connaissons le namespace dans lequel évolue un élément, il sera facile de l’y retrouver sans aucune obfuscation.
A termes, tout ceci peut également facilier le réarrangement de ces éléments.
Auto-explication des variables #
La nomenclature à attribuer aux variables touche avant tout à la compréhension générale, afin d’éviter d’avoir des lignes gigantesques - tout en assurant malgré tout une bonne compréhension contextuelle, en évitant les a
, b
et tmp
.
Combiner une bonne nomenclature avec des namespaces est une bonne idée.
One big pile #
L’objectif du nettoyage de code est d’apporter une forme de paix et de satisfaction. A contrario, y passer du temps à chaque revue de code ne va faire qu’ajouter de la frustration et de l’énervement, car ces revues doivent tenir compte d’autres éléments (couplage, découplage, cohésion, …). Il faut faire attention à ne pas sombrer en abîme.
Lorsqu’un nettoyage est nécessaire et que le code ressemble trop à une assiette de spaghettis, une bonne manière de faire consiste à tout remettre à plat, en remontant le contenu des fonctions et méthodes au sein d’une seule et même fonction. De cette manière, il doit être plus facile d’avancer et de réorganiser le contenu, plutôt que de se perdre parmi les différentes fonctions appelées.
Commentaires #
Quand un commentaire explique le fonctionnement interne d’un morceau de code, supprimez-le ; il est possible d’expliquer exactement ce que font quelques lignes de code au travers de phrases, mais il n’existe aucun mécanisme permettant de vérifier et valider que l’explication colle précisément avec la réalité du résultat. Ceci entraîne un couplage entre les commentaires et le code lui-même, et doit être évité.
Un commentaire doit être aussi petit et précis que possible, et ne communiquer que des informations qui ne peuvent être déduites au travers du code.
Par contre, lorsqu’une défectuosité est détectée, et même si cela ajoute un peu de couplage entre le code et les commentaires, il vaut mieux expliquer le point d’accroche que de le laisser de côté et de devoir s’y repencher plus tard.
Coûts d’un développement informatique #
L’équivalence de Constantine indique le coût du développement d’une application est pratiquement égal au coût de sa maintenance. Après la release initiale, un gros travail doit être réalisé afin de stabiliser l’existant. Ensuite seulement, cette maintenance se stabilisera.
Une autre manière de visualiser ceci consiste à regarder le cumul des coûts :
L’équation à résoudre intervient lorsque le coût d’une nouvelle fonctionnalité sans refactoring est inférieur au coût de cette même fonctionnalité avec nettoyage. Dans l’autre cas, il n’y a absolument pas à réfléchir.
La réflexion devient plus complexe lorsque l’ajout d’une fonctionnalité avec refactoring permettra de gagner du temps sur l’ensemble des fonctionnalités à venir. Dans ce cas précis, on parle effectivement d’investissement et de valeur ajoutée, avec une fonction exponentielle sur la valeur des investissements passés et options futures.
Conclusions #
Sympa à lire, pas trop touffu, pas trop prise de tête, et rédigé de manière suffisamment accessible que pour être lu par n’importe quel développeur ayant déjà quelques années d’expériences. Reste à suivre les quelques conseils prodigués au fil des pages, et on sera bons 😉