Journal

La stack web a enfin gagné en maturité

La dernière décennie n’a pas couronné un framework. Elle a produit de meilleurs runtimes, des applications plus légères, des standards communs et des workflows durables. Il reste encore à réunir ces fondations dans une même plateforme.

Tous les articlesArchitectureOpen sourceDéploiement
Publié le
29 juillet 2026
Lire
21 min de lecture
Écrit par
Eric Chautems
Sommaire

Sur cette page

Le web n’a pas passé la dernière décennie à attendre qu’un framework remporte la partie.

On pourrait pourtant le croire, à nous voir encore débattre comme si l’un d’eux allait finir par trancher toutes les questions. React ou Vue. Next.js ou Nuxt. Composants serveur ou îlots. Serverless ou, finalement, un bon vieux serveur. Les noms changent sans cesse, alors que l’essentiel se joue en dessous. Nous avons réparé le runtime. Fait disparaître le build pendant le développement. Appris à envoyer moins de JavaScript, à décrire une API avant de l’implémenter, à suivre une requête à travers plusieurs services et à reprendre le travail après la mort d’un processus.

Deno, JSR, Vite, Preact, OpenAPI et OpenTelemetry, chacun associé à l’hypothèse dont il nous a débarrassés.Deno, JSR, Vite, Preact, OpenAPI et OpenTelemetry, chacun associé à l’hypothèse dont il nous a débarrassés.
FigureAucun n’est un framework. Chacun a supprimé une hypothèse que la génération précédente avait fini par accepter.

Deux choses sont sorties de cette décennie : des métaframeworks robustes pour la production, qui assument leurs choix, et des standards qui n’appartiennent à personne en particulier. Ce qui manque toujours, c’est une solution qui réunisse les deux, déploiement compris, sans joindre la facture d’une plateforme.

Deno a compris qu’un successeur a toujours besoin de son prédécesseur

Ryan Dahl sur scène à la JSConf EU 2018.
FigureRyan Dahl présente Deno pendant sa conférence consacrée aux décisions qu’il regrettait dans Node.js.1

Quand Ryan Dahl a présenté Deno, il a parlé sans détour des décisions prises pour Node.js qu’il regrettait. Son remplaçant serait sécurisé par défaut, exécuterait directement TypeScript et intégrerait les outils que la plupart des projets avaient mis des années à assembler eux-mêmes : outil de formatage, linter, lanceur de tests et compilateur capable de produire un exécutable unique.

La réponse était nettement plus propre. J’avais envie qu’elle fonctionne.

Le problème n’était pas le runtime. C’était la décennie de logiciels déjà accumulée sur npm. Un nouveau projet peut choisir de meilleurs réglages par défaut ; une entreprise qui exploite déjà une application a des clients, des échéances et des dépendances que personne ne réécrira sous prétexte qu’un runtime possède un modèle de permissions plus élégant. Deno pouvait avoir raison et rester tout de même à la porte.

Il a donc changé. La compatibilité avec Node s’est améliorée, les paquets npm sont devenus des citoyens de première classe et, à la sortie de Deno 2, package.json, les workspaces et même node_modules étaient acceptés — ce répertoire que même ses défenseurs suppriment parfois, juste pour voir si la situation s’arrange. Le projet conçu pour échapper à Node savait désormais exécuter des projets Node.

Si l’objectif est la pureté, cela ressemble à une capitulation. Si l’objectif est l’adoption, c’est un progrès. TypeScript nous avait déjà donné la leçon : il n’a pas vaincu JavaScript, il l’a rendu plus sûr tout en préservant l’immense patrimoine logiciel existant.

Un successeur ne gagne pas en effaçant la génération précédente. Il gagne en l’emmenant avec lui.

C’est aussi pour cela que Node n’est pas près de disparaître, et tout va bien. COBOL déplace encore de l’argent. Les marchés ne remplacent pas leurs fondations selon un calendrier ; ils ouvrent une meilleure voie, puis avancent au rythme autorisé par les logiciels déjà en place.

La compatibilité ne représentait que la moitié du pari. L’autre se trouvait sous TypeScript. Deno a bâti son runtime en Rust et créé rusty_v8, les bindings qui permettent à un hôte Rust d’embarquer V8 sans faire basculer l’application elle-même en C++. JavaScript restait la surface portable ; Rust prenait en charge le code où les durées de vie, les threads et la mémoire native cessent d’être des problèmes théoriques.

