zumbrunn.com

30 ans de Javascript côté serveur

Javascript côté serveur, ou ServerJS comme la communauté a fini par l'appeler, aurait vraiment dû être une histoire à succès dès le départ. Lorsque Netscape a introduit Javascript côté serveur sous la forme de LiveWire en 1996, ils avaient remarquablement bien cerné bon nombre de choses.

La vision était élégante : utiliser le même langage côté client et côté serveur. On pouvait écrire du Javascript pour valider un champ de formulaire dans le navigateur, puis utiliser ce même Javascript pour valider et traiter les mêmes données côté serveur. Ce n'était pas simplement pratique, c'était conceptuellement puissant. Le modèle mental était unifié. Les compétences étaient transférables, le code pouvait être partagé.

LiveWire a été pionnier d'idées qui allaient devenir la pratique courante des décennies plus tard. Il suivait automatiquement l'état par utilisateur (via l'objet client), associait l'accès aux bases de données à des objets Javascript et leurs propriétés (via l'itération cursor.next()), et était capable de générer du balisage HTML dynamiquement côté serveur. La capacité d'intégrer la logique serveur directement dans les pages réapparaîtrait dans PHP, ASP, JSP, et finalement dans des frameworks modernes comme Next.js et Fresh. Netscape avait compris que les développeurs avaient besoin de générer du HTML dynamiquement et de maintenir un état entre les requêtes, avec une intégration étroite de l'accès aux bases de données. Ces capacités ont été intégrées dans LiveWire dès le départ.

Le plus impressionnant peut-être est que LiveWire reconnaissait que le code côté serveur devait être compilé et mis en cache, et non interprété à chaque requête. Le compilateur LiveWire transformait Javascript en bytecode que le serveur pouvait exécuter efficacement. C'était une réflexion sophistiquée pour 1996.

Pourtant, malgré toute sa clairvoyance technique, Netscape a commis une erreur stratégique fatale. Plutôt que de publier leur implémentation Javascript côté serveur comme un standard ouvert pouvant fonctionner sur n'importe quel serveur web tel qu'Apache ou IIS, ils l'ont réservée exclusivement à leur propre Netscape Enterprise Server, tentant essentiellement de s'approprier le web. Au lieu d'y participer, ils voulaient être l'entreprise qui dominerait son avenir.

Mais le web évoluait selon des règles différentes, fondées sur des principes d'ouverture et d'accessibilité. Les technologies qui allaient le définir — HTML, HTTP et Javascript lui-même — ont réussi parce qu'il s'agissait de standards ouverts que n'importe qui pouvait implémenter. En gardant LiveWire propriétaire, Netscape a empêché Javascript de s'imposer et de dominer côté serveur dès le départ, comme il l'a fait dans le navigateur.


Le gain de vitesse de 150 000 × en 30 ans

La première incarnation de Javascript côté serveur chez Netscape reconnaissait la nécessité d'une exécution de code plus rapide et ajoutait une étape de compilation qui transformait Javascript en bytecode. Mais les développeurs web de l'époque voulaient pouvoir modifier et servir des pages web sans avoir à reconstruire au préalable. Les environnements d'exécution Javascript modernes comme Deno et Bun héritent des grands gains de performance réalisés au fil des décennies, qui ont éliminé ce défi.

Entre 1995 et aujourd'hui, la vitesse d'exécution de Javascript a été multipliée par environ 150 000. Ce n'est pas une coquille. Les moteurs Javascript sont devenus environ 430 fois plus rapides grâce à l'optimisation logicielle, et le matériel est devenu environ 350 fois plus rapide en vitesse d'exécution brute mono-thread. L'effet combiné est stupéfiant : 430 × 350 = plus de 150 000 fois plus rapide.

La percée est venue avec la compilation Just-In-Time. Au lieu d'interpréter le code source ligne par ligne, les compilateurs JIT traduisaient Javascript en véritable code machine à la volée pendant l'exécution du programme. Cette avancée a déclenché une concurrence acharnée entre le moteur Javascript V8 de Google, SpiderMonkey de Mozilla et JavascriptCore d'Apple avec le moteur Nitro, tous ajoutant des optimisations de plus en plus sophistiquées. Bien qu'il s'agît essentiellement d'une guerre des navigateurs, Javascript côté serveur en est devenu un grand bénéficiaire.


Les graines d'un écosystème en émergence

Bien que Brendan Eich ait créé Javascript en quelques jours seulement en 1995 pour l'intégrer rapidement dans le navigateur, les ambitions côté serveur étaient là dès le départ. Eich imaginait un langage capable de fonctionner à la fois dans le navigateur et sur le serveur, permettant aux développeurs d'écrire du code une seule fois et de le déployer sur l'ensemble de la pile web. Ce n'était pas une réflexion a posteriori ou un pivot ultérieur — cela faisait partie de la philosophie de conception originale.

Le verrouillage propriétaire de LiveWire a empêché l'émergence d'un écosystème complet avec des outils, des bibliothèques, un support communautaire et des mécanismes de distribution. Différents projets Javascript côté serveur ont adopté diverses approches uniques pour contourner ce problème au cours des années suivantes.

WebCrossing a abordé ce défi avec une vision architecturale distincte : une plateforme intégrée où Javascript et le stockage de base de données étaient étroitement couplés dès le départ. WebCrossing proposait une plateforme d'applications web avec des capacités NoSQL intégrées et Javascript comme langage de script. C'était véritablement en avance sur son temps. L'idée d'une plateforme native Javascript avec persistance des données intégrée précède le mouvement «full-stack Javascript» moderne de plusieurs années. WebCrossing a résolu le manque d'un écosystème d'outils et de bibliothèques en étant extrêmement monolithique. Il ne rendait pas seulement les données de la base de données directement accessibles sous forme d'arborescence d'objets traversable en Javascript, mais donnait accès par script à une pile complète incluant SMTP, POP, IMAP, NNTP, FTP, XML-RPC et HTTP lui-même, le tout intégré dans le même binaire et directement scriptable, avec tous les services interagissant avec le même arbre de contenu. WebCrossing montrait que les développeurs pouvaient créer des applications web complètes en Javascript sans avoir à intégrer des systèmes de bases de données séparés, recourir à des services externes ou gérer des pipelines de déploiement complexes. Mais comme LiveWire, WebCrossing était une plateforme propriétaire qui n'attirait qu'une petite communauté. Il a prouvé que le concept fonctionnait, mais n'a pas pu générer les effets de réseau nécessaires à une adoption généralisée, parce qu'il était propriétaire.

Le projet Helma a adopté une approche architecturale très différente, en faisant tourner Javascript sur la Java Virtual Machine. Cette stratégie avait un mérite réel. La JVM était mature, éprouvée et bénéficiait d'un vaste écosystème d'entreprise. Si l'on pouvait faire tourner Javascript sur la JVM, on pouvait théoriquement exploiter le modèle de threading de Java, sa bibliothèque standard étendue, ses outils d'entreprise et sa légitimité professionnelle. Plus important encore, la JVM offrait quelque chose de crucial : la performance grâce à la compilation JIT de HotSpot. Il ne s'agissait pas seulement d'accéder à l'écosystème Java — il s'agissait d'obtenir une optimisation sérieuse à l'exécution pour le code Javascript.

Construit sur le moteur Rhino de Mozilla, Helma proposait un Javascript côté serveur multi-thread avec des capacités de mapping objet-relationnel, une intégration de base de données et un framework d'application web complet. Il était architecturalement solide, techniquement impressionnant et véritablement prêt pour la production. Les développeurs pouvaient écrire du code Javascript qui accédait aux bibliothèques Java, utilisait les primitives de concurrence de Java et se déployait sur n'importe quel serveur compatible JVM.

Helma compilait Javascript en bytecode Java, qui passait ensuite par le compilateur HotSpot de la JVM. Cela signifiait que Helma bénéficiait du même type de percées en compilation JIT qui allaient rendre V8 révolutionnaire. HotSpot profilait le bytecode en cours d'exécution, identifiait les chemins critiques et les compilait en code machine optimisé. Pour les charges de travail serveur, où les mêmes chemins de code s'exécutent à répétition, cette optimisation était transformatrice. Helma n'était pas une solution interprétée lente. Il disposait d'une optimisation sérieuse des performances grâce à l'un des compilateurs JIT les plus matures qui existaient.

L'architecture de Helma exploitait au maximum l'héritage par prototype de Javascript, implémentant un système Model-View-Control propre que l'on pouvait conserver dans des fichiers séparés. Les prototypes Javascript représentaient les données et la logique métier, associés à la base de données via le mapping objet-relationnel intégré de Helma (Model). Ces prototypes Javascript avaient également des templates de skin (Views) qui étaient des composants HTML avec des espaces réservés et la capacité d'appeler des macros, ainsi que des méthodes de prototype (Controls) qui étaient des «actions», traitant les requêtes ou les gestionnaires de macros, appelables depuis l'intérieur des vues. Ainsi, Helma avait une structure de type MVC très claire et explicite, environ cinq ans avant que Ruby on Rails popularise le concept.

La version e4xd de Helma chargeait les vues en tant qu'objets E4X. E4X, ou ECMAScript for XML, a été standardisé sous le nom ECMA-357 en 2004. Il permettait aux développeurs d'écrire des littéraux XML directement dans le code Javascript, traitant XML comme un type de données natif plutôt que des chaînes à analyser. Le support E4X de Helma était sans doute supérieur à JSX, implémenté 10 ans plus tard dans React, résolvant essentiellement le même problème. Les deux permettaient d'avoir du balisage intégré directement dans le code, mais JSX nécessite une étape de construction. Il faut transpiler JSX en Javascript ordinaire avant qu'il puisse s'exécuter. E4X n'en avait pas besoin. C'était une fonctionnalité native du langage Javascript, intégrée dans l'environnement d'exécution. Pas d'étape de construction, pas de transpileur, pas de configuration. Le moteur Javascript Rhino comprenait les littéraux XML de la même façon qu'il comprenait les littéraux de chaînes ou les littéraux de tableaux. E4X dans Helma fonctionnait très bien pour assembler et rendre du HTML côté serveur — ce à quoi Next.js avec JSX est revenu 10 ans plus tard.

Tandis que XML était parfaitement adapté aux templates HTML, il n'était pas idéal pour le transport de données et l'interopérabilité. Pour cela, Javascript avait une autre réponse native sous la forme de la notation d'objets Javascript. Douglas Crockford a proposé en 2002 de reconnaître JSON comme un standard de format d'échange de données léger. Il voyait que la syntaxe d'objet native de Javascript offrait un format efficace, lisible par l'homme et indépendant du langage pour la communication serveur-client. Il était natif Javascript, ce qui signifiait que l'analyse et la génération de JSON en Javascript ne nécessitaient pas de bibliothèques externes ni de transformations complexes. Lors de l'utilisation de Javascript côté serveur, c'était tout à fait évident et naturel. L'acceptation dans d'autres environnements a été plus difficile, car XML devait être détrôné comme format de données universel. Au fur et à mesure que chaque autre langage implémentait des parseurs et des générateurs JSON, les serveurs écrits dans n'importe quel langage ont commencé à communiquer avec les clients Javascript en utilisant JSON. Il est devenu le format de données universel pour les API web.


Résoudre les dépendances fait germer l'écosystème

En 2006, lorsque jQuery a popularisé l'utilisation des fermetures dans le navigateur, la communauté Helma a commencé à expérimenter des façons d'envelopper les modules de bibliothèques Javascript dans des fermetures dans le cadre du processus de chargement des modules — un concept que l'équipe Helma a pleinement adopté dans un framework architectural de bas niveau qui est devenu RingoJS. Cette approche de système de modules a contribué ultérieurement à la formalisation de CommonJS. Lorsque Kevin Dangoor de Mozilla a écrit un article de blog en 2009 encourageant l'interopérabilité ServerJS, la communauté Helma, entre autres, l'a contacté et un groupe s'est formé pour collaborer à la définition de la spécification de module CommonJS.

En l'absence d'un système de modules natif faisant partie du standard du langage Javascript — qui n'allait arriver qu'environ six ans plus tard — CommonJS a permis aux environnements Javascript de construire un écosystème de bibliothèques natives, similaire à ce qui existait pour Helma sous la forme de bibliothèques Java. La fonction require() et le pattern module.exports sont devenus la norme dans l'ensemble de l'écosystème Javascript côté serveur.

Le besoin le plus explicite d'un système de modules natif approprié était celui de Node.js, un projet présenté pour la première fois par Ryan Dahl à la JSConf EU en novembre 2009. Node.js a choisi CommonJS comme système de modules. Le besoin de Node en matière de gestion des dépendances et d'un système de modules était plus pressant car son objectif était de faire de Javascript un langage serveur généraliste de première classe, et non simplement un framework web. En tant que registry de modules CommonJS et pour les télécharger automatiquement avec toutes leurs dépendances, Isaac Schlueter a implémenté NPM. Au fur et à mesure que davantage de paquets apparaissaient dans la registry, il devenait plus facile de construire des applications complexes en composant des paquets existants plutôt que de tout écrire de zéro. La culture «il y a un paquet pour ça» est née. Besoin d'analyser des arguments en ligne de commande ? Il y a un paquet. Besoin d'effectuer des requêtes HTTP ? Il y a un paquet. Besoin de valider des adresses e-mail ? Il y a un paquet. La barrière pour construire des applications sophistiquées a chuté dramatiquement. L'écosystème a explosé de façon exponentielle. En 2015, NPM comptait des centaines de milliers de paquets. En 2020, plus d'un million.


Le pivot de la JVM vers Node

Avant 2010, la JVM était le roi incontesté du développement Javascript côté serveur — mature, JIT rapide, bibliothèques massives. L'arrivée de Node marque un point de bascule après lequel ServerJS n'était plus le plus puissant et performant dans la JVM, mais en dehors de celle-ci.

Node est apparu au moment où plusieurs innovations ont convergé. Le moteur V8 de Google a soudainement rendu JavaScript rapide grâce à la compilation JIT. JSON était devenu le format de données universel. CommonJS proposait un système de modules sensé avec l'écosystème explosif de NPM. Node a combiné cela avec le passage du multi-thread au single-thread utilisant une boucle d'événements pour les I/O non bloquants. Soudainement, il devenait possible d'écrire des applications web performantes en dehors de la JVM. JavaScript est passé du statut de «jouet de navigateur» dénigré par les développeurs mainstream à celui de plateforme serveur sérieuse, même en dehors de la JVM, presque du jour au lendemain.

L'adoption des I/O non bloquants par Node était fortement inspirée de nginx, qui avait déjà prouvé qu'un modèle single-thread, orienté événements utilisant libev pouvait gérer une concurrence massive de manière extrêmement efficace. Au lieu de bloquer un thread en attendant les I/O, on enregistre un callback et on continue. Lorsque les I/O se terminent, le callback s'exécute. La boucle d'événements devient l'orchestrateur : elle traite les événements, exécute les callbacks et ne bloque jamais. Un seul thread peut gérer des milliers de connexions simultanées parce qu'il n'attend jamais — il fait toujours un travail utile ou dort efficacement jusqu'à ce que le prochain événement arrive. Tandis que nginx avait prouvé l'efficacité du modèle pour servir du contenu statique, l'appliquer à la logique applicative — où l'on n'exécute pas seulement des fichiers mais une logique métier complexe avec des requêtes de base de données et des appels API — était différent. Javascript se trouvait être un langage où les callbacks étaient naturels, où les patterns asynchrones étaient idiomatiques, où le modèle orienté événements semblait la bonne façon d'écrire du code. Avec Node, ServerJS était incroyablement efficace pour les travaux à forte intensité d'I/O (la principale tâche des serveurs web), lui permettant de gérer une concurrence massive avec peu de mémoire.

Pour la décennie suivante, Node a dominé Javascript côté serveur presque exclusivement. Durant cette période, le langage lui-même a mûri considérablement. ES6, sorti en 2015 et officiellement appelé ES2015, a apporté les classes, les fonctions fléchées, let et const pour la portée de bloc, la déstructuration, les littéraux de gabarits et les Promises comme primitives asynchrones de première classe. En 2016, WebAssembly a apporté le comportement en virgule flottante (f32 et f64), la sécurité mémoire et la réutilisation de code via des bibliothèques en Rust, C, C++ et Go, entre autres. Async/await est arrivé en 2017, résolvant enfin le problème des chaînes de Promises qui avait tourmenté les développeurs. Les années suivantes ont ajouté top-level await, les champs privés, le chaînage optionnel et la coalescence nulle — chacun une amélioration modeste mais significative de l'ergonomie du développeur. Et les modules standardisés, la syntaxe import et export, ont fourni une façon native d'organiser le code, bien que l'adoption ait pris du retard pendant des années car l'écosystème continuait à utiliser CommonJS sans ressentir le besoin de changer.


L'environnement d'exécution TypeScript parfait

La nature dynamique de Javascript — le fait que les variables peuvent contenir n'importe quel type et changer de type à l'exécution — était à la fois une force et une faiblesse. Elle rendait le langage flexible et facile à apprendre, mais rendait les grandes bases de code difficiles à maintenir. Le refactoring était effrayant parce qu'on ne pouvait jamais être sûr de ce qui allait casser. Les IDE ne pouvaient pas fournir une bonne autocomplétion ou des outils de refactoring parce qu'ils ne savaient pas quels types les variables contenaient. Avec l'introduction de TypeScript est venue une solution : le typage progressif. On pouvait ajouter des annotations de type au code Javascript, et le compilateur TypeScript vérifierait ces types à la compilation, détectant les erreurs avant l'exécution. Mais le typage était optionnel, on pouvait l'adopter progressivement en ajoutant des types aux parties de la base de code où ils apportaient le plus de valeur. Les outils étaient exceptionnels. IntelliSense offrait une autocomplétion précise. Les outils de refactoring pouvaient renommer en toute sécurité des variables et des fonctions dans une base de code entière. En 2018, TypeScript était passé d'optionnel à essentiel dans l'écosystème, mais il avait été ajouté après coup. Il fallait une étape de compilation, des fichiers de configuration, des définitions de types pour chaque paquet et une gestion soigneuse de la frontière entre le code typé et non typé. Ça fonctionnait, mais ça ressemblait à un contournement plutôt qu'à une fonctionnalité de première classe.

À la JSConf EU 2018, Ryan Dahl dans sa conférence «10 Things I Regret About Node.js» a présenté Deno, qui allait faire de TypeScript une fonctionnalité de première classe et remédier à d'autres lacunes de Node. Dahl a identifié des décisions de conception spécifiques qui avaient du sens en 2009, mais qui montraient leur âge une décennie plus tard. Le modèle de sécurité en était un. Node.js donnait aux programmes un accès complet au système de fichiers et au réseau par défaut. Il n'y avait pas de sandboxing, pas de système de permissions. Il n'y avait pas encore TypeScript disponible. Node faisait un usage intensif des callbacks et n'utilisait pas les Promises dès le départ. Les principes de conception de Deno allaient directement remédier aux limitations de Node.

Dans Deno, la sécurité est devenue la posture par défaut plutôt qu'une réflexion après coup. Les programmes Deno s'exécutent dans un sandbox par défaut. Si le code doit lire des fichiers, effectuer des requêtes réseau ou accéder à des variables d'environnement, ces permissions doivent être explicitement accordées. Deno exécute TypeScript directement. On écrit des fichiers .ts, et Deno gère la compilation de manière transparente. La vérification des types est intégrée dans l'environnement d'exécution. Le système de modules est passé exclusivement aux modules ES, utilisant la syntaxe d'import standard du web et les imports basés sur des URL. Au lieu de la registry centralisée de npm, Deno charge les modules directement depuis des URL. On peut importer depuis n'importe quel serveur HTTP, n'importe quel CDN, n'importe quelle source qui sert des fichiers JavaScript. Les dépendances sont mises en cache localement après le premier téléchargement, mais il n'y a pas de package.json, pas de répertoire node_modules, pas d'algorithme de résolution de version. En plus de prendre en charge les imports basés sur des URL, l'équipe Deno a lancé JSR, le JavaScript Registry, en 2024. Plutôt qu'une registry de tarballs comme npm, JSR est natif TypeScript dès le départ. Les paquets publient leur source directement, les informations de type sont intégrées, et les scores de documentation et de couverture de type sont calculés automatiquement. Fait crucial, les paquets JSR fonctionnent sur tous les environnements d'exécution : Node, Bun et Deno indifféremment. Il est conçu pour être une registry pour l'ensemble de l'écosystème ServerJS plutôt qu'un remplacement de npm réservé à Deno.

Deno est livré avec des outils intégrés tels qu'un formateur de code, un linter, un runner de tests et un générateur de documentation — tout inclus directement dans l'unique exécutable Deno. Le framework Fresh, construit sur Deno, utilise une architecture d'îlots. L'architecture d'îlots dans Fresh signifie que par défaut, vos pages n'envoient aucun JavaScript au client. Le serveur rend le HTML et l'envoie au navigateur. Si vous avez besoin d'interactivité — un menu déroulant, un formulaire avec validation, une mise à jour en temps réel — vous marquez ce composant comme un îlot. Seuls ces îlots envoient du JavaScript. Le reste de la page est du HTML statique. Cela vous donne les avantages de performance et de SEO du rendu côté serveur avec l'interactivité du JavaScript côté client, mais uniquement là où vous en avez vraiment besoin.

Fresh intègre Preact, une alternative légère à React, pour son modèle de composants, offrant la syntaxe JSX, la composition de composants et les hooks, mais avec un environnement d'exécution plus petit et de meilleures performances. L'approche du framework pour le routage est basée sur les fichiers, ce que Next.js a montré être plus intuitif que le routage basé sur la configuration. Comme dans Remix, la co-localisation du chargement des données avec les composants réduit la complexité. SvelteKit avait prouvé que l'amélioration progressive basée sur les composants peut être le juste milieu entre le rendu côté client et côté serveur, ce qui a informé le concept d'îlots de Fresh.


De nouveaux sommets à l'horizon

Deno et Fresh synthétisent toutes les leçons apprises en 30 ans d'expérience ServerJS et offrent à mon avis le meilleur environnement d'exécution et framework web pour Javascript côté serveur à l'heure actuelle. Bien sûr, d'autres environnements d'exécution ont leurs cas d'usage légitimes selon la niche spécifique d'un projet. Après tout, nous pouvons maintenant choisir parmi une liste étonnamment longue d'environnements d'exécution Javascript disponibles.

Node.js reste le standard d'entreprise et héritage avec une profondeur d'écosystème inégalée ; Deno excelle dans la sécurité, le développement TypeScript-first et les défauts modernes ; Bun domine lorsque la vitesse brute et les outils tout-en-un sont critiques ; Cloudflare Workers et autres environnements d'exécution edge WinterCG brillent pour les applications à distribution mondiale et faible latence. Pour l'intégration de JavaScript dans des applications personnalisées, les développeurs peuvent choisir parmi V8 (haute performance, largement utilisé), GraalJS (excellente interopérabilité Java sur la JVM), QuickJS (extrêmement léger), Boa (Rust pur, expérimental), Hermes (optimisé pour mobile), ou SpiderMonkey et JavaScriptCore pour des besoins d'intégration spécifiques. Les applications de bureau sont bien servies par Electron (Chromium + Node). C'est un nombre fou d'options d'environnement d'exécution qu'aucun autre langage n'approche. Tandis que Java mérite une mention honorable, le dauphin en termes de nombre d'implémentations d'environnement d'exécution est en fait étroitement lié aux environnements Javascript : WebAssembly et AssemblyScript.

WebAssembly (WASM) est un format d'instruction binaire conçu pour s'exécuter à une vitesse quasi-native dans les navigateurs web et les environnements d'exécution JavaScript. Ce n'est pas un remplacement pour JavaScript, c'est un complément. Les modules WASM compilent depuis des langages comme Rust, C++ ou AssemblyScript vers du bytecode portable qui s'exécute dans un environnement sandboxé. Les caractéristiques de performance sont spectaculaires : les opérations cryptographiques s'exécutent 10 à 30 fois plus vite, les calculs numériques approchent les vitesses C natives, et les opérations gourmandes en mémoire évitent entièrement la surcharge de la collecte des déchets.

Le pattern d'intégration est élégant. JavaScript gère la logique applicative, l'interaction utilisateur et l'orchestration. WASM gère les opérations critiques en termes de performance que JavaScript ne peut pas faire efficacement. Une application web pourrait utiliser JavaScript pour le rendu UI et la gestion d'état tout en déléguant le traitement d'images à un module WASM compilé depuis Rust. Un outil d'analyse de données pourrait utiliser JavaScript pour l'API et la visualisation tout en exécutant des calculs statistiques dans WASM. La frontière entre les deux est explicite et intentionnelle.

AssemblyScript a considérablement abaissé la barrière à cette intégration. Avant AssemblyScript, utiliser WASM signifiait apprendre Rust ou C++, comprendre leurs chaînes d'outils de construction et gérer l'inadéquation d'impédance entre ces langages et JavaScript. AssemblyScript fournit une syntaxe de type TypeScript qui compile directement en WebAssembly. Les développeurs JavaScript peuvent écrire du code qui semble familier — avec des classes, des fonctions, des annotations de type — et le compiler en WASM sans quitter le modèle mental de l'écosystème JavaScript. Ce n'est pas aussi performant que du Rust optimisé à la main, mais c'est bien plus rapide que JavaScript et beaucoup plus accessible.

Les opérations cryptographiques sont un cas clair où recourir à WASM a du sens. Le hachage, le chiffrement et la vérification de signature sont intensifs en CPU et critiques pour la sécurité. Les exécuter dans WASM offre à la fois rapidité et isolation. Le traitement d'images et de vidéos, le calcul numérique et les simulations scientifiques représentent d'autres domaines où WASM brille. La performance est souvent 20 fois supérieure ou plus.

Dans Deno, les modules WASM se chargent avec la même syntaxe de module ES que JavaScript : import { process } from "./image.wasm";. Le modèle de sécurité de Deno s'étend au code WASM — les modules s'exécutent dans le même sandbox avec le même système de permissions. WASM est donc un citoyen de première classe de l'écosystème, bien intégré au niveau de l'environnement d'exécution.


Les dix prochaines années devraient être intéressantes

Trente ans après, ServerJS est dans sa meilleure forme qu'il n'a jamais connue. Les environnements d'exécution sont rapides, les outils matures, et l'écosystème converge plutôt que de se fragmenter. Ce que j'observe de plus près est la couche IA, parce que Javascript présente de véritables avantages pour les applications qui intègrent étroitement des agents IA et des modèles de langage. Les propriétés qui rendaient Javascript bien adapté aux serveurs web à forte intensité d'I/O s'avèrent tout aussi bien adaptées à la couche applicative IA émergente. Les grands modèles de langage répondent sous forme de flux de tokens, et non de charges utiles uniques — et le streaming est idiomatique en Javascript depuis les débuts de Node. JSON, que Javascript traite comme un type de données natif, est la lingua franca des API de LLM. Async/await, que le langage a passé des années à affiner, s'adapte naturellement au profil de latence de l'inférence des modèles.

Tandis que les développeurs créent des applications qui orchestrent plusieurs modèles IA, enchaînent des appels d'outils et diffusent des résultats aux utilisateurs en temps réel, Javascript devient le langage de colle dominant de la pile IA. Le Vercel AI SDK, LangChain.js et un écosystème croissant de bibliothèques IA natives ServerJS le reflètent. La combinaison des défauts sécurisés de Deno, du modèle d'amélioration progressive de Fresh et de l'inférence navigateur/edge maturant via WebGPU + Wasm (atteignant maintenant 60 à 85 % des performances natives pour les modèles quantisés sur du matériel capable) est particulièrement convaincante. Tandis que Python reste dominant pour la recherche sur les modèles de base et les charges de travail d'entraînement lourdes, JavaScript est de plus en plus le langage de livraison de l'IA — la couche qui transforme les modèles en produits utilisables et réactifs.

Un autre domaine qui mérite attention est AssemblyScript comme cible de compilation déterministe. En restreignant JavaScript/TypeScript à un sous-ensemble prévisible qui compile en WebAssembly propre, il offre un pont potentiel pour apporter une expérience développeur de type JavaScript dans des environnements qui exigent un déterminisme strict — notamment pour les contrats intelligents. Bien que toujours de niche, la combinaison de la familiarité TypeScript avec des performances quasi-natives et la reproductibilité pourrait ouvrir de nouvelles frontières intéressantes à mesure que les blockchains commencent à prendre en charge la combinaison AssemblyScript et WASM pour les contrats intelligents plus largement.

Les oracles sont une direction connexe et tout aussi prometteuse. Les contrats intelligents sont déterministes et isolés par conception — ils ne peuvent pas atteindre le monde extérieur par eux-mêmes. Les oracles comblent cette lacune en apportant des données du monde réel sur la blockchain : prix, météo, résultats sportifs, réponses d'API. Des projets comme XEQMLabs et DarkFi explorent une infrastructure d'oracle axée sur la confidentialité, où le contenu de la requête, l'identité du demandeur et l'objectif restent privés par conception, ce qui pourrait rendre l'utilisation des oracles beaucoup plus populaire. Un nœud oracle doit récupérer des API web, analyser et transformer des réponses JSON, gérer les erreurs et les cas limites, et livrer un résultat vérifié — ce qui est une description de poste ServerJS. Chainlink Functions l'explicite : il exécute du code JavaScript sur un réseau oracle décentralisé, permettant aux développeurs d'écrire du JS standard qui récupère depuis n'importe quel endpoint HTTP et achemine le résultat sur la chaîne. L'ensemble de l'écosystème d'API web devient accessible aux contrats intelligents via une couche que les développeurs savent déjà écrire. L'exigence de déterminisme renvoie à AssemblyScript : plusieurs nœuds oracle doivent arriver indépendamment au même résultat pour le consensus, ce qui exige une exécution reproductible. Le sous-ensemble TypeScript compilant en WASM d'AssemblyScript fournit une logique d'oracle à la fois conviviale pour les développeurs et vérifiablement déterministe. Le modèle sandbox de Deno correspond également naturellement aux exigences de sécurité des nœuds oracle, ce qui facilite la restriction précise des endpoints réseau et des ressources auxquels le code oracle est autorisé à accéder.

À mesure que ces éléments continuent de mûrir — une meilleure intégration GPU, une observabilité plus forte des agents et des voies déterministes viables comme AssemblyScript — JavaScript définira de plus en plus les couches interactives et d'orchestration pour les contrats intelligents, les oracles et l'IA.

Les dix prochaines années devraient être très intéressantes.