J'ai depuis longtemps le livre Working Effectively with Legacy Code de Michael Feathers dans ma bibliothèque. L'idée principale du livre est la suivante : quand on veut faire des changements importants dans du code qui a du vécu, on suit idéalement une espèce de cycle : rendre testable, écrire les tests, refactorer et enfin revérifier. Le livre donne plein de techniques intéressantes pour aider dans ce processus, surtout pour la partie « rendre testable ».
Le mot clé ici est « idéalement ». Et, en pratique, idéalement rime avec rarement. J'y reviens plus loin.
Je me suis amusé à réaliser un petit projet personnel. Un jeu de dés simple qui a fini par prendre la forme d'une PWA en TypeScript et React. Je l'ai faite essentiellement en vibe coding avec un agent IA. La logique du jeu vivait dans des modules séparés et avait ses tests unitaires depuis le début. L'interface usager, elle, tenait dans un seul fichier de plus de deux mille lignes sans aucun test.
À un moment donné j'ai commencé à expérimenter avec un autre agent et un autre modèle IA. J'ai commencé par lui demander de réviser le code du projet pour déterminer si des améliorations étaient souhaitables. Il m'a proposé de découper le module monolithique en plusieurs parties pour éviter qu'il devienne un monstre impossible à maintenir. Repensant à Feathers, j'ai répondu « OK, mais avant, assure-toi que le code est couvert par des unit tests pour pouvoir démontrer l'absence de régression une fois le découpage terminé. »
Il est parti. Il a constaté qu'il n'existait aucune infrastructure de test côté client, choisi un outillage moderne, installé les dépendances, fait la plomberie nécessaire pour rendre l'interface testable. Il a écrit quelques tests d'intégration qui exercent le comportement réel à travers l'interface. Tout est passé au vert. Il a ensuite découpé le fichier en plusieurs composantes, ramenant le fichier principal à huit cents lignes. Il a relancé les tests. Toujours vert. La séquence entière a pris moins de cinq minutes.
Ça m'a laissé bouche bée... (Déjà que je ne parle pas beaucoup !) J'ai travaillé sur du legacy, au sens de Feathers, pendant presque toute ma carrière. La recommandation centrale du livre, soit couvrir le code avant de le refactorer, je la connaissais. Je l'ai appliquée quelques fois, partiellement, quand le contexte le permettait. Mais sur la plupart des refactorings auxquels j'ai participé, ça ne s'est pas passé comme ça. On refactorait sans filet, en s'appuyant sur la connaissance du système, le plus de prudence possible et sur des tests manuels par nos valeureux QA. Le coût d'écrire les tests d'abord, surtout sur du code dont l'architecture résistait à la testabilité, dépassait le budget disponible. Le client ne veut pas payer pour ça. Et écrire ces tests à la main sur du code intriqué, pas quantiquement mais quand même, reste un travail long et ingrat dont on préfère se passer.
Dans mon cas, c'était un petit projet, du code récent et propre, une stack que les agents connaissent très bien. Sur du vrai code legacy, dans un domaine peu documenté, avec une architecture qui résiste à la testabilité, c'est évident que ça ne se déroulerait pas aussi proprement. Mais même partiel, ce que fait l'agent de façon tout à fait autonome change la donne. Les raisons qui empêchaient d'appliquer Feathers ont largement disparu.
La proverbiale game a changé. Maintenant, on a les moyens. L'idéal devient possible, à condition d'y penser.

Discussion des membres