La refactorisation est un exercice qui devrait être maîtrisé par tous les développeurs, encadré par tous les chefs de projets et encouragé par tous les directeurs techniques.
Le refactoring, qu'est-ce que c'est ?
Derrière cet affreux anglicisme se cache le fait de réécrire du code qui a déjà été développé. Le but n'est donc pas d'ajouter de nouvelles fonctionnalités, mais plutôt d'assurer un suivi de l'existant, de faire un ménage qui en facilitera la maintenance.
Nous nous sommes tous retrouvés un jour ou l'autre devant des bouts de code sans queue ni tête, manifestement écrits à la va-vite, ne respectant aucune norme, avec une documentation périmée, sans commentaire, ou truffés de code mort. À chaque fois, nous sommes horrifiés. Dans le meilleur des cas, on se souvient des raisons historiques qui ont conduit à cela (« Ah oui, ça date du projet X, qu'on avait dû faire à toute vitesse il y a 2 ans ») ; dans le pire des cas, on va retrouver le responsable de cette horreur pour lui passer un savon.
Mais la bonne attitude, c'est d'organiser l'amélioration de ce code. Il faut garder en tête qu'on ne travaille pas continuellement sur les mêmes fichiers. Ce qu'on est en train de développer un jour sera utilisé pendant des mois voire des années sans qu'on revienne dessus. Mais au fil du temps, le code ancien devient « friable » : chaque correction de bug devient plus délicate et sujette aux bugs de régression ; chaque ajout de fonctionnalité prend de plus en plus de temps et devient plus difficile.
Je vais faire un parallèle avec la construction immobilière. Quand on construit une maison, on commence par faire les fondations, puis on monte les murs extérieurs, puis le toit et enfin les cloisons intérieures. Quand on développe un logiciel, c'est un peu la même chose ; chaque développement, chaque ajout de fonctionnalité, s'appuie sur des objets ou des librairies qui doivent rester fiables dans le temps. Il faut donc pouvoir revenir à tout moment sur n'importe quel bout de code, accéder à sa documentation, lui ajouter de nouvelles capacités, voire résoudre des bugs qui ne s'étaient encore jamais déclarés.
Parce que le jour où vous faites tomber des cloisons, vous ne devez pas devoir refaire les murs extérieurs ; et si vous ajoutez une étage à votre maison, vous devez pouvoir faire confiance à la chape de béton de vos fondations.
Comment s'y prendre
On peut découper un refactoring en 4 étapes précises. Les trois premières sont importantes et nécessaires, alors que la dernière est à mettre en oeuvre si le besoin s'en fait sentir uniquement.
- Élimination du code mort. Si un objet contient des méthodes qui ne sont plus utilisées depuis longtemps, cela veut dire que ces méthodes ne sont plus tenues à jour à cause de leur inutilité. La crainte d'avoir un jour besoin de ces méthodes pousse à les garder en place, mais le manque de maintenance du code fera prendre un risque considérable le jour où on voudra l'utiliser. De plus, les outils de gestion de source (type Subversion) permettent justement de retrouver les anciennes versions d'un fichier. Alors autant effacer immédiatement le code inutile. Cette remarque s'applique aussi aux blocs de code qui sont laissés en commentaire depuis la nuit des temps, et que personne n'ose toucher « au cas où on en aurait besoin ».
- Normalisation et documentation du code. Le code restant, celui qui est réellement utilisé, doit ensuite être nettoyé pour le rendre compréhensible de nouveau. Si vous avez mis en place des normes de codage, assurez-vous qu'elles soient appliquées. Reprenez la documentation "externe" (celle qui permet d'utiliser ce code) ; assurez-vous que les paramètres et les valeurs de retours correspondent bien à ceux présents dans le code ; vérifiez que les explications fournies sont toujours exactes au regard du code ; rappelez-vous que la documentation doit dire à quoi sert le code, ce qu'il fait, et non comment il le fait. Enfin, ajoutez des commentaires aux endroits les plus complexes du code, pour expliquer comment il fonctionne.
- Renommage et harmonisation. Vous aviez un petit objet nommé SimpleUserManager, mais qui contient maintenant 80 méthodes ? Ou un autre a été rapidement nommé GlobalApplicationProxy ? Prenez le temps de les renommer intelligemment. Faites de même avec les méthodes, qui devraient idéalement se comprendre rien que par leur nom, sans nécessiter de documentation. S'il le faut, reprenez aussi l'ordre des paramètres pour qu'ils restent logiques d'une méthode à l'autre. Le mot d'ordre doit être de créer une harmonie à travers le code, pour simplifier sa maintenance future.
- Optimisation. Si et seulement si les étapes précédentes ont été réalisées sérieusement, vous pouvez procéder à l'amélioration du code pour des raisons de performance. Cela peut concerner un algorithme un peu délicat, ou des requêtes complexes sur une base de données. Mais souvenez-vous que les optimisations doivent toujours se faire en dernier, à l'unique condition que le périmètre applicatif soit connu et bien défini. Il est déjà assez risqué d'optimiser un système sans le mettre en vrac, ne vous lancez surtout pas dans une optimisation mélangée avec des modifications fonctionnelles, vous iriez droit dans le mur.
Quand faire une refactorisation
C'est la grande question habituelle. Faut-il vraiment ralentir les développements en prenant du temps à s'embêter avec ça ? La plupart des jeunes développeurs ont tendance à vouloir foncer dans leurs développements, ajouter sans cesse de nouvelles fonctions, sans jamais se retourner. Si on leur parle de refactorisation, ils admettent la nécessité de la chose, mais préfèrent toujours remettre à plus tard.
En fait, il faut comprendre que le calcul est simple. Plus tôt le refactoring intervient, moins il est coûteux en temps.
Chaque période de développement (par exemple un sprint si vous utilisez la méthode Scrum) devrait être immédiatement suivie d'un temps de refactorisation, qui servira à consolider le code qui vient juste d'être réalisé, le débarrasser de ses scories et s'assurer que la documentation qui l'accompagne est complète.
Si on schématise un peu, on peut imaginer qu'un mois de développement ne prendra qu'une seule journée à être mis au propre si c'est fait dans la foulée. Ainsi, au bout d'une année de codage, c'est à peine 12 jours qui auront été consacrés à la refactorisation.
Par contre, je peux vous dire d'expérience que si vous attendez la fin de l'année de développement avant de faire le grand ménage, vous y passerez au minimum un mois entier. Non seulement le coût global sera bien plus élevé, mais vous prenez le risque d'interrompre un jour tous vos projets pendant un mois entier !
A contrario, dépenser une simple journée par mois est assez indolore en terme de coût et de planning.
Après, c'est à vous d'être responsable et consciencieux. Vous aurez sûrement à convaincre vos collègues du bien-fondé de cette pratique. Si vous n'avez pas de chance, vous aurez même à l'expliquer à vos supérieurs. Mais tenez bon, c'est à ce prix – minime – que vous préparerez un futur serein.