Ce choix paraît aujourd’hui plus visionnaire qu’à l’époque. En juillet 2026, Bun a annoncé la réécriture en Rust de son implémentation Zig après des années de use-after-free et de soucis de durée de vie autour de JavaScriptCore. Bun se garde bien d’accuser Zig, et il a raison : sans Zig, le projet initial n’aurait pas existé. La conclusion est plus précise, donc plus utile. Un runtime construit autour d’un moteur à ramasse-miettes profite énormément d’un langage sous-jacent qui impose les règles de propriété.3

La réécriture est aussi arrivée de la manière la plus 2026 qui soit : Bun a confié une grande partie de la traduction à un modèle en préversion et publié le prompt qui avait lancé l’opération, Claude, rewrite Bun in Rust. Le choix de Rust se défend facilement. Confier l’essentiel de la réécriture d’un runtime à un modèle pas encore sorti, voilà la partie qui mérite un sourcil levé. Si l’IA doit écrire du code système, autant lui donner un langage qui refuse de lui faire confiance.

La bibliothèque standard n’est plus une loterie de paquets

Le runtime n’était que le premier héritage. Le suivant, c’était l’endroit d’où venait le code.

npm reste l’une des grandes réussites de l’écosystème JavaScript. Il s’est aussi développé autour de Node, de CommonJS et de paquets qui publient du JavaScript généré plutôt que le TypeScript réellement écrit par leurs auteurs.

JSR a conservé la compatibilité avec npm tout en changeant les postulats. Les paquets publient leur source TypeScript et des modules ES standard, Deno les consomme directement, Node reçoit du JavaScript transpilé et des déclarations, et tout ce qui n’est pas Node cesse d’être traité comme un cas particulier.

main.ts
// the registry became a prefix, not a migration
import { join } from 'jsr:@std/path';
import { z } from 'npm:zod';
La fiche de @std/path sur JSR : les runtimes compatibles, un score JSR de 100 %, une licence MIT, 139 795 téléchargements hebdomadaires et 1 738 paquets dépendants.La fiche de @std/path sur JSR : les runtimes compatibles, un score JSR de 100 %, une licence MIT, 139 795 téléchargements hebdomadaires et 1 738 paquets dépendants.
FigureLe même paquet vu par le registre : il déclare les runtimes avec lesquels il fonctionne plutôt que celui pour lequel il a été écrit, et la ligne d’installation est l’import.5

L’éditeur de paquets le plus important sur JSR est peut-être aussi le moins palpitant : @std. Chemins, archives, encodages, utilitaires HTTP et formats de données ne devraient pas commencer par un concours de popularité entre cinq paquets, suivi d’une enquête médico-légale pour savoir quel mainteneur possède encore le droit de publier. Le code courant devrait relever de l’infrastructure, pas d’une loterie.

Vite a supprimé le travail dont le navigateur n’avait plus besoin

L’outillage frontend réglait un autre type d’héritage. Webpack avait résolu un vrai problème des navigateurs : pendant des années, le bundling fut le seul moyen pratique de transformer une forêt de fichiers source et de formats de modules incompatibles en quelque chose qu’un navigateur savait charger. Puis les navigateurs ont adopté les modules natifs, la contrainte d’origine a disparu, et les builds de développement ont continué à tout regrouper malgré tout.

Vite a posé la question évidente : et si on arrêtait ?

Comparaison entre un serveur de développement fondé sur un bundle et un serveur ESM natif qui transforme les modules à la demande.Comparaison entre un serveur de développement fondé sur un bundle et un serveur ESM natif qui transforme les modules à la demande.
FigureLes mêmes modules, le même navigateur. Seule change la quantité d’application qui doit exister avant l’arrivée du premier octet.6

En développement, Vite sert les modules source et les transforme à la demande. L’application peut donc grossir sans que chaque démarrage déclenche une reconstruction complète. La production reçoit toujours un bundle optimisé. Le développeur cesse simplement de payer le coût de la production après chaque frappe au clavier. Une petite décision architecturale aux conséquences humaines immenses : nous avions transformé l’enregistrement d’un fichier en pipeline de build, puis baptisé la pause-café « expérience développeur ».

Vite n’avait pas besoin de devenir un framework pour corriger cela. Les projets React, Vue, Preact, Svelte ou TypeScript pur ont tous profité du progrès sans changer de nature. Le motif se dessinait. Les outils qui réussissaient ne réclamaient pas une nouvelle réécriture. Ils repéraient l’hypothèse coûteuse et la supprimaient.

Preact et Fresh ont refait du « moins » une qualité

