"Refactoring" de Martin Fowler
Le blog entre en 2020 avec la présentation de Refactoring, de Martin Fowler 🚀 : la trame principale et les grandes idées. L’article est long, mais je pense qu’il apportera l’essentiel du livre à ceux qui n’ont pas le temps de le lire… Sans priver les autres de l’envie de le faire. Et je conseille à tous de le faire 😃.
Cette seconde édition du livre est une réécriture (partielle) pour JavaScript. Son but est de guider les développeurs dans l’amélioration de leur code, par petites itérations, sans en changer le comportement. C’est l’exacte définition du refactoring : changer la structure du code, sans changer son comportement…
Et non, Refactoring ne signifie pas “réécrire un code moche”.
Pour ceux qui ne connaissaient pas l’auteur, je vous invite à lire son excellent site et à le suivre sur Twitter.
L’ouvrage présente un long catalogue des techniques de refactorings, consultable sur le site officiel.
J’en profite aussi pour partager Refactoring.Guru qui présente du contenu de qualité sur le refactoring et les design patterns, inspiré des livres comme : Refactoring, Effective Java, Design Patterns (GoF), Head First Design Patterns…
Chapitre 1 - Un premier exemple de Refactoring
Le livre ouvre directement la marche avec un exemple. Le code de départ présente plusieurs imperfections (et code smells). Et ô surprise, une évolution est demandée par l’utilisateur 😱. Avant de se lancer, l’auteur effectue un rapide “état des lieux” (analyse critique).
💡 Le premier chapitre est consultable gratuitement et légalement en ligne. Vous pourrez y voir ledit code et toutes les étapes du refactoring, avec les explications de Martin Fowler.
Si l’ajout d’une fonctionnalité est compliqué à cause de l’état du code, l’auteur recommande le refactoring avant tout nouveau développement. Et avant de procéder à la moindre modification, il faut s’assurer que le code soit couvert par des tests.
S’ensuivent plusieurs techniques de refactorings du catalogue, en exécutant continuellement les tests pour vérifier que le comportement du code n’ait pas été altéré. Les refactorings appliqués sont assez communs : Extract Function (Method), Inline Variable, Move Method, etc. Petit à petit, le code servant d’exemple devient plus lisible et maintenable.
En résumé
- Pas de refactoring sans test
- Le refactoring :
- est effectué en petites étapes, précédées par une avancée dans la compréhension du code
- ne modifie pas le comportement du code : les modifications ne nécessitent pas de nouveaux tests
- n’a pas besoin d’être violent : même une petite amélioration est bonne à prendre, Boy scout rule, “Laissez le camp plus propre que vous ne l’avez trouvé”
- Les tests existants sont exécutés continuellement, c’est notre filet de sécurité
- Un code a été “amélioré” lorsqu’il est plus simple à modifier : la productivité du développeur est augmentée car il peut ajouter de la valeur au logiciel plus rapidement… “Code should be obvious”.
Au risque de me répéter… Des petites étapes, appuyées par les tests. À l’image des baby steps du TDD, elles permettent d’éviter de longues sessions de debugging. Refactorman n’est pas un héros 😬.
Chapitre 2 - Les fondements du Refactoring
Martin Fowler donne une définition précise de Refactoring, à la fois en tant que nom et en tant que verbe :
Refactoring (nom) : Changement de la structure d’un code pour faciliter sa compréhension et sa modification, sans changer son comportement observable.
Le comportement observable est celui connu par l’utilisateur. Il n’est pas question du comportement technique (allocations mémoires, utilisation des cycles CPU, etc.).
Refactoring (verbe) : Restructurer du code en appliquant une série de refactorings.
Donc, quand on refactor, on applique des refactorings. La prochaine fois qu’un collègue vous dira “ce code bug, je dois faire une refacto”, n’hésitez pas à prendre un air hautain et toxique pour lui expliquer qu’il dit n’importe quoi 😏.
En effet, le terme est souvent mal employé (pour désigner une session de debug, voire la réécriture complète d’un code…). L’auteur a écrit un article à ce sujet : Refactoring Malapropism.
Le Refactoring : Pourquoi ?
- Faciliter la compréhension du code (et par conséquent, la recherche de bugs)
- Améliorer la conception (design) du logiciel
- Développer plus rapidement
Dans son article Design Stamina Hypothesis, Martin Fowler présente l’hypothèse du seuil de rentabilité du design (qualité, conception), alias design payoff line.
Sous ce seuil, il est possible de gagner en productivité (livrer davantage de fonctionnalités) en négligeant la qualité (le design) du code. C’est ce qu’on appelle Acheter de la dette technique. Passé ce seuil, c’est l’inverse qui se produit : le développement est ralenti, les livraisons prennent du retard, la moindre modification devient coûteuse.
Le refactoring permet, par l’amélioration continue du code, de maintenir un certain niveau de qualité et de productivité 🤓.
Le Refactoring : Quand ?
L’auteur affirme faire du refactoring en permanence, comme de nombreux développeurs qui y ont pris goût.
Le meilleur moment est celui qui précède l’ajout d’une fonctionnalité, ou la correction d’un bug. C’est ce que Martin appelle le Refactoring préparatoire. Il s’agit d’améliorer l’état du code avant d’avoir à le modifier.
L’auteur définit d’autres “catégories” de refactorings, comme :
- Compréhension : si le code a nécessité un effort intellectuel pour être compris, alors il est certainement possible de l’améliorer. Encore une fois, “Code should be obvious”. Des actions aussi simples que Rename Variable peuvent avoir un sérieux impact.
- Ramassage des déchets : quand on remarque que le code est mal conçu, ou inutilement complexe. Il n’a pas besoin d’être entièrement réécrit, mais on peut l’améliorer progressivement, sur la durée. “Laissez le camp plus propre que vous ne l’avez trouvé”.
💡 Si vous faites des conventional commits, pensez aussi à isoler vos refactorings dans un commit
refactor
. Vos collègues chargés de la revue de code apprécieront certainement.
Etc.
Le chapitre est long et dense. L’article n’a pas pour vocation de couvrir tous les éléments qui donnent sa valeur au livre.
Néanmoins, puisque c’est un vice très répandu dans l’industrie, je partage l’une des notions abordées : la Speculative Generality. On connait tous un dév qui écrit parfois 3 fois plus de code que nécessaire “juste au cas où”. Et il est même souvent “prêt à parier que ça va servir”.
L’auteur avance l’idée qu’avec un refactoring continu, il est très facile de rendre le code plus flexible au besoin, plutôt que d’anticiper cette flexibilité (flexi-complexité…). C’est l’opposition entre le Use-case driven development et ce que j’appelle le “Just in case” driven development.
Over-engineering...
Speculative Generality est aussi abordé dans le chapitre 3 sur les code smells. Dans ce chapitre 2, Martin Fowler parle plus précisément de “Speculative flexibility”.
Chapitre 3 - Les Smells dans le code
Une longue série de Code smells, voilà ce qu’est ce (court) chapitre coécrit avec Kent Beck, l’auteur de Test Driven Development: by Example qui est aussi le créateur du “TDD”.
Large class, Divergent Change, Shotgun Surgery, Duplicated code… Retrouvez-les sur Refactoring.guru.
Looks familiar…
Au sujet de Large Class, Divergent Change et Shotgun Surgery, je trouve inévitable le rapprochement avec le Single Responsibility Principle (SRP) d’Uncle Bob, trop souvent mal interpété. En effet, celui-ci ne stipule pas qu’un module ne devrait faire qu’une seule chose mais qu’il ne devrait avoir qu’une seule raison de changer.
“Regroupez les choses qui changent pour les mêmes raisons. Séparez celles qui changent pour des raisons différentes”.
En d’autres termes, le SRP (S de SOLID) a pour objectif de limiter l’impact des changements effectués dans le code. On peut considérer que c’est ce que préconise aussi le livre, avec ces smells.
Chapitre 4 - Construire les tests
Martin Fowler explique la valeur des tests automatisés, leur nécessité dans le refactoring, et le temps qu’ils font économiser en “debugging” pour mieux le réinvestir dans la production de valeur ajoutée.
Si vous êtes, comme moi, habitué et rompu au TDD, ce chapitre ne sera pas le plus passionnant de l’ouvrage. Il a cependant le mérite de présenter les fondamentaux des tests unitaires et de recommander le TDD 🔥.
When you get a bug report, start by writing a unit test that exposes the bug.
Résumé
Puisque tous les chapitres restants présentent le catalogue disponible en ligne, je passe directement au résumé 🎉.
Au-delà des techniques de refactorings présentées, j’ai apprécié la lecture de cet ouvrage aussi sage que pragmatique. Martin Fowler offrait, déjà en 2000, une solution à certains “travers” de l’industrie que l’on subit encore en 2020. J’aurais d’ailleurs souhaité le lire il y a 10 ans, quand j’ai écrit mes premières lignes de code en tant que Développeur “professionnel”.
Quand on démarre un projet, il est impératif d’écrire le minimum de code pour répondre au besoin exprimé, qui est souvent encore mal compris et mal connu. Plus un code est simple, moins il demande d’effort en refactoring… Refactoring qui facilite son évolution tout en contenant sa complexité (inexorablement amenée à croître). Ce qui implique une vélocité préservée pour les développeurs. Comme le dit l’auteur dans le chapitre 3 :
After all, software is meant to be soft.
Puisque le refactoring ne doit pas être effectué sans test, et puisqu’il doit être effectué par petites itérations, c’est un parfait associé du TDD. Mieux que ça, il fait partie intégrante de son mantra “Red, Green, Refactor”.
Je conclurai en vous recommandant chaudement la lecture et en citant cette punchline (que j’adore) présente sur la couverture :
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
Retrouvez-moi sur Twitter :
@VinceOPS