accueil / blog / Refonte ou refactoring

Refonte ou refactoring : comment choisir pour votre application ?

Améliorer le code existant ou le remplacer ? Les définitions, les critères qui font pencher la balance, un tableau de décision et une voie du milieu : la refonte progressive.

Adrien Le Breton Lecture 6 min

En bref : le refactoring améliore la structure du code sans changer ce que fait l'application ; la refonte remplace tout ou partie du système. En production, on commence presque toujours par poser des tests et refactorer, puis on remplace module par module. La réécriture complète se justifie quand la technologie ou le modèle de données ne tiennent plus.

Refonte, refactoring : de quoi parle-t-on ?

Le refactoring modifie la structure interne du code sans changer son comportement. Martin Fowler, qui a popularisé le terme, le définit comme « une technique disciplinée pour restructurer un code existant, en modifiant sa structure interne sans changer son comportement externe » (refactoring.com). Renommer, découper une fonction trop longue, supprimer une duplication, isoler une dépendance : l'utilisateur ne voit aucune différence, et l'équipe gagne du temps sur chaque évolution suivante.

La refonte remplace tout ou partie du système : nouvelle architecture, nouveau framework, parfois nouveau modèle de données. Elle peut se faire d'un bloc, c'est la réécriture complète (le « big bang »), ou par morceaux, c'est la refonte progressive.

Les deux questions se posent le plus souvent sur une application dite legacy. Michael Feathers en donne une définition utile dans Working Effectively with Legacy Code : du code sans tests (une analyse de cette définition). L'âge compte peu. Sans tests, chaque changement est un pari, qu'on refactore ou qu'on réécrive.

Qu'est-ce que la dette technique, et comment la repérer ?

La dette technique désigne ce qui, dans le code, rend les changements plus coûteux qu'ils ne devraient l'être : raccourcis pris pour livrer vite, dépendances vieillies, structure qui ne correspond plus au métier. La métaphore vient de Ward Cunningham, et Martin Fowler en résume le mécanisme : l'effort supplémentaire qu'il faut fournir pour ajouter une fonctionnalité, ce sont les intérêts de la dette (TechnicalDebt). Un peu de dette est normal, et parfois rentable quand il faut tester une idée vite. Le problème commence quand les intérêts mangent le temps prévu pour les nouveautés.

Quelques indices suffisent souvent pour la repérer. Une petite évolution prend nettement plus de temps qu'il y a deux ans. Une bonne partie des bugs sont des régressions. Certains fichiers sont modifiés à chaque livraison, mais personne n'ose les retoucher en profondeur. Ces zones, en général peu testées, sont les premières candidates au refactoring.

Quels critères doivent guider votre choix ?

La dette technique est-elle localisée ?

Si les bugs et les lenteurs se concentrent dans deux ou trois modules, refactorer ces modules coûte moins cher que tout remplacer. Si chaque partie du code dépend de toutes les autres, chaque correction en déclenche une autre, et le refactoring avance trop lentement pour suivre le rythme des demandes.

Quelle est la couverture de tests ?

Des tests automatisés sur les parcours critiques rendent le refactoring sûr : on change la structure, les tests confirment que le comportement n'a pas bougé. Sans tests, la première étape est la même quel que soit le choix : écrire des tests qui figent le comportement actuel. Une réécriture lancée sans ce filet perd en route des règles métier que personne n'avait documentées.

La technologie est-elle encore maintenue ?

Un framework maintenu se met à jour par étapes. Un framework abandonné change la donne : AngularJS, par exemple, n'est plus maintenu depuis janvier 2022 (annonce officielle). Ses failles de sécurité ne sont plus corrigées, et chaque mois qui passe rend la sortie plus chère. Ce cas pousse vers la refonte, de préférence progressive.

Qui saura maintenir le système ?

Si personne, ni dans l'équipe ni sur le marché, ne connaît plus la stack, refactorer revient à investir dans une impasse. À l'inverse, changer de technologie pour suivre la mode coûte cher pour un gain mince. Migrer une application Angular saine vers React « parce que tout le monde le fait », par exemple, impose une réécriture certaine pour un gain qui reste théorique.

Quel est le risque pour l'activité ?

Une application qui porte la facturation ou la relation client ne peut pas s'arrêter. Une réécriture complète oblige souvent à geler les évolutions pendant des mois, ou à faire vivre deux systèmes en parallèle. Joel Spolsky a qualifié la décision de Netscape de réécrire son navigateur à partir de zéro de « pire erreur stratégique » qu'un éditeur de logiciel puisse commettre (Things You Should Never Do, Part I, 2000). Plus le risque métier est élevé, plus l'approche progressive s'impose.

Refonte ou refactoring : que dit le tableau de décision ?