Accélérer les builds a réduit l’attente sur la machine du développeur. Cela n’a rien changé à ce que nous envoyions sur toutes les autres. Pendant un temps, la réponse à une application web lente était davantage d’application web : plus d’état côté client, plus d’hydratation, plus de cache, un nouvel écran de chargement pour dissimuler le travail. Une fois lancée, elle pouvait sembler rapide. Mais la lancer était devenu un métier à part entière.

Preact a conservé le modèle de composants et l’essentiel de l’API déjà utilisée, dans une implémentation beaucoup plus petite. Il a ainsi prouvé que compatibilité et retenue ne s’opposent pas. Fresh a poussé le principe plus loin : les pages sont rendues sur le serveur, et seules les parties qui doivent rester interactives reçoivent du JavaScript. Ces parties sont des îlots. Tout le reste est du HTML, comme le web l’avait prévu.

Une page HTML essentiellement statique, où seuls la recherche et le panier sont signalés comme des îlots interactifs.Une page HTML essentiellement statique, où seuls la recherche et le panier sont signalés comme des îlots interactifs.
FigureLa question n’est plus de savoir comment hydrater la page, mais quelles parties ont réellement besoin de JavaScript.7

Envoyer du HTML n’a rien de nouveau. C’est précisément ce qui rend l’idée si utile. Pendant tout ce temps, HTML attendait patiemment sous le spinner.

La question par défaut n’est plus comment hydrater cette page ?, mais quelle partie de cette page a réellement besoin de JavaScript ? En général, pas grand-chose.

Les frameworks ont repris les décisions en main

En envoyer moins est une décision, et quelqu’un doit la prendre. Pendant des années, chaque projet l’a prise seul, comme toutes les autres.

React s’est imposé en partie parce qu’il laissait tant de choix ouverts. L’écosystème construit autour de lui a fini par les refermer. Next.js, Nuxt, SvelteKit, Astro et Fresh décident où vivent les routes, ce que le serveur rend, comment les données arrivent jusqu’à la page et combien de code traverse la frontière du navigateur. Leurs réponses diffèrent radicalement, mais tous s’accordent sur une chose : une application a besoin de réponses. Un nouveau projet ne commence plus par trois jours à choisir un routeur, un outil de build, une stratégie de rendu, un lanceur de tests et le dossier où tout cela sera expliqué au prochain développeur — qui contestera la moitié des décisions. Ils ont aussi appliqué la leçon que Deno avait dû apprendre : ajouter leurs convictions par-dessus un écosystème que les gens avaient déjà choisi.

Mais un framework web observe le système depuis la page vers l’intérieur. Il peut décider où placer un endpoint. Il décide rarement de ce qui arrive lorsque cet endpoint lance du travail dans trois services, que le deuxième réussit, que le troisième expire et que le processus redémarre avant le retour de la réponse. Ce n’est pas une critique, c’est une frontière. Ces frameworks ont pris en charge une part bien plus large de l’application que les bibliothèques précédentes ; il nous revenait toujours d’assembler l’architecture métier.

Les standards ont cessé d’appartenir aux fournisseurs

Heureusement, nous n’avons plus à inventer le langage de cette architecture.

Une API peut être décrite avec OpenAPI avant même l’écriture d’un client, et cette description génère documentation, validation et clients pour d’autres équipes dans d’autres langages. L’essentiel n’est pas le site généré qui énumère les endpoints. C’est que producteur et consommateur cessent d’entretenir deux versions de la vérité. Le contract-first place cet accord au début : le schéma n’est pas une formalité rédigée après le code, il est la frontière que le code implémente. Dès lors, un outil peut refuser une rupture au lieu de laisser un client la découvrir.

Une même rupture sur deux chronologies, tracées de part et d’autre d’une seule ligne verticale de mise en production. En haut, la spécification est écrite à partir du code : le code, puis un client écrit pour s’y conformer, puis une spécification générée depuis le code — après quoi plus rien ne refuse le changement, et la cellule cuivrée de la rupture tombe à droite de la mise en production, découverte par un client. En bas, le schéma est écrit en premier : le schéma, puis le serveur généré à partir de lui, puis les clients générés à partir de lui, et la cellule cuivrée identique tombe à gauche de la mise en production, refusée par l’outil.Une même rupture sur deux chronologies, tracées de part et d’autre d’une seule ligne verticale de mise en production. En haut, la spécification est écrite à partir du code : le code, puis un client écrit pour s’y conformer, puis une spécification générée depuis le code — après quoi plus rien ne refuse le changement, et la cellule cuivrée de la rupture tombe à droite de la mise en production, découverte par un client. En bas, le schéma est écrit en premier : le schéma, puis le serveur généré à partir de lui, puis les clients générés à partir de lui, et la cellule cuivrée identique tombe à gauche de la mise en production, refusée par l’outil.
FigureLa même rupture, de part et d’autre de la mise en production. Le contract-first n’ajoute pas un contrôle : il avance le schéma assez tôt pour qu’un contrôle existe.

