Une histoire de (continuous) refactoring pour les non-initiés



Avez-vous déjà joué à l’exercice de kata de code appelé “Sing a song” ? Dans cet exercice simple mais efficace, les développeurs sont invités à reprendre une base de code existante non testée pour couvrir un cas très simple. L’objectif de la pratique du kata n’est pas seulement d’améliorer les compétences en matière de codage, mais aussi de favoriser un état d’esprit d’amélioration et d’apprentissage continus parmi les membres de l’équipe et parfois les parties prenantes.

Dans cette première partie, j’expliquerai pourquoi le (continuous) refactoring joue un rôle crucial dans le cycle de vie du développement logiciel. Je discuterai de mon expérience de l’utilisation du kata pour communiquer efficacement l’importance de la pratique à diverses parties prenantes.

La Nécessité du Refactoring

Le refactoring est la pratique qui consiste à restructurer un code existant sans en modifier le comportement externe. Elle permet aux développeurs d’améliorer la conception interne de leur base de code, ce qui présente plusieurs avantages :

  1. Amélioration de la qualité du code : aide à éliminer la dette technique et à améliorer la qualité globale du code.

  2. Un temps de développement plus court : Une base de code plus propre et plus facile à maintenir permet aux développeurs d’écrire de nouvelles fonctionnalités plus efficacement.

  3. Réduction des coûts de maintenance : Le code remanié est plus facile à comprendre et à modifier, ce qui réduit les coûts de maintenance à long terme.

  4. Amélioration de la collaboration au sein de l’équipe : Des pratiques constantes de refactoring permettent de s’assurer que tous les membres de l’équipe de développement travaillent avec une base de code de haute qualité.

Cependant, le refactoring n’est pas toujours une tâche simple. Dans certains cas, il peut être nécessaire de mettre en œuvre une fonctionnalité avant que la base de code ne puisse intégrer les changements souhaités. Dans de nombreux cas, une base de code peut ne pas supporter initialement une nouvelle fonctionnalité en raison d’une conception obsolète ou d’une mauvaise organisation, ou simplement parce que ce n’était pas nécessaire.

Comme le montre en partie l’historique Git suivant, aucune équipe n’a été en mesure d’implémenter complètement la fonctionnalité demandée sans effectuer une grande étape de refactorisation :

* 53617bb feat: Make the song configurable for different list of animals.
* 6908eae refactor: Put breaking line inside intro to avoid unecessary code.
* 106e86b refactor: Encapsulate intro inside Animal.
* f56c2c1 refactor: Use named constructor and fluent interface to ease readibility.
* 626303d refactor: Extract code in dedicated classes and ghet rid of static.
* fd7077f refactor: Remove unecessary function.
* 3c5580f refactor: Get rid of all remaining replace using string interpolation.
* 2542a12 refactor: Remove ultimate replace of first animal.
* afe46f4 refactor: Remove ultimate replace of last animal.
* a74ec51 refactor: Rename some variables.
* 794caae refactor: Build last paragraph inside common function.
* 3226cb5 refactor: Encapsulate animal inside a dedicated class.
* 7f3be87 refactor: Factorise intro of each paragraph.
* 6c71183 refactor: Encapsulate building paragraph.
* 6e5c4f8 refactor: Revert intro and outro.
* 5c1d5a6 refactor: Encapsulate intro and outro.
* 4b0b2a5 refactor: Encapsulate repition in a loop.
* 7801f39 refactor: Encapsulate middle paragraphs.
* 2019f25 refactor: Encapsulate repeated part inside paragraph into function.

Les parties prenantes l’ont immédiatement remarqué. Ils se sont projetés dans des conditions réelles et ont commencé à poser de nombreuses questions pertinentes. Parmi elles, comment éviter de se retrouver dans cette situation ?

Favoriser l'apprentissage et l'amélioration continue

Le faire en continu

Le refactoring exige des développeurs qu’ils investissent beaucoup de temps et d’efforts dans l’amélioration de la base de code existante. Lorsque les développeurs informent les parties prenantes que le rythme de déploiement des nouvelles fonctionnalités devra être ralenti, cela peut provoquer des frictions pour plusieurs raisons :

  1. Non alignement des attentes : Les parties prenantes peuvent avoir fixé des délais et des objectifs spécifiques sur la base des estimations initiales de développement. Lorsque ces délais sont retardés en raison des efforts de refactoring, ils peuvent se sentir déçus ou frustrés.

  2. Perception d’un manque de progrès : Le refactoring peut ne pas être aussi visible ou tangible que la création de nouvelles fonctionnalités, ce qui conduit les parties prenantes à le percevoir comme un manque de productivité ou de progrès dans le projet.

  3. Augmentation des coûts de développement : Le refactoring peut prendre du temps et nécessiter des ressources supplémentaires, ce qui peut entraîner une augmentation des coûts de développement susceptible d’avoir une incidence sur le budget global du projet.

  4. Retard dans les mises en production : Les nouvelles fonctionnalités sont reportées, ce qui risque de retarder la mise sur le marché du produit et de permettre aux concurrents de prendre l’avantage.

  5. Communication inadéquate : Les développeurs peuvent ne pas avoir communiqué efficacement l’importance du refactoring et son impact sur le développement de nouvelles fonctionnalités aux parties prenantes, ce qui entraîne des malentendus et des conflits potentiels.

  6. Problèmes d’allocation des ressources : Les parties prenantes pourraient remettre en question les priorités si elles constatent que les développeurs consacrent beaucoup de temps au refactoring plutôt qu’à la création de nouvelles fonctionnalités qui contribuent directement à la croissance du chiffre d’affaires.

Parfois, la situation peut s’aggraver au point qu’il devient difficile de faire la distinction entre la phase de refactoring et une réécriture complète du logiciel. Ce scénario est généralement le résultat d’une maintenance négligée et d’un manque de priorisation des efforts de refactoring continu dans le processus de développement.

Comme le dit Martin Fowler, [“Frequency Reduces Difficulty”] (https://martinfowler.com/bliki/FrequencyReducesDifficulty.html). En développement logiciel, il existe un conseil paradoxal bien connu qui pourrait s’appliquer au refactoring : “Si ça fait mal, faites-le plus souvent”. Cette affirmation apparemment contre-intuitive repose sur l’idée que le fait de s’attaquer aux points douloureux de manière précoce et régulière peut avoir des effets bénéfiques à long terme.

Conclusion

En bref, le refactoring joue un rôle crucial dans le maintien de la qualité, de la performance et de la durabilité des produits logiciels. Malgré l’utilité du refactoring ad hoc, le faire continuellement est la plupart du temps préférable car cela évite d’avoir des phases dédiées qui peuvent durer une longue période.

Les développeurs intègrent le refactoring dans leurs routines de développement quotidiennes, en veillant constamment à ce que la base de code reste en bon état.

Martin Fowler exprime le continuous refactoring sous le nom de “Opportunistic Refactoring”. Le nom change mais le principe reste le même.

From the beginning I’ve always seen refactoring as something you do continuously […]. Yet there’s a common misconception about refactoring in that it’s something that needs to be planned out. Certainly there is a place for planned efforts at refactoring, even setting aside a day or two to attack a gnarly lump of code that’s been getting in everyone’s way for a few months. But a team that’s using refactoring well should hardly ever need to plan refactoring, instead seeing refactoring as a constant stream of small adjustments that keep the project on the happy curve of the DesignStaminaHypothesis.

Le prochain chapitre du Kata “Sing a Song” est en cours d’écriture.

L’article a été rédigé avec l’aide de Mistral AI.