Refactoring :
changer sans casser.
Refactoring:
change without breaking.
Refactorer, c'est modifier la structure interne d'un code sans changer ce qu'il fait de l'extérieur. La discipline est là : si le comportement change, ce n'est pas du refactoring, c'est une modification — et il faut la traiter comme telle.
La définition stricte
Le refactoring est une transformation du code source qui améliore sa structure interne sans modifier son comportement observable. Martin Fowler, qui a codifié le terme, insiste sur ce point : la fonctionnalité vue de l'extérieur reste exactement identique.
Cette précision n'est pas de la pédanterie. Elle a une conséquence pratique énorme : un refactoring est vérifiable. Si vos tests passaient avant et passent après, la transformation est correcte. C'est ce qui rend l'opération sûre — et ce qui explique pourquoi refactorer sans tests automatisés n'est pas du refactoring, c'est du bricolage optimiste.
Pourquoi refactorer
Pas pour l'élégance. Pour le coût. Un code mal structuré ne coûte rien tant que personne n'y touche ; il coûte cher à chaque modification. Le refactoring est un investissement qui se rembourse en vitesse de changement future.
Les déclencheurs légitimes :
- La règle du boy-scout : vous devez modifier un fichier de toute façon, vous le laissez un peu plus propre que vous l'avez trouvé.
- Le refactoring préparatoire : la fonctionnalité demandée serait pénible à ajouter dans la structure actuelle. On réorganise d'abord, on ajoute ensuite. « Pour aller à l'est, il faut parfois d'abord aller au nord. »
- La compréhension : vous relisez un code obscur, vous renommez les variables et extrayez des fonctions au fur et à mesure que vous comprenez. Le refactoring devient votre prise de notes.
- Le nettoyage post-livraison : la solution rapide a été livrée sous pression, on revient rembourser la dette pendant que le contexte est frais.
Les techniques les plus rentables
Extraire une fonction
De loin la plus utilisée. Un bloc de code a besoin d'un commentaire pour être compris ? Ce commentaire est probablement le nom de la fonction que vous devriez extraire. Une fonction de deux cents lignes devient huit fonctions nommées, et le code de haut niveau se lit comme une description du processus.
Renommer
Le refactoring le plus sous-estimé. Un nom exact économise plus de temps de lecture qu'une optimisation économise de temps machine. Si vous devez lire l'implémentation pour savoir ce que fait process(), le nom a échoué.
Remplacer les conditionnelles par du polymorphisme
Le même switch sur le même type revient à six endroits ? Chaque nouveau cas oblige à modifier six fichiers, et on en oubliera un. Le déplacer dans des classes ou des stratégies fait qu'ajouter un cas revient à ajouter un fichier.
Introduire un objet-paramètre
Une fonction à huit arguments dont trois voyagent toujours ensemble ? Ces trois-là forment un concept que le code n'a pas encore nommé.
Séparer requête et commande
Une fonction qui répond à une question ne devrait pas modifier l'état, et inversement. Une fonction qui fait les deux ne peut être appelée ni librement ni testée simplement.
La méthode, en pratique
- Vérifiez le filet. Des tests couvrent-ils le comportement que vous vous apprêtez à déplacer ? Sinon, écrivez-les d'abord — même grossiers, même des tests de caractérisation qui figent le comportement actuel, bugs compris.
- Faites un pas. Une transformation à la fois : extraire une fonction, puis lancer les tests, puis renommer, puis relancer.
- Livrez souvent. Un refactoring qui traîne trois semaines dans une branche produit un conflit de fusion qui coûtera plus cher que le gain.
- Ne mélangez jamais refactoring et changement de comportement dans le même commit. Quand quelque chose casse en production, vous devez pouvoir distinguer les deux.
Refactorer ou réécrire ?
La grande question, et celle où l'on se trompe le plus cher. La réécriture complète est séduisante : la promesse d'un code propre, sans le poids de l'histoire. Elle échoue le plus souvent, pour trois raisons.
- Le vieux code contient des années de correctifs. Chaque bizarrerie que vous voulez supprimer est peut-être un cas limite réel découvert à vos dépens. La réécriture les redécouvre tous, un à un, en production.
- Pendant la réécriture, le produit gèle. Vos concurrents, eux, continuent de livrer.
- Les deux systèmes doivent coexister. Le double maintien coûte plus cher que prévu, toujours.
La réécriture se justifie quand la technologie n'est plus supportée ou sécurisable, quand l'expertise a totalement disparu de l'organisation, ou quand le modèle de données rend impossible ce que le métier demande maintenant.
Entre les deux, il existe une troisième voie, presque toujours la bonne : le motif de l'étrangleur. On place une façade devant l'ancien système, on redirige une fonctionnalité à la fois vers du code neuf, et l'ancien rétrécit jusqu'à disparaître. Vous livrez de la valeur en continu, vous pouvez arrêter à tout moment, et vous n'avez jamais de big bang.
Comment le vendre à une direction
Le problème du refactoring, c'est qu'il ne produit rien de visible. Trois angles qui fonctionnent :
- Ne le demandez pas comme un projet. « Trois mois de refactoring » sera refusé, et devrait l'être. Intégrez-le au travail de fonctionnalité : le coût du nettoyage fait partie du coût de la fonctionnalité.
- Parlez en délais, pas en propreté. « Cette fonctionnalité prend six semaines à cause de la structure actuelle ; deux semaines de réorganisation d'abord et elle en prend deux — et les trois suivantes aussi. »
- Montrez les chiffres. Délai moyen entre un commit et sa mise en production, taux d'échec des changements, temps passé en correction de bugs par rapport au temps passé en fonctionnalités neuves. Ces courbes parlent aux directions.
Questions fréquentes
Refactor, ça veut dire quoi ?
Refactorer signifie modifier la structure interne d'un code sans changer son comportement observable de l'extérieur. Le but est de rendre le code plus facile à comprendre et à modifier, pas d'ajouter ou de corriger une fonctionnalité.
Quelle est la différence entre refactoring et réécriture ?
Le refactoring transforme le code existant par petites étapes sûres, en gardant le système fonctionnel à chaque instant. La réécriture repart de zéro. Le refactoring est presque toujours moins risqué ; la réécriture ne se justifie que si la technologie est morte, l'expertise disparue, ou le modèle de données incompatible avec le besoin actuel.
Peut-on refactorer sans tests automatisés ?
Techniquement oui, en pratique c'est imprudent. Sans tests, rien ne prouve que le comportement n'a pas changé, et le refactoring perd sa propriété essentielle : être vérifiable. La bonne séquence est d'écrire d'abord des tests de caractérisation qui figent le comportement actuel.
Combien de temps consacrer au refactoring ?
Plutôt qu'un pourcentage fixe, intégrez-le au travail courant : chaque fonctionnalité inclut le nettoyage du code qu'elle touche. Les grands chantiers de refactoring isolés sont difficiles à financer, difficiles à finir, et produisent des conflits de fusion coûteux.
À lire ensuite
- Code smells : le catalogue utile
- Dette technique : la mesurer et la rembourser
- Développement logiciel : le cycle complet
- Développement logiciel sur mesure
Parlons de votre projet
Vous avez un système qui vous ralentit et vous ne savez pas s'il faut le réparer ou le remplacer ? C'est exactement le genre de question qu'on tranche en premier. Décrivez-nous la situation.
To refactor is to change a program's internal structure without changing what it does from the outside. That is the whole discipline: if behaviour changes, it is not a refactoring, it is a modification — and it needs to be treated as one.
The strict definition
Refactoring is a transformation of source code that improves its internal structure without altering its observable behaviour. Martin Fowler, who codified the term, is insistent on this: externally visible functionality stays exactly the same.
That precision is not pedantry. It has a large practical consequence: a refactoring is verifiable. If your tests passed before and pass after, the transformation is correct. That is what makes the operation safe — and why refactoring without automated tests is not refactoring, it is optimistic tinkering.
Why refactor
Not for elegance. For cost. Badly structured code costs nothing while nobody touches it; it costs a lot on every change. Refactoring is an investment repaid in future speed of change.
Legitimate triggers:
- The boy scout rule: you have to modify a file anyway, so you leave it slightly cleaner than you found it.
- Preparatory refactoring: the requested feature would be painful to add in the current structure. Reorganise first, add second. "To go east, sometimes you first have to go north."
- Comprehension: you are reading obscure code, and you rename variables and extract functions as understanding arrives. The refactoring becomes your note-taking.
- Post-release cleanup: the quick fix shipped under pressure, and you come back to repay the debt while context is fresh.
The highest-return techniques
Extract function
By far the most used. Does a block of code need a comment to be understood? That comment is probably the name of the function you should extract. A two-hundred-line function becomes eight named functions, and the top-level code reads like a description of the process.
Rename
The most underrated refactoring. An exact name saves more reading time than an optimisation saves machine time. If you have to read the implementation to know what process() does, the name failed.
Replace conditional with polymorphism
The same switch on the same type appearing in six places? Every new case forces six edits, and one will be missed. Moving it into classes or strategies means adding a case is adding a file.
Introduce parameter object
An eight-argument function where three always travel together? Those three are a concept the code has not named yet.
Separate query from modifier
A function that answers a question should not change state, and vice versa. A function doing both can neither be called freely nor tested simply.
The method, in practice
- Check the safety net. Do tests cover the behaviour you are about to move? If not, write them first — even rough characterisation tests that pin current behaviour, bugs included.
- Take one step. One transformation at a time: extract a function, run the tests, rename, run them again.
- Ship often. A refactoring that lives three weeks in a branch produces a merge conflict costing more than the gain.
- Never mix refactoring and behaviour change in the same commit. When something breaks in production, you need to tell the two apart.
Refactor or rewrite?
The big question, and the one where mistakes are most expensive. A full rewrite is seductive: clean code, free of history. It usually fails, for three reasons.
- Old code contains years of fixes. Every oddity you want to delete may be a real edge case discovered the hard way. A rewrite rediscovers all of them, one at a time, in production.
- During the rewrite, the product freezes. Your competitors keep shipping.
- Both systems must coexist. Dual maintenance always costs more than planned.
A rewrite is justified when the technology is unsupported or unsecurable, when the expertise has entirely left the organisation, or when the data model makes what the business now needs impossible.
Between the two there is a third path, almost always the right one: the strangler fig pattern. Put a façade in front of the old system, route one capability at a time to new code, and let the old one shrink until it disappears. You deliver value continuously, you can stop at any point, and there is never a big bang.
How to justify it to leadership
The problem with refactoring is that it produces nothing visible. Three angles that work:
- Do not ask for it as a project. "Three months of refactoring" will be refused, and should be. Fold it into feature work: cleanup cost is part of feature cost.
- Talk in lead times, not cleanliness. "This feature takes six weeks because of the current structure; two weeks of reorganisation first and it takes two — and so do the next three."
- Show the numbers. Lead time from commit to production, change failure rate, time spent fixing bugs versus building new features. Those curves speak to executives.
Frequently asked questions
What does refactor mean?
To refactor means changing a program's internal structure without changing its externally observable behaviour. The goal is to make the code easier to understand and modify, not to add or fix functionality.
What is the difference between refactoring and rewriting?
Refactoring transforms existing code in small safe steps, keeping the system working at every point. A rewrite starts over. Refactoring is almost always less risky; a rewrite is only justified when the technology is dead, the expertise gone, or the data model incompatible with current needs.
Can you refactor without automated tests?
Technically yes, in practice it is reckless. Without tests nothing proves behaviour did not change, and refactoring loses its defining property: verifiability. The right sequence is to first write characterisation tests that pin current behaviour.
How much time should go to refactoring?
Rather than a fixed percentage, fold it into ordinary work: every feature includes cleaning the code it touches. Isolated large refactoring projects are hard to fund, hard to finish, and produce expensive merge conflicts.
Read next
- Code smells: the useful catalogue
- Technical debt: measure it, pay it down
- Software development: the full lifecycle
- Custom software development
Let's talk about your project
Have a system slowing you down and no clear answer on whether to fix it or replace it? That is exactly the question we settle first. Describe the situation.