L’observabilité a suivi le même chemin. Logs, métriques et traces commençaient autrefois par le choix d’un fournisseur ; OpenTelemetry leur a donné un modèle neutre et une manière commune de transporter les données. L’instrumentation appartient ainsi à l’application plutôt qu’au tableau de bord que l’entreprise paie cette année. OpenTelemetry a atteint en 2026 le niveau « graduated » de la CNCF. La date compte moins que ce qu’elle démontre : la télémétrie partagée est désormais une infrastructure commune aux langages, aux plateformes et aux fournisseurs.

Le Collector OpenTelemetry : les récepteurs OTLP, Jaeger et Prometheus alimentent des pipelines de traitements — batch, attributs, filtres — qui exportent vers ces mêmes trois backends.Le Collector OpenTelemetry : les récepteurs OTLP, Jaeger et Prometheus alimentent des pipelines de traitements — batch, attributs, filtres — qui exportent vers ces mêmes trois backends.
FigureLe Collector sépare l’instrumentation du stockage : récepteurs, traitements et exportateurs peuvent évoluer sans réécrire l’application.8

Je pensais autrefois que tout cela appartenait à la dernière phase de durcissement d’un projet sérieux. Cette phase est généralement une autre façon de découvrir, trop tard, qu’un contrat non documenté et une requête impossible à tracer coûtent cher à réparer. Contrat, identifiant de trace et endpoint de santé devraient exister dès la première fonctionnalité, pas lorsque le premier client demande pourquoi elle a échoué.

Les standards ne nous manquaient pas. Nous les traitions comme des finitions facultatives.

Les équipes JavaScript n’étaient pas particulièrement incapables de les utiliser. Elles venaient d’un écosystème qui célébrait le choix de chaque pièce. Sur des plateformes plus directives, les mêmes capacités arrivaient avec le framework et se fondaient dans la définition d’une application normale.

L’architecture est devenue une affaire de frontières, pas de taille

La décennie qui a mis les microservices à la mode nous a aussi appris à ne plus en faire une unité de vertu. Découper un système peut créer des frontières utiles. Cela peut aussi transformer un appel de fonction en appel réseau, une transaction en problème de coordination et un déploiement en douze déploiements. Un petit service n’est pas automatiquement indépendant, pas plus qu’un monolithe n’est automatiquement emmêlé.9

Mon vocabulaire s’est fixé sur deux unités plus larges. Un macroservice est une capacité métier substantielle, avec ses propres données et une frontière qui mérite une exploitation indépendante ; l’industrie le rangerait plus volontiers du côté de l’architecture orientée services que de tout ce que suggère le mot micro.10 À l’intérieur, je préfère les services par cas d’usage : du code organisé autour d’une tâche réalisée par le métier, plutôt qu’autour de contrôleurs, de dépôts et de couches utilitaires. Le terme établi est architecture en tranches verticales. Modifier un cas d’usage reste dans sa tranche, au lieu de traverser toutes les couches horizontales de l’application.

Les noms importent moins que la direction. Nous avons cessé de demander jusqu’où réduire un service pour nous demander quelles modifications devraient voyager ensemble.

Deux manières de regrouper la même application. À gauche, trois couches — contrôleurs, services applicatifs, dépôts — traversées par une même modification. À droite, un macroservice contenant quatre tranches par cas d’usage — soumettre, approuver, publier, notifier — où cette même modification tient dans une seule tranche.Deux manières de regrouper la même application. À gauche, trois couches — contrôleurs, services applicatifs, dépôts — traversées par une même modification. À droite, un macroservice contenant quatre tranches par cas d’usage — soumettre, approuver, publier, notifier — où cette même modification tient dans une seule tranche.
FigureRegrouper par couche disperse une modification dans du code partagé. Regrouper par cas d’usage la maintient dans une tranche d’une capacité qui possède ses propres données.

