Go et WebAssembly, une alternative prometteuse à React ? Dagger tente l'approche pour optimiser les performances de son frontend
et unifier sa base de code
L’architecture des applications web modernes repose généralement sur des bibliothèques JavaScript populaires comme React, Vue.js ou Angular, qui permettent de gérer l’interactivité et le rendu dynamique du contenu dans les navigateurs. Cependant, ces solutions, bien que robustes, viennent avec leurs propres défis, notamment en termes de performance, de complexité et de gestion des différentes interfaces utilisateurs.
Dans un mouvement qui pourrait redéfinir certaines approches du développement frontend, Dagger, une plateforme d'intégration et de déploiement continu (CI/CD), a décidé de se détacher des frameworks JavaScript traditionnels pour réinventer son interface utilisateur avec une technologie qui ne cesse de croître : Go et WebAssembly (WASM). Ce choix de remplacer React par Go couplé à WebAssembly a suscité un grand intérêt dans le domaine du développement, tant pour ses avantages que pour les défis qu’il soulève.
Dagger Cloud fournit une traçabilité unifiée et de bout en bout pour les builds et les tests - indépendamment des CI, des environnements de développement, des langages et des plates-formes. Tout ce que Dagger Engine peut exécuter, Dagger Cloud peut le tracer.
Quel en est l'intérêt ? Les développeurs expliquent : « "Qu'est-ce qui n'a pas fonctionné ? Pourquoi est-ce si lent ?" Si vous avez déjà perdu du temps à regarder les journaux de CI pour répondre à ces questions, il est temps d'adopter le traçage. Cela vous aidera à optimiser vos constructions et à résoudre plus rapidement les problèmes lorsque les choses tournent mal ».
Le contexte : un besoin de cohérence et de performance
« Il y a quelques semaines, nous avons lancé Dagger Cloud v3, une toute nouvelle interface utilisateur pour Dagger Cloud. L'une des principales différences entre la v3 et son prédécesseur v2 est que la nouvelle interface utilisateur est écrite en WebAssembly (WASM) à l'aide de Go. À première vue, ce choix peut sembler étrange (Go n'est généralement pas le premier langage auquel on pense lorsqu'on décide de programmer une interface utilisateur Web) mais nous avions de bonnes raisons ».
Avant de s'engager dans cette refonte, Dagger faisait face à une situation où deux interfaces utilisateurs distinctes étaient en parallèle :
- L'interface de terminal (TUI) développée en Go, directement intégrée au CLI de Dagger.
- L'interface Cloud développée en React pour le tableau de bord web, utilisé pour l’interaction avec les utilisateurs.
Maintenir deux interfaces différentes engendrait des défis majeurs. D’une part, la duplication du code ralentissait le développement. Chaque nouvelle fonctionnalité devait être réécrite et testée sur deux plateformes distinctes, ce qui non seulement alourdissait le processus de développement, mais aussi augmentait les risques d’incohérences entre les deux interfaces. D’autre part, la gestion des performances de l’interface Cloud devenait de plus en plus complexe. En particulier, la plateforme devait gérer de grandes quantités de données en temps réel, ce qui ralentissait l’expérience utilisateur et rendait l'interface moins réactive, surtout dans des scénarios de forte utilisation.
C’est dans ce contexte qu’une réflexion profonde a été menée, aboutissant à la décision de remplacer l’interface frontend React par une solution unifiée, basée sur Go et WebAssembly.Deux bases de code = plus de travail, moins de fonctionnalités
Dagger fonctionne en construisant un DAG d'opérations et en les évaluant, souvent en parallèle. Par nature, il s'agit d'une chose difficile à afficher. Pour aider les utilisateurs à s'y retrouver, nous proposons deux interfaces de visualisation en temps réel : l'interface terminale de Dagger (TUI), incluse dans le CLI de Dagger, et Dagger Cloud, un tableau de bord Web en ligne. L'interface utilisateur de Dagger est implémentée en Go, et Dagger Cloud (pré-v3) a été écrit en React.
Il est évident que nous voulons que les deux interfaces utilisateur soient aussi proches que possible l'une de l'autre. Mais l'interprétation du flux d'événements de Dagger en temps réel et la production d'une interface utilisateur sont des tâches assez complexes. Certains des flux d'événements les plus complexes que nous ayons vus contiennent des centaines de milliers de données OpenTelemetry, et la gestion des structures de données qui les entourent devient très compliquée, très rapidement. L'interface Web ne pouvait souvent pas suivre l'énorme volume de données qu'elle devait traiter et elle devenait lente et laggy ; pour résoudre ce goulot d'étranglement des performances, nous avons été contraints d'adopter un modèle d'implémentation différent pour l'application React.
Nous nous sommes donc retrouvés avec deux interfaces essayant d'accomplir la même chose, l'une dans un langage et un écosystème (TypeScript/React), l'autre dans un langage et un écosystème totalement différents (Go), et nous ne pouvions pas facilement partager la logique métier entre elles. En tant que petite équipe, nous devons livrer rapidement. Avoir à réimplémenter chaque fonctionnalité deux fois était une taxe massive sur notre vélocité.
Nous avons commencé à réfléchir à une nouvelle approche de Dagger Cloud, avec deux objectifs principaux :
- Unifier les bases de code, afin d'éliminer les doublons et de rendre plus efficace la livraison de nouvelles fonctionnalités.
- Tenir la promesse d'une interface Web claire et rapide, à la hauteur de la vitesse et de la performance de l'interface du terminal.
Go et WebAssembly : la solution choisie
Le choix de Go et WebAssembly ne s’est pas fait par hasard. Ce combo représente une approche relativement novatrice mais prometteuse pour le développement frontend, et ce pour plusieurs raisons.
Go : La Cohérence et la Productivité
Go, un langage de programmation compilé développé par Google, est particulièrement apprécié pour sa simplicité, sa rapidité et son efficacité. Son adoption au sein de Dagger était déjà bien ancrée pour le développement de leur interface en ligne de commande (CLI) et du backend. L'utilisation de Go pour l'interface utilisateur présente plusieurs avantages :
- Unité de code : En choisissant Go, l’équipe de développement a pu unifier l’écriture du code backend et frontend. Cela facilite non seulement la maintenance, mais aussi la collaboration entre les équipes de développement, qui sont toutes familières avec le même langage.
- Haute performance : Go étant un langage compilé, il offre des performances supérieures à celles des langages interprétés comme JavaScript. Ce facteur est essentiel pour des applications qui manipulent une grande quantité de données, comme c’est le cas de Dagger.
- Simplicité : Go est réputé pour sa syntaxe simple et son faible coût d’apprentissage, ce qui facilite la prise en main pour les développeurs, même ceux venant de langages de programmation plus complexes.
WebAssembly : L’exécution nativement dans le navigateur
WebAssembly (WASM) est une technologie qui permet d'exécuter du code à grande vitesse dans les navigateurs, presque à la vitesse native. L’une des caractéristiques principales de WASM est sa capacité à exécuter des langages autres que JavaScript dans l’environnement du navigateur, avec un rendu extrêmement rapide et efficace.
- Performance accrue : WASM permet à Dagger de déplacer des parties du code à exécuter directement dans le navigateur, au lieu de dépendre uniquement du JavaScript. Cela permet de maximiser les performances, surtout dans des cas d’utilisation avec de lourdes opérations de calcul.
- Portabilité : Le code Go compilé en WebAssembly peut être exécuté dans n’importe quel navigateur moderne, sans nécessiter de plugins supplémentaires. Cette portabilité est un atout majeur, car elle permet à Dagger de s’assurer que son application web fonctionne de manière fluide sur une large gamme de plateformes et de dispositifs.
- Réduction de la charge serveur : En déplaçant certaines tâches côté client grâce à WASM, Dagger a pu alléger la charge des serveurs, réduisant ainsi les coûts d'infrastructure et améliorant la réactivité de l’application.
Avec Dagger Cloud v3, nous voulions offrir une expérience plus fluide et plus rapide tout en maintenant une parité complète avec l'interface utilisateur du terminal Dagger, nous avons donc adopté WebAssembly (WASM).
L'adoption de WebAssembly (WASM) a été l'une des tendances croissantes dans l'écosystème du cloud au cours des dernières années. Et pour cause ! WASM vous permet d'exécuter du code écrit dans différents langages de programmation (comme Go, Rust, C++, etc.) dans des navigateurs web ou d'autres environnements à une vitesse quasi native.
En remplaçant notre ancienne application React par une interface utilisateur basée sur WebAssembly, nous avons unifié la logique commerciale sous-jacente entre le terminal Dagger et les expériences web. Auparavant, le maintien d'implémentations séparées en TypeScript/React (pour le web) et Go (pour le CLI) créait des doublons et ralentissait notre capacité à livrer des fonctionnalités de manière cohérente sur toutes les plateformes. Désormais, avec WASM, l'interface terminal et l'interface web partagent la même base de code backend.
Pour construire notre nouvelle interface utilisateur, nous avons utilisé Go-app, un framework Go pour les applications web progressives, qui a facilité la concrétisation de notre vision basée sur WebAssembly.
Des erreurs plus faciles à lire
Les défis du passage à Go et WebAssembly
Bien que les avantages de cette approche soient évidents, la transition n’a pas été sans difficultés. Le passage de React à Go et WebAssembly a demandé un investissement important en termes de temps, de ressources et de compétences techniques.
- Maturité technologique de WASM : Le manque d'outils et de bibliothèques adaptées a obligé l’équipe de Dagger à développer eux-mêmes de nombreux composants frontend, ce qui a allongé le temps de développement.
- Limites de la mémoire WebAssembly : Le standard WebAssembly impose des limites de mémoire qui, bien qu’ayant été étendues dans les dernières versions des navigateurs, restent relativement faibles (environ 2 Go par application). Ce paramètre est particulièrement contraignant pour des applications qui doivent traiter de grandes quantités de données en temps réel, comme c’est le cas pour Dagger.
- Expérience utilisateur : Le passage de React, un framework optimisé pour le rendu d’interfaces riches et dynamiques, à une solution en Go et WebAssembly, a nécessité de repenser une partie de l’interactivité et de l’ergonomie de l’interface. Les composants web devaient être réinventés, ce qui a demandé une phase de prototypage et de tests exhaustive.
Notre objectif initial était de pouvoir réutiliser une base de code pour Dagger Cloud et l'interface utilisateur. Nous avons décidé assez tôt d'en faire une base de code Go. Techniquement, nous aurions pu faire l'inverse et utiliser TypeScript pour l'interface utilisateur. Mais nous sommes avant tout une équipe d'ingénieurs Go, et le choix de Go a permis aux autres membres de l'équipe de contribuer plus facilement, d'ajouter une fonctionnalité ou de passer quelques heures pour aider à déboguer un problème. En plus de la standardisation sur un seul langage, cela nous a donné de la flexibilité et a brisé les silos au sein de notre équipe.
Une fois que nous avons décidé d'exécuter le code Go directement dans le navigateur, WebAssembly était l'étape suivante logique. Mais il restait encore quelques défis à relever :
- La combinaison Go + WebAssembly n'est pas encore aussi mature que React et d'autres frameworks JavaScript. Il n'existe pas de bibliothèques de composants prêts à l'emploi, les outils de développement ne sont pas aussi riches, etc. Nous savions que nous devrions créer la plupart de nos composants d'interface utilisateur à partir de zéro.
- Les applications WebAssembly sont soumises à une limite de mémoire de 2 Go dans la plupart des navigateurs. Nous nous attendions à ce que cela pose un problème lors de l'affichage de traces volumineuses, et nous savions que nous devrions faire beaucoup d'optimisation pour minimiser l'utilisation de la mémoire et maintenir l'interface utilisateur stable. Mais ce n'était pas entièrement mauvais ; le point positif était que toute amélioration de l'utilisation de la mémoire apportée à l'interface WebAssembly profiterait également aux utilisateurs de l'interface utilisateur, puisqu'il s'agissait maintenant d'une base de code partagée.
Des indicateurs d'état plus clairs
Les résultats : une interface plus performante et cohérente
Malgré ces défis, la refonte a été couronnée de succès. Les résultats ont été significatifs :
- Performance améliorée : La gestion des données en temps réel est devenue beaucoup plus fluide. L’application est désormais plus réactive, avec des temps de réponse plus rapides et une expérience utilisateur nettement améliorée.
- Unité et cohérence : L’unification du code backend et frontend sous Go a facilité la maintenance et la collaboration entre les équipes. En éliminant la duplication du travail, Dagger a gagné en efficacité et a réduit les risques d’incohérences dans le développement.
- Réduction de la taille du fichier WASM : L’optimisation de la taille des fichiers WASM, notamment grâce à la compression Brotli, a permis de réduire de manière significative la taille du code à charger, améliorant ainsi les temps de chargement des pages.
L’avenir du développement frontend : Go et WebAssembly comme solutions viables ?Une fois la décision prise, la question suivante était : « Comment allons-nous construire cela ? » Nous avons décidé de construire la nouvelle interface utilisateur basée sur WebAssembly dans le framework Go-app. Go-app est un framework de haut niveau spécialement conçu pour les Progressive Web Apps (PWA) en WebAssembly. Il offre des avantages clés de Go, comme la compilation rapide et le typage statique natif, et il suit également un modèle d'interface utilisateur basé sur des composants, comme React, ce qui a facilité la transition.
La combinaison Go + WebAssembly n'étant pas très répandue, l'équipe de Dagger a fait preuve d'un certain scepticisme quant à sa faisabilité. Par exemple, il n'y avait pas de véritable écosystème pour les composants d'interface utilisateur Go-app et nous savions que nous devrions écrire les nôtres, mais nous n'étions pas sûrs de la facilité ou de la difficulté de cette tâche. Nous étions également préoccupés par les intégrations avec d'autres services (Tailwind, Auth0, Intercom, PostHog), et par le rendu de plusieurs centaines de composants mis à jour en direct en même temps.
Pour répondre à ces questions et réduire les risques du projet, j'ai passé près d'un mois à prototyper, avec l'objectif de réimplémenter autant que possible l'interface utilisateur existante dans Go-app. Il s'est avéré qu'il n'y avait pas beaucoup d'obstacles : WebAssembly est déjà un standard ouvert bien documenté et la plupart des autres questions ont trouvé leur réponse dans la documentation de Go-app. Le plus grand défi, comme prévu, était la limite d'utilisation de la mémoire, qui a nécessité une conception et une optimisation minutieuses.
L’expérience de Dagger soulève plusieurs questions intéressantes sur l'avenir du développement frontend. Le modèle Go + WebAssembly semble être une alternative prometteuse aux frameworks JavaScript traditionnels, notamment pour les applications qui nécessitent une gestion intensive des données ou des performances optimisées. Cependant, cette approche reste relativement nouvelle et nécessite une expertise technique poussée.
Il est donc probable que dans les années à venir, des solutions hybrides ou plus complètes émergeront, combinant les meilleures caractéristiques de Go, WebAssembly et des frameworks frontend populaires comme React. La clé résidera dans l’adaptation de ces technologies aux besoins spécifiques de chaque projet, et dans l’émergence de nouvelles bibliothèques et outils pour simplifier leur adoption.
Source : Dagger (1, 2)
Et vous ?
Quel rôle l’utilisation de Go dans le frontend pourrait-elle jouer dans la réévolution du paradigme des frameworks JavaScript ? Est-ce le début d'une tendance plus large ?
Quels sont les avantages et inconvénients de la combinaison Go + WebAssembly par rapport à des technologies comme Rust ou Blazor pour des applications frontend complexes ?
Dans quelle mesure la gestion de la mémoire et des limites de taille des fichiers dans WebAssembly peut-elle affecter les applications à grande échelle, comme les plateformes de streaming ou les applications de données volumineuses ?
Pensez-vous que l’approche Go + WASM soit adaptée à d’autres types de projets (par exemple, des applications à forte interactivité ou à grand volume de contenu multimédia) ?
Est-ce que la forte spécialisation des équipes techniques (Go dans ce cas) pourrait devenir un obstacle pour les entreprises souhaitant adopter cette approche, étant donné la courbe d’apprentissage ?
Envisageriez-vous d’autres solutions pour optimiser la performance des interfaces web modernes tout en gardant la souplesse et l’ergonomie de frameworks comme React ?
Voir aussi :
Exécuter des programmes C dans le navigateur en utilisant le runtime WebAssembly « une étape majeure pour faire fonctionner n'importe quel logiciel avec WebAssembly », d'après Wasmer
Le langage Go souffle ses 15 bougies et atteint sa position la plus haute sur l'indice Tiobe, Google annonce que le nombre d'utilisateurs de Go a plus que triplé au cours des cinq dernières années
L'équipe de Meta annonce la sortie de React 19, qui introduit de nouvelles fonctionnalités et améliorations : fonctions asynchrones, composants/actions serveur et prise en charge d'éléments personnalisés
Partager