Moderniser une plateforme B2B React et Node.js
Seul développeur sur une plateforme collaborative B2B en production depuis 2021, j’ai mis en œuvre la modernisation de son code sans interrompre les évolutions : retrait de Redux, composants passés aux hooks, pages plus rapides et droits revus.
SRS fait travailler ensemble les entreprises partenaires d’un grand groupe français de la distribution. Elles y partagent des documents, organisent des événements, lancent des sondages, échangent par messagerie et suivent les économies réalisées en commun. Chez Ikomobi, j’étais le développeur référent du projet, de la correction d’anomalies aux nouvelles fonctionnalités en passant par la modernisation progressive du code. Voici comment elle s’est faite sans jamais mettre la plateforme en pause.
L’état de la plateforme à mon arrivée
En service depuis 2021, la plateforme repose sur trois applications. Une API Node.js / Express range les données structurées dans MySQL et les contenus plus souples dans MongoDB ; un back-office React sert aux administrateurs ; une application React installable (PWA) est destinée aux membres des entreprises.
Comme toute application qui vit depuis plusieurs années, elle avait évolué par couches, au fil des équipes et des besoins, et certaines modifications demandaient plus de temps qu’elles n’auraient dû. Plusieurs raisons à cela :
- des parties du code écrites à des époques et dans des styles différents ;
- Redux, choix courant au moment de sa mise en place, devenu plus lourd que nécessaire pour l’usage qu’en faisait l’application ;
- des composants React encore écrits sous forme de classes ;
- des anomalies à traiter au fil de l’eau ;
- une bibliothèque de documents lente à charger.
Il fallait pourtant continuer à livrer les nouvelles fonctionnalités attendues, sans que les utilisateurs perçoivent la moindre rupture.
Comment moderniser sans arrêter les évolutions ?
Une refonte complète aurait gelé le produit pendant des mois. Le lead dev, le chef de projet et le client ont retenu l’approche inverse, qui consiste à améliorer le code par petites étapes et à profiter de chaque évolution pour assainir la partie qu’elle touche.
Il fallait aussi que ce travail trouve sa place dans le planning. J’ai proposé au chef de projet et au client, qui l’ont validé, de consacrer une part du temps de TMA (tierce maintenance applicative, c’est-à-dire le temps réservé à la maintenance de l’application) à la qualité du code et aux temps de chargement, en plus des corrections et des demandes du client. Leur accord a rendu la modernisation possible sans ouvrir de projet dédié.
Chaque changement passait ensuite par un environnement de staging, où une recette manuelle vérifiait les parcours concernés avant toute mise en production.
Les priorités restaient fixées par le chef de projet et le client. Je mettais en œuvre les améliorations qu’ils retenaient, et j’en proposais lorsque c’était cohérent avec leurs objectifs, si bien que toutes avançaient au même rythme que les nouvelles fonctionnalités.
Pourquoi retirer Redux, et par quoi le remplacer ?
Redux se justifie quand de nombreux écrans partagent un état complexe qui change souvent. Ce n’était plus le cas ici, et le store servait surtout à conserver des copies de données venues de l’API. Ces copies se désynchronisaient facilement : une donnée modifiée sur un écran n’était pas toujours rafraîchie sur un autre, et pouvait même être écrasée par une version plus ancienne. Chaque correction passait en outre par des actions, des reducers et des sélecteurs, si bien que maintenir Redux coûtait plus cher que le remplacer.
Il n’en reste aujourd’hui plus aucune trace. L’état réellement partagé passe par quelques contextes React ciblés, chacun chargé d’une seule responsabilité :
- l’authentification et l’utilisateur connecté ;
- la navigation ;
- les notifications et messages affichés à l’utilisateur ;
- les documents et dossiers de la bibliothèque, ou encore les groupes.
Tout le reste vit dans l’état local des composants, et chaque écran recharge désormais les données qu’il affiche, ce qui a mis fin aux données écrasées ou périmées. Dans le même mouvement, les composants en classes ont été réécrits en fonctions avec des hooks, ce qui a rendu le code plus court et plus homogène.
Accélérer les pages, appel par appel
Chaque page a été reprise du point de vue de ses appels réseau. Le cas le plus courant était celui d’appels indépendants lancés les uns après les autres, dont les temps d’attente s’additionnaient. En les lançant en parallèle avec Promise.all, on ramène l’attente à la durée de l’appel le plus long. En voici un exemple générique, réécrit pour l’article :
// Before: three sequential calls, wait times add up
const group = await api.get(`/groups/${id}`);
const events = await api.get(`/groups/${id}/events`);
const documents = await api.get(`/groups/${id}/documents`);
// After: independent calls start at the same time
const [group, events, documents] = await Promise.all([
api.get(`/groups/${id}`),
api.get(`/groups/${id}/events`),
api.get(`/groups/${id}/documents`),
]);Second levier, les appels API eux-mêmes ont été allégés, de sorte que chaque page ne reçoive que ce qu’elle affiche. La bibliothèque de documents, la plus lente à charger, en a profité la première, et toutes les pages ont fini par y gagner en temps de chargement.
Qui a le droit de faire quoi ?
La plateforme accueille plusieurs entreprises, dont les utilisateurs n’ont pas les mêmes responsabilités. Les droits combinent donc deux niveaux. Les rôles globaux, comme celui d’administrateur de la plateforme, valent partout ; le rôle de pilote, que j’ai développé, s’attribue groupe par groupe et donne la main sur un groupe sans ouvrir l’administration de toute la plateforme.
L’API assure ces contrôles au moyen de middlewares Express placés devant les routes concernées, car c’est au serveur, et non à l’interface, de trancher. La DXP multi-tenants sur laquelle j’ai travaillé pose la même question à plus grande échelle.
Ce que la plateforme propose aujourd’hui
Les entreprises partenaires y trouvent désormais :
- une bibliothèque de documents partagée entre entreprises, avec des statistiques de consultation ;
- des événements avec suivi des inscriptions et de la participation, reliés à Google Agenda ;
- des sondages commentables, une boîte à idées, des actualités et des tutoriels gérés depuis le back-office ;
- une messagerie interne et un centre de notifications ;
- le suivi des économies et des gains réalisés dans le cadre des partenariats.
Côté infrastructure, je gère aussi sur Google Cloud Platform les clés des services Google qu’utilise la plateforme, comme l’agenda.
Que ferais-je autrement aujourd’hui ?
Je soignerais davantage la communication, dès le départ. Au début, j’hésitais à relancer ou à solliciter quelqu’un pour un point qui me semblait secondaire. J’ai appris depuis à faire clarifier chaque point dès qu’il est flou, quitte à relancer, car une question posée tôt coûte toujours moins cher qu’un malentendu découvert à la livraison.