Les événements sont utiles lorsque le travail franchit ces frontières : une capacité annonce ce qui s’est produit sans connaître à l’avance toutes les réactions. Mais le diagramme est la partie facile. Le plus dur consiste à se souvenir de ce qui a déjà eu lieu, à réessayer sans danger et à réparer un workflow qui a terminé quatre étapes avant d’échouer à la cinquième. Là encore, rien de nouveau. Les sagas décrivaient déjà les transactions longues en 1987 ;12 l’event sourcing, CQRS et l’idempotence sont documentés depuis des années.13 L’exécution durable a donné au runtime un moyen pratique d’enregistrer la progression afin de reprendre après un crash, plutôt que de redémarrer avec pour seules ressources la mémoire et l’espoir.14

La découverte embarrassante n’était pas que l’industrie manquait de réponses. C’était de voir combien de fois nous avions reconstruit des réponses partielles sans employer les noms qui nous auraient conduits au reste. Une fois nommés, ces patterns peuvent entrer dans la plateforme : un service généré avec une frontière d’idempotence, un workflow durable par défaut, un événement qui transporte un contexte de trace.

Un seul langage n’a jamais été le but

Rien là-dedans n’impose que le code derrière une frontière soit du TypeScript. Deno en est la démonstration : TypeScript en surface, Rust en dessous.

Plusieurs langages excellent dans des domaines différents, et la seule position honnête consiste à les utiliser. Go reste la bonne réponse pour un petit worker autonome : un programme compilé, presque aucune cérémonie entre le build et la machine. Rust va plus loin lorsque la maîtrise de la mémoire et les performances natives comptent ; pour une catégorie croissante de code système, noyau Linux compris, il est devenu le successeur du C. Le .NET moderne n’a pas disparu dans les archives de l’informatique d’entreprise, et P/Invoke reste le moyen le plus pratique de dialoguer avec la bonne vieille DLL qui, discrètement, fait tourner la moitié d’une entreprise sous Windows. La JVM demeure redoutable pour les calculs intensifs et les applications fortement concurrentes, même si, apparemment, Java lui-même ne peut pas négocier avec le nombre de cœurs de la machine. Python règne sur le machine learning pour de bonnes raisons. Bash et PowerShell résolvent encore de petits problèmes d’exploitation avant que la plupart des frameworks applicatifs aient fini de s’installer.

Le danger ne vient pas du langage. Il apparaît lorsque chacun apporte son propre modèle de déploiement, sa configuration, ses permissions, ses logs, ses reprises et sa façon non documentée de transmettre des données au suivant.

Le code polyglotte n’est pas le problème. Le code polyglotte sans contrat l’est.

Une tâche peut déclarer son entrée et sa sortie, limiter son accès au système de fichiers et au réseau, fixer une mémoire et une durée maximales, recevoir un identifiant de trace et envoyer ses logs à un endroit unique. Derrière cette frontière, elle peut prendre la forme d’un exécutable Go, d’une bibliothèque Rust, d’un worker .NET, d’un script Python ou de dix lignes de Bash. Un script shell à portée limitée, isolé et surveillé n’est pas un fardeau hérité du passé. Un script qui récupère les identifiants de production, tourne éternellement et communique en ajoutant des lignes dans un fichier mystérieux, si.

L’ancienne fragmentation venait de l’assemblage de plateformes. Le meilleur modèle consiste à assembler des tâches et à traiter le langage de chacune comme un détail d’implémentation.

Le déploiement est devenu un spectre

Les contrats ont convergé. Le déploiement, non, parce que le marché n’a jamais eu une seule exigence.

Le cloud nous a dispensés de posséder du matériel. Le serverless, de gérer un processus de longue durée. Les conteneurs ont permis à l’application et à son runtime de voyager ensemble. Kubernetes a fourni aux grandes organisations un plan de contrôle commun pour de nombreux conteneurs, équipes et machines. Chacune de ces évolutions a résolu un vrai problème, et chacune a introduit sa propre hypothèse.

Kubernetes en est l’exemple le plus clair : il excelle à résoudre les problèmes à la taille de Kubernetes ; les ennuis commencent quand nous agrandissons le problème jusqu’à ce qu’il soit admissible.

L’architecture officielle d’un cluster Kubernetes. Un cadre « control plane » contient etcd, le kube-api-server, l’ordonnanceur, le controller manager et le cloud-controller-manager, qui dialogue avec l’API d’un fournisseur cloud. Deux nœuds l’accompagnent, chacun exécutant un kubelet et un kube-proxy au-dessus d’un runtime de conteneurs qui héberge les pods. Les flèches convergent vers l’api-server.L’architecture officielle d’un cluster Kubernetes. Un cadre « control plane » contient etcd, le kube-api-server, l’ordonnanceur, le controller manager et le cloud-controller-manager, qui dialogue avec l’API d’un fournisseur cloud. Deux nœuds l’accompagnent, chacun exécutant un kubelet et un kube-proxy au-dessus d’un runtime de conteneurs qui héberge les pods. Les flèches convergent vers l’api-server.
FigureUn magasin clé-valeur, un serveur d’API, un ordonnanceur, deux contrôleurs et un agent sur chaque nœud — avant que votre application ne démarre. Juste à l’échelle d’un cluster, une hypothèse partout ailleurs.15

