Leçons tirées d’une migration progressive avec les Web Components
Tobias Kohr
La dépréciation est un défi bien connu dans le monde du logiciel. Les technologies frontend évoluent vite, et les frameworks qui ont façonné l’industrie finissent toujours par atteindre leur fin de vie. Quand ce moment arrive, les équipes doivent composer avec les risques liés à une technologie vieillissante : le support disparaît, la compatibilité devient incertaine, et la maintenance se fait plus lente et plus coûteuse.
AngularJS 1 en est un bon exemple. Lors de sa sortie en 2010, il a changé la manière dont de nombreuses équipes concevaient le développement frontend. Son support officiel a pris fin en 2022, mais beaucoup de plateformes durables reposent encore sur lui. La question clé devient alors : comment avancer sans tout arrêter ?
L’une des plateformes confrontées à cette situation était le Géoportail du Luxembourg, une application avancée basée sur Geomapfish, OpenLayers et AngularJS. Au fil des années, elle était devenue riche en fonctionnalités et fortement personnalisée. L’environnement de développement était lent et lourd, en partie à cause de la complexité de sa composition Docker. Une réécriture complète n’était pas réaliste, car le système en production continue d’évoluer avec de nouvelles fonctionnalités et des correctifs. Il fallait une migration progressive, permettant de moderniser pas à pas tout en gardant la plateforme pleinement opérationnelle.
Nous avons abordé ce travail en plusieurs phases.
Phase 1 : Commencer petit avec LitElement
Notre première étape a consisté à introduire des Web Components LitElement directement dans l’application existante. Nous avons conservé l’architecture d’origine presque intacte. Le CSS basé sur LESS est resté en place, et nous avons évité de modifier l’état global de l’application. Techniquement, l’intégration fonctionnait. Mais l’expérience n’était pas satisfaisante. Le développement restait lent, et le couplage fort avec le code legacy rendait chaque changement lourd. Il est vite apparu que simplement intégrer des composants Lit dans l’ancien framework ne suffirait pas à obtenir les améliorations recherchées.
Phase 2 : Créer de l’espace pour évoluer avec RxJS
La deuxième phase visait à gagner en liberté. Nous avons créé une application et un dépôt séparés pour développer les nouveaux Web Components. Libérés des contraintes du système legacy, le développement est devenu immédiatement plus fluide et plus rapide.
En parallèle, nous avons commencé à construire un nouveau cœur d’application avec une gestion d’état basée sur RxJS et avons introduit Tailwind CSS pour le style. Lit et RxJS fonctionnaient bien ensemble, mais la combinaison était verbeuse et peu répandue. Le tout marchait, mais ne semblait pas être la voie la plus durable.
Phase 3 : Passer à Vue et Pinia
Dans la troisième phase, nous avons fait évoluer la stack vers Vue et Pinia. Nous avons aussi développé un nouveau persistor d’état pour synchroniser l’état de l’application avec l’URL et le stockage local.
La migration s’est déroulée plus facilement que prévu. Le système de réactivité de Vue apportait ce qui manquait à Lit : une réactivité intégrée, avec moins de couches et moins de boilerplate. Les patterns étaient plus clairs et plus faciles à maintenir. Vitest a également simplifié les tests.
Intégration : un modèle hybride pour continuer d’avancer
Une fois le nouveau cœur et les composants stabilisés, nous les avons packagés sous forme de bibliothèque et avons mis en place un environnement hybride capable de les faire fonctionner aux côtés de l’application legacy. Cela nous a permis de remplacer progressivement des parties de l’ancien frontend sans interrompre le travail en production.
Le nouveau cœur a facilité l’intégration. Les composants fonctionnaient bien ensemble. Les parties legacy pouvaient toujours écouter le nouvel état, et l’objet OpenLayers continuait de circuler vers l’ancien code, ce qui maintenait les outils existants opérationnels.
Il y a eu quelques points de friction. Par exemple, les Web Components Vue ne permettent pas facilement de désactiver le Shadow DOM, ce qui a nécessité quelques ajustements CSS des deux côtés. Ces difficultés restaient gérables et n’ont pas ralenti la migration de manière significative.
Le modèle hybride s’est révélé être une stratégie robuste. Nous pouvions construire la nouvelle application en parallèle, tout en intégrant ses pièces dans le système en production à un rythme soutenable.
Conclusion
Avec le recul, plusieurs enseignements se dégagent.
Commencer par de petites expérimentations a aidé à créer de la confiance, mais les progrès réels sont arrivés lorsque nous avons isolé le nouveau développement de l’environnement legacy. Une migration progressive n’a pas besoin d’être disruptive. En superposant les nouvelles technologies avec précaution et en maintenant l’ancien système suffisamment longtemps pour transmettre ses responsabilités, nous avons construit une trajectoire fluide pour les développeurs comme pour les utilisateurs.
Le modèle hybride a demandé un effort supplémentaire, mais il nous a offert la flexibilité nécessaire. Il nous a permis de moderniser la plateforme étape par étape, sans bloquer le développement en cours ni prendre le risque d’une rupture majeure. Techniquement, plusieurs combinaisons étaient possibles. LitElement et RxJS fonctionnaient, mais généraient plus de bruit. Le passage à Vue et Pinia a offert une expérience de développement plus claire et plus productive.
Cette approche nous a permis de moderniser une application complexe tout en respectant son histoire, ses utilisateurs et le rythme des opérations réelles.
Carrière
Vous souhaitez travailler dans un environnement inspirant et rejoindre nos équipes motivées et multiculturelles ?