Le refacto, quand, comment et pourquoi le faire?
Le refacto est une phase de mon activité de développement que j'ai trop longtemps laissée au second plan. Aujourd'hui, cette tâche est devenue le pilier central de toute ma pratique.
Quels sont les meilleurs moments pour procéder à un peu de refacto et quelles méthodes. Vous comprendrez ainsi pourquoi je choisis d'investir autant de temps et d'énergie dedans? C'est parti!
Quand? Après avoir codé une feature, parce que TDD!
Pour les pratiquants du TDD, la boucle red/green/refactor n'a de sens que si on met suffisamment d'effort sur le 3ème point qu'est la phase de refacto.
Pour ma part, il est clair que faire une relecture du code une fois que les tests sont verts est un minimum: vérifier les typos, indenter proprement, renommer de manière pertinente, supprimer le code mort, mutualiser ce qui pourrait l'être, encapsuler ce qui doit l'être,... Les chantiers sont légions et ne manquent pas!
Quand? Avant de coder une feature
Si la passe de refacto inhérente à la méthodologie TDD me semble indispensable, passer par une phase de refacto avant de démarrer une feature est devenu un rituel que je refuse d'éluder aujourd'hui.
Pourquoi faire ça avant de démarrer une nouvelle fonctionnalité? Déjà parce que mes refactos "à chaud" produits à la fin du dev précédent sont rarement satisfaisants. En fin de feature, on manque généralement de recul sur ce qu'on vient de coder et on peut être moins pertinent dans notre auto-critique. Ce n'est pas forcément le cas pour tout le monde, mais c'est clairement le mien. Ensuite, en fin de dev, on n'a pas non plus encore un recul sur les futures évolutions et sur la forme que va prendre le code à l'avenir. En démarrant une nouvelle feature on a justement une bonne vision de l'évolution métier et il convient de préparer au mieux le code pour qu'il puisse accueillir tous les changements à venir.
Tel l'artisan qui fait place nette dans son atelier avant de démarrer un nouveau projet, chaque dev devrait selon moi mettre son code au propre avant de démarrer une feature. C'est d'une part le meilleur moyen de juguler les écueils techniques avant de tomber dessus; si je sais d'avance les zones de code qui vont être impactées, je peux déjà encapsuler certaines briques techniques ou certains comportement afin de qu'ils ne me dérangent pas par la suite. D'autre part, en pratique quotidienne, ça devient mon meilleur allié pour prévoir au plus tôt les débordements éventuels s'il y a eu chiffrage/engagement. Enfin, pour boucler la comparaison, on n'aurait pas idée de cuisiner sur un plan de travail plein de miettes et de morceaux du plat de la veille, ni même de faire cuire dans une casserole où sont restés accrochés des bouts de tomate séchés que le lave-vaisselle n'a pas réussi à faire partir! Keep It Sanitized Stupid!
Comment?
Maintenant que je vous ai convaincu de la pertinence de faire du refacto, comment je m'y prends? Alors déjà ça dépend du contexte: legacy vs green field, pré/post feature, état du planning, ...
D'une manière générale, avant de coder, j'essaie d'avoir un minimum de notions métiers. Ensuite, j'essaie de faire en sorte de retrouver au moins le vocabulaire métier dans mon code (#DDD #UbiquitousLanguage). Pour ce faire, je renomme les méthodes/variables/classes afin d'y voir un peu plus clair.
Ensuite, j'apprécie de pouvoir parcourir du code dans trop me perdre, ni me faire saigner les yeux. Je vais donc faire une passe de lisibilité : indentation, déplacement/regroupement de fonctions liées entre elles... Sur des gros legacy un peu trop fat, je vais même parfois jouer avec les classes partial pour éclater du code en plusieurs fichiers. Ces déplacements sont généralement temporaires, le temps de procéder à un refacto plus en profondeur par la suite. Dans ces cas-là, commencer simple sur le plus petit périmètre possible.
A l'inverse du regroupement, je vais parfois vouloir extraire du comportement, l'encapsuler afin de l'identifier, le reconnaitre et le mettre en évidence. Cette activité va être liée aux 2 précédentes : si je sais nommer le comportement que je veux extraire, tout se passera bien. En revanche, si j'ai du mal à trouver un nom, c'est que j'ai probablement une carence en connaissance fonctionnelle ou que le périmètre que j'ai choisi n'est pas le bon. C'est en tout cas un smell à prendre en compte.
At last but not least, le couplage. Le couplage est la plaie invisible de tout développement. Ignorez le couplage(sans cohésion), c'est décupler sa charge de travail dans un futur très proche. C'est aussi laisser la porte ouverte à de multiples régressions, qu'il s'agisse de couplage entre les tests et les implémentations, ou entre différentes couches de code. Si ce couplage technique reflète un couplage métier il peut être raisonnable de le laisser tel quel. Dans le cas contraire, il conviendra de limiter les dépendances inutiles: encapsuler, inverser les dépendances, générer un code métier pure sans effet de bord, etc. Je reviendrai peut-être sur certains de ces points dans un futur article.
Pourquoi faire tout ça?(c'est de la surqualité non?)
J'ai déjà exprimé quelques raisons qui me poussent à faire régulièrement des sessions de refacto à différents moments. Que ce soit un travail préparatif ou une façon de "nettoyer derrière moi", il m’apparait de plus en plus crucial d'intégrer cette phase le plus régulièrement possible. Je ne conçois plus de coder sans faire de refacto à chaque étape de développement.
Ce que je n'aime pas en revanche, c'est faire d'énormes sessions de refacto pur en mode "j'ai de la dette à rembourser, je passe ma semaine/mon mois là-dessus". Même si cela peut parfois faire du bien de mettre un bon coup de pied dans la fourmilière, cette façon de procéder est trop décorrélée du métier. C'est pour moi le meilleur moyen de passer à côté de notre travail en se laissant guider par des exigences purement techniques. Qu'un code soit moche et difficilement maintenable n'est pas problématique en soit tant qu'il n'a pas à évoluer. C'est la raison pour laquelle j'aime préparer le terrain avant d'entrer dans une nouvelle feature. Et c'est aussi parce que je ne sais pas forcément comment devra évoluer le code à l'avenir, que je tâche de limiter un peu mes refacto post-dev, afin de ne pas passer trop de temps à produire du code que j'aurai plus de mal à faire évoluer.(les CP aime ce #pragmatisme dont je sais faire preuve )
Enfin, il reste un élément très personnel que j'ai avec mes refactos, c'est l'autosatisfaction que ce travail peut procurer. Quoi de plus agréable que de partir d'une vieille flaque de boue pour en faire émerger un merveilleux parterre fleuri? Arriver à un état de code qui permettra de voir venir la suite de manière sereine, sans rush ni grosse sueur est une sensation particulièrement agréable.
Le refacto, c'est la vie!