Mais toutes les cibles de ce spectre suivent la même logique. La plupart des logiciels n’ont pas besoin de tourner partout, mais les produits n’ont pas toujours ce luxe. Une application publique peut parfaitement convenir à une plateforme managée, tandis que le même produit vendu à un hôpital doit rester dans son réseau, que le client suivant exploite déjà Kubernetes et refuse tout ce qui le contourne, et que le site industriel n’a ni runtime de conteneurs ni connexion Internet.

À ce stade, le déploiement fait partie de l’architecture ; ce n’est plus la dernière commande d’un pipeline. Configuration, stockage, découverte de services, observabilité et mises à jour doivent tous survivre au déplacement. Un adaptateur boulonné à la fin peut traduire une commande de build. Il ne peut pas retirer une hypothèse inscrite dans l’application par le framework dès le premier jour.

Aspire est la tentative la plus nette de refuser cette hypothèse dès le départ, et elle mérite le détour même si vous n’écrivez jamais une ligne de C#. Vous décrivez la topologie de l’application dans du code — cette base de données, cette API, ce frontend, et lequel dépend de quoi — et une seule description sert tous les environnements. En local, elle démarre les conteneurs et les processus dans l’ordre des dépendances, injecte les chaînes de connexion et les points de terminaison, et donne à chaque service un nom que les autres savent résoudre.

apphost.ts
const builder = await createBuilder();

const db = await builder.addPostgres("db")
.addDatabase("appdata")
.withDataVolume();

const api = await builder.addNodeApp("api", "../api", "server.js")
.withReference(db)
.waitFor(db);

await builder.addViteApp("frontend", "../frontend")
.withHttpEndpoint({ env: "PORT" })
.withReference(api);

await builder.build().run();

C’est ce même graphe de ressources qui est ensuite publié. Demandez Docker Compose et il écrit le fichier compose, demandez Kubernetes et il écrit les manifestes, demandez un environnement cloud managé et il le provisionne : un seul graphe produit une sortie spécifique à chaque cible sans dupliquer la topologie dans chaque procédure de déploiement.16 Les services générés portent déjà un point de terminaison OTLP et un nom de service, parce que la télémétrie appartenait au modèle au lieu d’être ajoutée à la fin. Et le projet est venu de .NET sans y rester : le même hôte orchestre des services Node, Python, Go et Java, et peut lui-même s’écrire en TypeScript.

C’est le même réflexe qui rend le retour de l’exécutable autonome plus intéressant qu’il n’y paraît. Un fichier unique n’est pas toujours le plus petit déploiement, mais il constitue un accord remarquablement portable.

terminal
deno compile --allow-net --output ./server main.ts
scp ./server ops@line-01.factory.local:/opt/app/

Go nous a réhabitués à cet accord, Deno l’a apporté à TypeScript et les conteneurs en proposent un semblable partout où existe un runtime adapté. En 2026, Deno Desktop a commencé à refermer l’autre moitié de la boucle : un frontend web et un backend TypeScript réunis dans une application de bureau unique, sur la WebView du système ou avec Chromium embarqué, capable d’ouvrir une fenêtre native, de dialoguer avec la machine et de conserver ses données en local.

L’enjeu dépasse la nostalgie du desktop. Les outils de productivité ont besoin de fichiers, de processus et de périphériques. Un logiciel isolé du réseau ne peut pas expédier son travail vers une fonction cloud. Un logiciel local-first ne devrait pas traverser un continent pour ouvrir une note. Et l’IA a remis la question sur la table pour tout le monde : un assistant est bien plus utile lorsqu’il peut agir sur la machine que lorsqu’il se contente de discuter dans un onglet.

Le web n’a pas remplacé le desktop. Il a enfin appris à en construire un.

La réponse mature ne consiste pas à déclarer une cible moderne et les autres obsolètes. Elle consiste à garder l’application suffisamment indépendante pour changer de cible sans imposer une réécriture.

L’IA a rendu les choix par défaut plus importants

