Refactorer une application sans tout casser (retour du Devoxx 2026)
Introduction
Aujourd’hui j’avais envie de quelque chose de différent, il y a quelques jours maintenant, j’ai eu l’occasion d’assister à la 14ème édition du Devoxx (Devoxx France – 14ème édition – du 22 au 24 avril 2026). Pour ceux qui ne connaissent pas, le Devoxx est un événement sur 3 jours ou sont présentées des conférences sur du devops, du développement…
Parmi ces conférences, il y en a une que j’ai particulièrement aimée qui est celle de Héla Ben Khalfallah. Cette conférencière est une ingénieure à OVH et le titre de sa conférence était « Refactorer sans tout casser: anatomie des patterns de modernisation incrémentale – Devoxx France 2026« . J’apprécie particulièrement ce genre de sujets et étant donné que l’on est sur mon blog et que je fais ce que je veux, j’ai décidé d’en faire un article sur ma vision des choses.
Pourquoi refactorer fait peur
Combien de fois a-t-on entendu « à non-surtout on ne touche pas au legacy sinon ça va péter » ou le fameux « tant que ça marche, on n’y touche pas ». En règle général le code legacy fait peur.
D’un côté, il est rare que ce soit une demande client, d’améliorer et refactorer le code d’une application. En conséquences, il y a peu voir pas de budget ou de temps de libérer pour cette tache. Ce qui fait que les moyens ne sont pas optimaux pour mettre en œuvre ce genre de changements. Il ne faut pas oublier que les clients préfèrent ce qui est palpable (la création de nouvelles fonctionnalités ou encore la correction de bugs), il est plus compliqué pour eux de se rendre compte que cela apporte de la valeur sans que cela ne soit visible.
D’un autre côté, le fait de refactorer une application legacy nécessite, un mode d’emploi, des règles et étapes précises, sinon cela devient un enfer pour tout le monde.
Le piège du “on refait tout”
Comme le dit Héla Ben Khalfallah dans sa conférence, il faut à tout prix éviter le « big bang rewrite », cela peut sembler être la meilleure approche de tout modifier en une fois ainsi que la plus rapide… spoiler alerte, ça ne l’est pas.
Voici les raisons pour lesquelles cela échoue :
- trop de changements en une fois,
- introduit un risque important de bugs et de régressions,
- la revue de code est compliquée si on a une « merge request » au lieu de plusieurs petites,
- trop compliqué de déboguer en cas de problème.
Alors cela étant dit, qu’elle serait la méthode à appliquer ?
Le principe clé : la modernisation incrémentale
Lors de cette conférences, 3 méthodes différentes ont été abordées :
- Le Parallel Change,
- Le Branch by Abstraction,
- Le Strangler Fig.
Le Parallel Change (ou Expand / Contract)
Ce principe se découpe en 3 étapes :
La première est « expand », lors de cette étape, on se contente d’implémenter une nouvelle version de notre fonction, rien n’est supprimé, le comportement actuel ne change pas, on a juste une nouvelle version qui cohabite avec l’ancienne.
La seconde est « migrate/transition », c’est à dire, changer l’appel des fonctions actuelles afin d’utiliser la nouvelle version au lieu de l’ancienne. Rien n’est supprimé, on peut toujours passer de l’une à l’autre si besoin de retourner en arrière. Une fois que la nouvelle version est validée alors on passe à la dernière étape.
Enfin, la dernière étape est « contract » est utilisée pour nettoyer la dette. On s’est assurés que la nouvelle version fonctionne correctement et que l’application n’utilise plus les anciennes, il est maintenant temps de supprimer les anciennes avant de passer à la fonctionnalité suivante.

Le Branch by Abstraction
Lors de la conférence, cette partie était découpée en 5 étapes différentes, cependant, je pense que ce principe peut se découper en 3 grandes étapes :
L’état initial est le suivant : notre application appelle notre service actuel directement.
La première étape est « Abstract / migrate », une interface est crée entre notre application et l’ancien service. Au lieu d’appeler notre service directement comme dans l’était initial, nous routons l’appel vers l’abstraction. Aucun changement fonctionnel n’est fait pour le moment.
La seconde étape est « build », on construit une nouvelle implémentation qui utilise notre nouvelle interface. Les deux implémentations cohabitent dans notre application.
Enfin, la dernière étape est « switch / cleanup », comme son nom l’indique, cette étape est simple, on bascule les appels sur la nouvelle implémentation. Une fois que l’on est assurés que cette nouvelle implémentation fonctionne parfaitement, on retire l’ancienne.

Le Strangler Fig
Ce principe se découpe en 4 étapes :
La première étape est « shell/extract », on va introduire le shell devant notre application. Pour le moment il n’y a aucun changement visible. Une fois fait, on va construire notre premier module. Il n’est pas encore relié, mais il existe en parallèle avec son propre pipeline…
La seconde étape est « reroute », on ajoute une nouvelle route vers le nouveau module.
La troisième étape est une boucle « repeat », on extrait les modules un par un en suivant la même logique que précédemment. Tout se fait progressivement.
Enfin, la dernière étape est « remove », la partie legacy est retirée et l’application fonctionne maintenant uniquement sur le nouveau système.
Dans le schéma ci-dessous, les étapes ont été fusionnées ensemble :

Conclusion
Refactorer sans tout casser, n’est pas une question de code, c’est une question de méthode, le plus important n’est pas de modifier le plus d’éléments possibles d’un coup au contraire. Le plus important est de le faire de manière méthodique afin de limiter au maximum les interférences avec la maintenance et le développement de nouvelles fonctionnalités.
Encore une fois, cet article n’est que ce que j’ai retenu et ma version de la conférence , pour ceux que ça intéresse, je vous invite à aller écouter la conférence en replay ou à lire la présentation ici, afin d’avoir une vie plus détaillée et expliquée.


Laisser un commentaire