Critère Plutôt refactoring Plutôt refonte
Dette technique Concentrée dans quelques modules Partout : chaque correction en casse une autre
Tests Parcours critiques couverts, ou faciles à couvrir Impossibles à poser sans réorganiser le code
Technologie Maintenue, mises à jour possibles Abandonnée, plus de correctifs de sécurité
Compétences L'équipe connaît la stack, on recrute dessus Plus personne ne la maîtrise
Modèle de données Adapté au métier actuel Contourné partout, il ne reflète plus le métier
Risque métier Arrêt impossible, évolutions continues Périmètre isolable, ou métier qui change en profondeur
Besoin fonctionnel Le produit fait ce qu'il faut, mais lentement Le produit doit faire autre chose : mobile, hors-ligne, multi-entreprises

Si la plupart des lignes penchent à droite, une refonte se justifie. Elle n'a pas besoin d'être faite d'un bloc pour autant.

Comment remplacer une application sans l'arrêter ?

La refonte progressive consiste à remplacer une application ancienne morceau par morceau, en la laissant en production pendant toute la transition. Martin Fowler l'a baptisée « strangler fig », du nom d'un figuier qui pousse autour d'un arbre hôte jusqu'à prendre sa place (StranglerFigApplication). Concrètement, on place une couche de routage (un proxy ou une passerelle d'API) devant l'ancien système. Chaque fonction nouvelle ou réécrite est servie par le nouveau code, le reste continue de passer par l'ancien. Module après module, le trafic bascule, puis l'ancien code devenu inutile est supprimé. Pour Fowler, l'intérêt est que l'investissement et les bénéfices arrivent progressivement et de façon visible, au lieu d'un unique pari à la fin. Le prix à payer : du code de transition pour faire cohabiter les deux systèmes, et une vigilance particulière sur les données qu'ils partagent.

Pour démarrer, choisissez un module utile mais peu risqué, aux frontières nettes. Remettez-le sous tests, construisez son remplaçant, basculez le trafic, observez, puis supprimez l'ancien. Ce premier module sert de rodage : il permet de mettre au point la chaîne de déploiement et la synchronisation des données avant d'attaquer les parties sensibles.

Quels signaux d'alerte surveiller ?

Côté application, ces signaux doivent vous faire réagir :

  • Une évolution simple prend des semaines.
  • Corriger un bug en crée un autre ailleurs.
  • Un module est devenu intouchable, ou une seule personne sait comment il marche.
  • Les dépendances ne sont plus maintenues ou portent des failles connues.
  • Chaque mise en production est manuelle et redoutée.
  • Vous peinez à recruter sur la stack.

Pendant une refonte, d'autres signaux montrent qu'elle dérape : la date de bascule recule à chaque comité, l'ancien système reçoit encore des évolutions qu'il faudra reporter dans le nouveau, et après plusieurs mois aucun utilisateur n'a touché le nouveau code. Dans ce cas, revenez à des livraisons plus petites.

Pourquoi faire un audit avant de trancher ?

Le choix entre refonte et refactoring repose sur des faits que personne n'a sous les yeux au départ : où se concentre la dette, quelle part du code est testée, quelles versions tournent encore, quelles règles métier ne sont écrites nulle part. Un audit court, en général de 3 à 5 jours, sert à les établir. L'auditeur lit le code, échange avec l'équipe qui le maintient, vérifie l'état des dépendances et repère ce qui ralentit les évolutions. Il en sort une carte de la dette et un plan d'action chiffré, qui compare les options sur la même base : refactorer telle zone, remplacer tel module, ou réécrire. Sans cet état des lieux, la décision repose sur des impressions, et les impressions poussent souvent à tout refaire. À l'échelle d'un chantier de plusieurs mois, ces quelques jours pèsent peu.

C'est le principe de notre audit avant de trancher. Pour les budgets, nous détaillons les ordres de grandeur dans l'article sur le coût d'une refonte d'application legacy. Et si votre existant est un prototype récent, la logique est la même, avec ses propres pièges : voyez comment reprendre un MVP généré par IA.

Et chez Neodev ?

La refonte d'application legacy fait partie de nos métiers. Nous commençons par un audit court, puis nous avançons par incréments réversibles, couverts par des tests de non-régression, avec un système qui reste en production à chaque étape. Votre équipe récupère la documentation et la connaissance au passage.

Décrivez-nous votre application : nous répondons sous 24 h, et le premier appel est offert.

Adrien Le Breton a fondé Neodev et pilote le collectif. Il fait du conseil et du développement logiciel depuis 2018, dont plus de deux ans en direction technique. En savoir plus sur l'auteur.

Votre application vous freine ?

Un audit court vous dit s'il faut refactorer, remplacer par étapes ou réécrire, avec un plan chiffré.

Réserver un appel