Le refactoring est une activité quotidienne pour les développeurs, mais elle est souvent perçue comme complexe. D'après les statistiques sur mon projet précédent, un mauvais refactoring se classe au 6e rang parmi les causes les plus fréquentes d’introduction de bugs. Cependant, ne pas refactorer n'est pas une solution, car c'est un processus essentiel pour maintenir la qualité et l’évolutivité d’un projet logiciel. Au fil du temps, la dette technique s'accumule, ce qui ralentit le développement, démotive les intervenants du projet, impacte la stabilité des livrables et freine la mise à l’échelle d'un projet. Le refactoring est un outil indispensable non seulement pour prévenir l’augmentation de la dette, mais également pour la réduire dans les cas les plus extrêmes.
J'ai récemment assisté à des conférences lors de l’événement Mobilis in Mobile et suivi une formation sur ce thème. J'aimerais partager les points clés sur le refactoring et comment le maîtriser. Cet article est le deuxième d'une série de trois, et il vous aidera, en tant que développeur, à réaliser un refactoring sans introduire de bugs.
Refactoriser, c'est aspirer à un code de meilleure qualité. Pour y parvenir, il est crucial de connaître les principes qui définissent un "bon" code et de savoir reconnaître les signes d'un "mauvais" code, souvent appelés code smells. Voici quelques théories fondamentales pour vous guider.
Au cœur de la compréhension du code de qualité se trouve le livre Clean Code de Robert C. Martin, alias Oncle Bob. Cet ouvrage regorge de règles essentielles du développement, classées en catégories : règles générales, tests, commentaires, fonctions, nommage, etc. Chaque règle est associée à un code smell, un indicateur commun de code défaillant.
Dans une approche similaire, consultez le site Refactoring Guru, qui liste également ces code smells.
Même en maîtrisant les bonnes pratiques de Clean Code, vous pouvez rencontrer des situations complexes où ces principes ne suffisent pas. Les design patterns, ou patrons de conception, sont des solutions éprouvées pour résoudre des problèmes spécifiques. Les connaître en profondeur est crucial pour construire des logiciels robustes. Toutefois, une mauvaise application peut complexifier le code et augmenter la dette technique. Utilisez-les judicieusement.
Exemple pratique :
Dans le cas d’une évolution future, nous avons déjà les classes W et X, et nous voulons ajouter les classes Y et Z, définies comme suit :
Plutôt que de copier-coller les méthodes doA
et doC
encore une fois dans mon code, limitant ainsi la maintenabilité, avant de faire l’évolution, je vais appliquer un refactoring vers le pattern decorator.
Inspirée du système de production de Toyota, la méthode des 5S optimise l'organisation du lieu de travail. Adaptée au développement, elle améliore la qualité du code :
Les principes SOLID sont cinq règles fondamentales pour un code orienté objet maintenable :
Le principe DRY (Don't Repeat Yourself) stipule qu'il ne faut pas dupliquer le code. Chaque fonctionnalité doit avoir une implémentation unique. Cela favorise la maintenabilité et réduit les erreurs. Cependant, dans certaines architectures comme les microservices, une duplication contrôlée peut éviter des couplages indésirables.
Le principe KISS (Keep It Simple, Stupid) encourage la simplicité dans le code. Un code simple est plus facile à comprendre, à tester et à maintenir. En somme, KISS est l'essence même du refactoring : rendre le code plus clair et accessible.
Armé de ces connaissances, vous pouvez désormais identifier le mauvais code. Chaque code smell a une ou plusieurs techniques de refactorisation associées.
Avant de commencer, évitez ces erreurs courantes :
Pour un refactoring réussi :
Pour chaque commit :
Pour réaliser un refactoring efficace, il est essentiel de respecter les points clés évoqués précédemment. Commencez par identifier le code smell grâce à vos connaissances, puis appliquez la technique de refactorisation appropriée. Vous pouvez trouver ces techniques détaillées dans le livre Refactoring de Martin Fowler ou sur le site Refactoring Guru.
Exemple pratique :
Imaginons que vous ayez repéré une méthode excessivement longue et complexe. Le corps de cette fonction dépasse 100 lignes, et les structures conditionnelles sont si confuses qu'elles rendent le code difficile à lire et à maintenir.
Voici comment vous pourriez procéder :
if
, then
et else
.En suivant cette démarche structurée, vous améliorerez progressivement la lisibilité et la maintenabilité de votre code sans introduire de bugs. Chaque étape validée par les tests vous assure que le comportement du programme reste inchangé du point de vue de l'utilisateur, tout en facilitant les développements futurs.
La plupart des IDE offrent des fonctionnalités de refactorisation automatisées (extraction de méthode, renommage, etc.). Utilisez-les pour gagner du temps et réduire les erreurs.
Pour les refactorings trop complexes pour être traités comme expliqué précédemment, la méthode Mikado est une approche efficace. Chaque bâton représente une partie du code que vous voulez modifier. Si vous essayez de tout changer en une seule fois, vous risquez de tout faire tomber, c'est-à-dire de provoquer des bugs ou de casser des fonctionnalités.
En résolvant les dépendances une par une, récusivement, vous atteindrez votre objectif sans casser d’autres éléments du code, rendant le refactoring plus sûr et contrôlé. Cette approche est particulièrement utile pour les refactorings à grande échelle. Il est conseillé de construire un Mikado-graph pour suivre toutes vos dépendances.
Exemple pratique :
Pour mieux comprendre la méthode Mikado, prenons une analogie avec nos héros bien connus, Han Solo et Chewbacca, qui cherchent à améliorer le Faucon Millenium pour échapper plus rapidement à l'Empire.
Jour 1 : L'achat de l'hyperpropulseur
Han décide d'acheter un nouvel hyperpropulseur pour augmenter la vitesse du vaisseau. Plein d'enthousiasme, il enlève l'ancien et installe le nouveau. Mais lorsqu'il tente de le connecter, il se rend compte que les câbles existants ne sont pas compatibles avec ce nouvel équipement.
Conscient du problème, Han remet l'ancien hyperpropulseur en place pour que le Faucon Millenium reste opérationnel. Il réalise qu'il doit d'abord s'attaquer au remplacement des câbles avant de pouvoir installer le nouvel hyperpropulseur.
Jour 2 : Le remplacement des câbles
Le lendemain, Chewbacca se charge d'installer de nouveaux câbles adaptés. Ils lancent une vérification via l'ordinateur de bord, mais une nouvelle erreur apparaît : l'alimentation électrique du vaisseau doit être reprogrammée pour supporter ces câbles récents. Plutôt que de laisser le vaisseau dans un état instable, Chewbacca remet les anciens câbles en place, gardant ainsi le Faucon Millenium prêt à décoller en cas d'urgence.
Jour 3 : La reprogrammation et l'installation finale
Le troisième jour, Han et Chewbacca décident de reprogrammer l'alimentation électrique pour qu'elle soit compatible avec les nouveaux câbles. Une fois cette étape réalisée, ils installent les câbles et replacent le nouvel hyperpropulseur. Cette fois-ci, tous les systèmes fonctionnent parfaitement. Le Faucon Millenium est désormais plus rapide et prêt à semer l'Empire à tout moment.
Il existe d'autres techniques, comme l'utilisation de patterns Proxy ou Facade pour isoler le code en cours de refactorisation. Cependant, ces méthodes peuvent ajouter de la complexité et potentiellement introduire des bugs. En étant plus permissives, elles laissent la possibilité de laisser échapper une erreur. Il est généralement préférable de s'en tenir aux techniques éprouvées.
Vous avez compris d’où vient la dette technique grâce au premier article de cette série. Vous avez maintenant une compréhension approfondie des méthodes pour refactoriser sans introduire de bugs grâce à ce dernier. Le défi restant est de trouver le temps pour le faire. Si les petits refactorings peuvent s'intégrer aisément dans votre flux de travail, les plus importants nécessitent une planification et une communication avec votre équipe ou vos clients. Dans le prochain et dernier article de cette série, nous explorerons comment démontrer au reste de l’équipe que l**'investissement dans la qualité du code est bénéfique pour tous**.