Reprendre une base de code existante en évitant toute régression
Une méthode issue de reprises réelles (une plateforme B2B en production depuis plusieurs années, un e-commerce refait à l’identique, une DXP repartie d’une base existante) : comprendre avant de modifier, sécuriser chaque mise en production, documenter et remettre à neuf par petites étapes validées avec les parties prenantes.
Arriver sur une base de code existante est la situation la plus courante d’un développeur, et la moins racontée. J’en ai repris plusieurs : une plateforme B2B en production depuis 2021 dont j’étais le développeur référent, un e-commerce refait à l’identique sur une API maintenue par un prestataire externe et une DXP repartie d’une base écrite par d’anciens collègues. J’en ai tiré une méthode pour faire évoluer un code en production sans régression.
Par où commencer quand on arrive sur un code existant ?
Par l’architecture, et par les gens qui la connaissent. Avant d’ouvrir un fichier au hasard, je demande à l’équipe comment le système est découpé, quelles applications et quelles bases de données il comprend, de quels services tiers il dépend et qui appelle qui. Un schéma griffonné en dix minutes avec quelqu’un qui connaît le projet vaut des heures de lecture de code.
Je lance ensuite le projet pour m’en servir comme un utilisateur. Il ne s’agit pas encore de juger le code, mais de comprendre le produit :
- à quoi sert l’application, et pour qui ;
- quels parcours utilisateurs il faut préserver, et lesquels sont critiques ;
- quels sont les enjeux métier, ce qui rapporte, ce qui coûte et ce que le client surveille ;
- pourquoi les choses sont faites ainsi, quand la raison n’a rien d’évident.
Vient alors la lecture du code, en suivant ces mêmes parcours de l’écran jusqu’à l’API, puis jusqu’à la base. C’est le moyen le plus rapide de relier ce que voit l’utilisateur à ce qu’il faudra modifier. En parallèle, je teste, et chaque comportement surprenant devient une question à poser à l’équipe au lieu d’une hypothèse.
Comment repérer ce qui coûte cher ?
Aucune base de code n’est uniforme. Les points de friction se concentrent à quelques endroits, qu’il s’agit de trouver. Sur la plateforme B2B que j’ai reprise, ils relevaient de cinq ordres :
- des couches successives, écrites à des époques et dans des styles différents ;
- un outil choisi pour d’autres besoins, devenu plus lourd que nécessaire ;
- des composants React encore écrits sous forme de classes, antérieurs aux hooks ;
- des anomalies à traiter au fil de l’eau ;
- des pages lentes, à commencer par une bibliothèque de documents.
Le bon critère n’est pas la beauté du code, mais ce qu’il coûte à chaque évolution. Un module vieillissant que personne ne touche peut attendre, alors qu’un module central qui ralentit chaque livraison passe en premier.
Comment préserver l’existant lorsqu’aucun test ne permet de le garantir ?
On entend souvent qu’on ne peut pas refactoriser sans tests. L’absence de tests automatisés ne dispense pourtant pas d’un filet de sécurité ; elle oblige à le construire autrement, sans jamais faire l’impasse dessus :
- plusieurs environnements, dont un staging identique à la production, où chaque modification est vérifiée avant de partir ;
- une recette manuelle qui rejoue tous les parcours et non le seul qui a changé, car c’est souvent l’écran voisin qui casse ;
- une seconde paire d’yeux avant chaque grosse mise en production, le chef de projet ou le lead dev repassant sur les parcours critiques ;
- des changements petits et fréquents, bien plus faciles à vérifier qu’une grosse livraison ;
- une documentation et des specs à jour, le minimum du minimum, sans lesquelles il ne reste aucune trace de ce qui a été décidé, ni pourquoi.
Ce filet permet de livrer en confiance, et c’est aussi lui qui rassure le client. Quand un projet permet d’ajouter des tests automatisés, c’est encore mieux. Sur la refonte d’un back-office chez Meilleurtaux Assurances, au sein d’une équipe de six développeurs, les tests unitaires avec React Testing Library, la code review et le pair programming faisaient partie du quotidien.
Comment obtenir le temps d’améliorer le code ?
Personne ne paie pour « refactoriser », mais tout le monde paie pour des évolutions plus rapides et des pages qui chargent vite. Sur la plateforme B2B, j’ai donc proposé au chef de projet et au client, qui l’ont validé, de réserver une part du temps de TMA (tierce maintenance applicative) à la qualité du code et aux temps de chargement, en plus des corrections.
Le reste se fait au fil de l’eau. Chaque évolution devient l’occasion d’assainir la partie qu’elle touche, dans le périmètre convenu avec le chef de projet et le client. Le code s’améliore ainsi là où il vit, sans projet dédié, sans gel des livraisons et sans surprise pour personne.
Quand remplacer un outil au lieu de le maintenir ?
Un outil se remplace quand le statu quo coûte plus cher que le changement. C’est la question à poser, chiffres à l’appui, à l’équipe et au client, et la décision leur appartient : un remplacement se propose et se valide, il ne s’impose pas.
Sur la plateforme B2B, ce raisonnement a conduit à retirer Redux au profit de quelques contextes React et de l’état local, comme le raconte l’article sur la modernisation de SRS. À aucun moment l’application n’a été figée.
Refaire à l’identique : comment garantir la parité ?
Une refonte à fonctionnalités identiques est un cas à part, où l’objectif est de reproduire fidèlement et non d’inventer. Sur l’e-commerce Cora, le nouveau site web devait reprendre les parcours des applications mobiles iOS et Android, sur une API maintenue par un prestataire externe. La parité reposait sur quatre éléments :
- les maquettes, comme référence commune ;
- des échanges réguliers avec les équipes mobiles, qui connaissaient chaque règle métier ;
- la comparaison avec les applications existantes ;
- des variables de style identiques à celles de Figma, pour que couleurs, espacements et typographies ne dérivent pas.
Et quand l’équipe d’origine n’est plus là ?
Tout ce qui précède suppose de pouvoir poser des questions. Sur la DXP Goodbizz, ce n’était pas possible, car le projet partait d’une base écrite par d’anciens collègues, mise en pause depuis, et l’équipe qui l’avait commencée n’était plus là pour en parler.
Avec le lead dev et le chef de projet, nous avons donc évalué au fil du développement ce qu’il fallait garder, améliorer ou supprimer. Plus qu’à relire, le code était à trier entre :
- des modules qui n’étaient plus à l’ordre du jour ;
- des modules commencés mais jamais terminés ;
- des modules attendus qui n’existaient pas encore.
Dans cette situation, l’apprentissage de la base de code fait partie du chantier dès le premier jour, et chaque choix technique se prend sans l’historique qui l’expliquerait. Le code devient la seule documentation, qu’on lit pour comprendre l’intention et dont on vérifie le comportement en le testant. C’est aussi la meilleure démonstration qu’une documentation et des specs à jour sont un minimum : sans elles, l’équipe suivante se retrouve à son tour sans aucune trace des décisions.
Faut-il tout réécrire ?
Je ne propose jamais de réécrire entièrement une application en production. La réécriture totale promet un code propre, mais elle gèle les évolutions, perd au passage des règles métier que personne n’avait documentées et livre d’un bloc ce qu’il aurait fallu vérifier petit à petit.
Je propose plutôt une mise à neuf, qui remet les bonnes pratiques en place, aligne le code sur les conventions du projet et met les dépendances à jour. Elle avance toujours par petits morceaux, en accord avec toutes les parties prenantes (équipe, lead dev, chef de projet, client), jamais sur un coup de tête. Sur un projet de grand compte, un développeur qui réécrit de son côté fait perdre la confiance qu’il cherche à gagner, quand un développeur qui propose, chiffre et livre par étapes la renforce. Même sur la DXP Goodbizz, où il a fallu trier ce qui restait, nous sommes repartis de la base existante, et j’ai repris plusieurs modules sur le code en place au lieu de les réécrire de zéro.