Skip to Content

Chapitre 11.3 — Refactorer sans casser

⏱️ TL;DR — Refactorer = changer la structure sans changer le comportement. La condition de sécurité : des tests verts avant (le filet) et verts après (la preuve qu’on n’a rien cassé). Sur du code non testé, on écrit d’abord des tests de caractérisation (qui figent le comportement actuel), puis on refactore. On procède par petits pas vérifiés, et on traite le refactor comme un chantier cadré et séparé — jamais glissé dans une feature. La skill improve-codebase-architecture aide à repérer quoi refactorer.

🎯 Objectifs

  • Poser la règle « comportement inchangé, tests verts avant/après ».
  • Écrire des tests de caractérisation avant de refactorer du code non testé.
  • Refactorer par petits pas vérifiés.
  • Cadrer le refactor comme un chantier séparé.

La règle d’or : comportement inchangé

Un refactor ne change pas ce que fait le code — il change comment il le fait (structure, lisibilité, découpage). Le test de réussite est donc : le comportement observable est identique. Concrètement : des tests verts avant, la refacto, des tests toujours verts après. Si un test change de résultat, ce n’est plus un refactor — c’est une modification de comportement (à traiter et tester comme telle).

Le filet d’abord : tests de caractérisation

Problème fréquent : le code à refactorer n’est pas testé. Refactorer sans filet, c’est marcher sur un fil sans corde — tu ne sauras pas si tu as cassé quelque chose. La parade : écrire d’abord des tests de caractérisation, qui figent le comportement actuel (même s’il est bizarre) :

« Avant de refactorer computePrice, écris des tests qui capturent son comportement actuel sur des cas représentatifs (y compris les cas limites). Ne corrige rien — documente ce qu’il fait aujourd’hui. »

Ces tests deviennent ton filet : si le refactor change un résultat, ils le signalent. On refactore ensuite, en confiance.

💡 Réflexe d’architecte — « Pas de refactor sans filet. » Si le code visé n’a pas de tests, la première tâche n’est pas de refactorer, c’est d’écrire les tests de caractérisation. L’agent est excellent pour ça (générer des cas à partir du comportement observé). Refactorer du code non testé « à l’œil », c’est parier — et tu perds à long terme.

Par petits pas vérifiés

Un gros refactor en un bloc est difficile à vérifier et à annuler. Découpe (rappel des tranches, 5.5) : chaque petit pas garde les tests verts, chaque pas est committé. Si un pas casse, tu reviens d’un cran, pas de tout le chantier.

1. Extraire la fonction pure → tests verts → commit 2. Remplacer les appels → tests verts → commit 3. Supprimer l'ancien code → tests verts → commit

Les hooks (P7) et le filet git (P5) rendent ce rythme naturel : à chaque pas, npm test (hook Stop) valide, un commit fige. Le refactor avance en sécurité, réversible à tout moment.

Cadrer : un chantier séparé

⚠️ Piège — Le refactor « tant que j’y suis », glissé dans une feature ou une correction de bug. On l’a dit sur le legacy (6.2) : c’est destructeur — tu mêles changement de comportement et changement de structure, et la revue ne peut plus distinguer les deux. Un refactor est un chantier cadré et séparé : sa propre branche, son propre commit, son intention claire (« refactor : extraire la couche de calcul de prix, comportement inchangé »). Feature ou refactor, pas les deux dans le même diff.

Repérer quoi refactorer : improve-codebase-architecture

Savoir refactorer est une chose ; savoir quoi refactorer en est une autre. La skill improve-codebase-architecture scanne un codebase et repère les opportunités (modules trop couplés, interfaces à approfondir, zones peu testables), souvent avec un rapport. Elle transforme « je sens que c’est bancal » en une liste d’améliorations concrètes, que tu traites ensuite comme des chantiers cadrés (filet + petits pas).

🧭 Sur TaskFlow — Si queries.ts et actions.ts d’une feature TaskFlow finissaient par dupliquer de la logique, on lancerait improve-codebase-architecture pour objectiver le problème, puis un chantier de refactor cadré : d’abord les tests de caractérisation (si manquants), puis l’extraction par petits pas, comportement inchangé, tests verts à chaque étape. Jamais mêlé à une feature.

✏️ Exercices

Exercice 1 — Filet avant refactor. Prends une fonction non testée que tu veux refactorer. Fais écrire ses tests de caractérisation d’abord. Puis refactore : les tests restent-ils verts ?

✅ Solution

Les tests de caractérisation figent le comportement actuel ; s’ils restent verts après refactor, tu as la preuve de n’avoir rien cassé. S’ils cassent, tu as changé un comportement (à traiter séparément). Sans ce filet, tu n’aurais eu aucune certitude — juste un espoir.

Exercice 2 — Séparé et cadré. Prends un « tant que j’y suis » que tu allais glisser dans une feature. Sors-le : crée-lui une branche/un commit dédiés, intention « refactor, comportement inchangé ». La revue est-elle plus claire ?

✅ Solution

Un diff « refactor pur » (comportement inchangé, tests verts) est facile à relire : le relecteur vérifie l’équivalence, pas un mélange feature+structure. Séparer les deux rend chaque diff lisible et sûr — et évite d’introduire un bug de comportement sous couvert de « nettoyage ».

🧠 Quiz de révision

1. Qu’est-ce qui définit un refactor réussi ?

Le comportement observable est inchangé : tests verts avant et verts après. Si un test change de résultat, ce n’est plus un refactor mais une modification de comportement.

2. Que faire avant de refactorer du code non testé ?

Écrire des tests de caractérisation qui figent le comportement actuel (le filet). On refactore ensuite, en confiance. Pas de refactor sans filet.

3. Pourquoi procéder par petits pas ?

Parce que chaque pas garde les tests verts et se committe : si un pas casse, on revient d’un cran, pas de tout le chantier. Les hooks + git rendent ce rythme naturel et réversible.

4. Pourquoi ne jamais glisser un refactor dans une feature ?

Parce que mêler changement de structure et de comportement rend le diff illisible et masque les vrais changements. Un refactor est un chantier séparé (branche/commit/intention propres).

5. À quoi sert improve-codebase-architecture ?

À repérer quoi refactorer : elle scanne le codebase et liste des opportunités (couplage, interfaces, testabilité), transformant un « ça semble bancal » en améliorations concrètes à cadrer.


Chapitre suivant : Tests & TDD avec l’IA — faire des tests un moteur, pas une corvée.