Tout cela était déjà en mouvement lorsque le sol a tremblé de nouveau.

Tous ceux qui détestaient JavaScript pouvaient encore espérer qu’un meilleur langage finirait par devenir le choix multiplateforme par défaut. L’IA a enterré cet espoir. Demandez à un modèle de produire une preuve de concept, un site simple ou l’un de ces clones de systèmes d’exploitation que certains aiment utiliser comme benchmark : il choisira HTML, CSS, JavaScript et, très souvent, React. Ce n’est pas une affaire de goût. Les modèles apprennent à partir de l’existant, et rien ne possède autant d’exemples que le web.

Si le développement assisté par IA était resté un loisir, cela importerait peu. Il fait maintenant partie du quotidien, ce qui change le rôle des conventions. Les décisions fournies par un framework ne guident plus un seul développeur ; elles posent les rails de toutes les machines auxquelles on demandera plus tard d’étendre le projet. Donnez un dossier vide à un modèle, il assemblera l’empilement statistiquement le plus familier. Donnez-lui une base de code avec un contrat exécutable, une forme de service connue, du travail durable et des traces déjà en place : ses choix se resserrent et deviennent beaucoup plus faciles à relire.

L’IA n’a pas créé le problème de discipline. Elle a accéléré la transformation de chaque discipline absente en code.

Nous avons enfin des fondations

On pourrait lire cette histoire comme une nouvelle liste de tout ce que le web fait encore mal. J’y vois l’inverse.

Le runtime peut être sécurisé, typé et autonome sans renoncer à l’écosystème Node. Une page peut n’envoyer du JavaScript que là où il le mérite. Contrats et télémétrie peuvent traverser fournisseurs et langages. Le travail distribué peut survivre aux pannes. La même application peut être empaquetée pour une fonction cloud, un conteneur ou une machine qui n’a jamais entendu parler de l’un ni de l’autre. Ce ne sont pas des propositions. Ce sont des pièces opérationnelles, entretenues par des projets sérieux, et elles surpassent largement ce dont nous disposions il y a dix ans.

Il reste à les assembler. Aujourd’hui, une bonne équipe peut choisir ces pièces et bâtir une plateforme solide : définir la forme d’un service, relier le contrat à l’implémentation, ajouter la télémétrie, choisir le comportement des événements et du travail durable, générer les clients, écrire un parcours de déploiement pour chaque environnement. Puis une autre bonne équipe fait des choix légèrement différents et reconstruit la même couche.

Cette répétition ne prouve pas que les pièces ont échoué. Elle prouve qu’elles sont devenues des fondations.

Les générateurs de projet existent précisément parce que ce travail est répétitif. Better-T-Stack pose dix-sept sections de questions — frontend web, frontend natif, backend, runtime, API, base de données, ORM, configuration de la base, déploiement web, déploiement serveur, authentification, paiements, gestionnaire de paquets, extensions, exemples, git, installation — et produit un monorepo fonctionnel quelles que soient les réponses.17 C’est la forme honnête et non dogmatique du problème : l’outil ne prétend pas qu’une stack est la bonne, il vous rend simplement les journées que vous auriez passées à câbler la vôtre.

Le générateur Better-T-Stack. Dans la colonne de gauche, la commande CLI bun create better-t-stack@latest et une stack sélectionnée affichant onze choix, avec des étiquettes pour TanStack Router, Hono, Bun, tRPC, SQLite, Drizzle, Better-Auth, bun et Turborepo. Une barre de sections de configuration déborde du bord droit : frontend web, frontend natif, backend, runtime, API, base de données, ORM, configuration de la base, déploiement web, déploiement serveur, authentification, paiements, gestionnaire de paquets, extensions. En dessous, la section frontend web propose neuf options, de TanStack Router et Next.js à Astro ou aucun frontend web.Le générateur Better-T-Stack. Dans la colonne de gauche, la commande CLI bun create better-t-stack@latest et une stack sélectionnée affichant onze choix, avec des étiquettes pour TanStack Router, Hono, Bun, tRPC, SQLite, Drizzle, Better-Auth, bun et Turborepo. Une barre de sections de configuration déborde du bord droit : frontend web, frontend natif, backend, runtime, API, base de données, ORM, configuration de la base, déploiement web, déploiement serveur, authentification, paiements, gestionnaire de paquets, extensions. En dessous, la section frontend web propose neuf options, de TanStack Router et Next.js à Astro ou aucun frontend web.
FigureDix-sept sections de choix, et un dépôt fonctionnel à la sortie. Rien dans ce menu n’est une trace, un contrat, ni un type qui survit au réseau.17

Ce qu’un générateur ne peut pas vous donner, c’est ce qui n’apparaît qu’une fois les pièces réunies. Une télémétrie de bout en bout couvrant chacun de ces choix, des types qui survivent à la frontière réseau, des clients générés depuis un contrat, des standards de l’industrie adoptés par défaut plutôt que rajoutés après coup : ce sont des propriétés de l’assemblage, pas d’un composant en particulier. Aucun menu de composants ne les produit, aussi bon soit-il.

Et c’est là tout l’écart : les frameworks prennent les décisions jusqu’à la page et s’arrêtent au réseau ; les standards définissent les frontières et n’appartiennent à personne ; le déploiement hérite de celui des deux par lequel on a commencé. Rien ne réunit encore les trois sans demander d’en sacrifier un.

La prochaine génération de frameworks n’a donc pas besoin d’inventer un routeur, un format de schéma ou une plateforme de déploiement de plus. Elle doit prendre les standards et les patterns auxquels nous faisons déjà confiance, en faire les choix par défaut et préserver suffisamment du web existant pour que les gens puissent réellement les adopter.

C’est un problème bien plus circonscrit que de réparer JavaScript. Et c’est enfin un problème que nous savons résoudre.

Commencez par Comment le web est devenu incontournable.

Sources

  1. 110 Things I Regret About Node.js — Ryan Dahl à la JSConf EU 2018, enregistrement de la conférenceyoutube.com
  2. 2Announcing Stable V8 Bindings for Rust — Annonce officielle de Deno par Ryan Dahldeno.com
  3. 3Rewriting Bun in Rust — Annonce officielle de Bun par Jarred Sumnerbun.com
  4. 4Rewriting Bun in Rust sur X — 456 réponses · 1,1 k republications · 8,9 k mentions J’aime · 5,1 k signets · 3,7 M vues, relevés le 30 juillet 2026x.com
  5. 5@std/path sur JSR — Fiche officielle du registre, relevée le 30 juillet 2026jsr.io
  6. 6Why Vite — Explication officielle du serveur de développement fondé sur les modules ESM natifsvite.dev
  7. 7Islands — Documentation officielle de Freshusefresh.dev
  8. 8OpenTelemetry Collector — Schéma de la documentation officielle, CC BY 4.0, couleurs inversées pour le thème sombreopentelemetry.io
  9. 9Microservice Premium — Martin Fowler, 13 mai 2015martinfowler.com
  10. 10Service-Based Architecture Style — Mark Richards et Neal Ford, Fundamentals of Software Architecture, chapitre 13oreilly.com
  11. 11Vertical Slice Architecture — Jimmy Bogard, 19 avril 2018jimmybogard.com
  12. 12Sagas — Hector Garcia-Molina et Kenneth Salem, ACM SIGMOD 1987, doi 10.1145/38713.38742dl.acm.org
  13. 13Event Sourcing — Martin Fowler, 12 décembre 2005martinfowler.com
  14. 14Understanding Temporal — Documentation officielle sur l’exécution durabledocs.temporal.io
  15. 15Cluster Architecture — Documentation officielle Kubernetes, figure 1, CC BY 4.0, couleurs inversées pour le thème sombrekubernetes.io
  16. 16How Aspire deployment works — Documentation officielle : un seul graphe de ressources produit une sortie de déploiement propre à chaque cibleaspire.dev
  17. 17Better-T-Stack Builder — Générateur de projet officiel, capturé le 30 juillet 2026better-t-stack.dev

Journal

À lire aussi

  • Quand contenu et processus métier se rencontrent

    Le site d’Autocorner est devenu le socle d’un projet plus vaste : formulaires réutilisables, disponibilités réelles, notifications et outils internes qui relient parcours client et travail quotidien.

    ArchitectureContenuCommerce6 min de lecture
  • Une véritable expérience multimarque et multicanale

    Autocorner n’avait pas seulement besoin d’un meilleur site, mais d’une plateforme capable de réunir marques, centres, contenus et véhicules disponibles sans transformer chaque évolution en nouveau projet.

    ArchitectureMigrationCommerce6 min de lecture
  • Un site simple, flexible et piloté par un CMS

    Un site élégant et facile à faire évoluer, que le cabinet peut mettre à jour lui-même, sans développeur et sans friction inutile.

    ContenuArchitecture4 min de lecture

Prochaine étape

Le problème vous parle ?

Si l’un de ces cas ressemble à votre système, la première conversation ne coûte rien.