WinterTC: Write once, run anywhere (for real this time)

The WinterCG community group was recently promoted to a technical committee, signaling a growing maturity for the standard that aims to solidify JavaScript runtimes. Now is good time to catch up with this key feature of modern JavaScript and the web development landscape.

The WinterTC manifesto

To understand what WinterTC is about, we can begin with the committee’s own manifesto:

The ultimate goal of this committee is to promote runtimes supporting a comprehensive unified API surface that JavaScript developers can rely on, regardless of whether their code will be used in browsers, servers, or edge runtimes.

What is notable here is that it was only very recently that the JavaScript server-side needed unification. For over a decade, this space was just Node. Nowadays, we have a growing abundance of runtime options for JavaScript and TypeScript; options include Node, Deno, Bun, Cloudflare Workers, serverless platforms like Vercel and Netlify, and cloud environments like AWS’s LLRT. While this variety indicates a healthy response to the demands of modern web development, it also leads to fragmentation. As developers, we may find ourselves managing constant mental friction: forced to worry about the where rather than the what.

Also see: The complete guide to Node.js frameworks.

WinterTC proposes to smooth out these hard edges by creating a baseline of guaranteed API surface across all JavaScript runtimes. It’s a project whose time has come.

Ecma TC55: The committee for interoperable web runtimes

WinterTC isn’t just a hopeful suggestion; it’s an official standard that any runtime worth its salt will need to satisfy. WinterTC (officially Ecma TC55) is a technical committee dedicated to interoperable web runtimes. It sits alongside TC39, the committee that standardizes JavaScript itself.

WinterTC is a kind of peace treaty between the major players in the web runtimes space—Cloudflare, Vercel, Deno, and the Node.js core team.

The main insight of TC55, which underpins the solutions it seeks, is simple: The browser is the baseline.

Instead of inventing new server-side standards, like a new way to handle HTTP requests, WinterTC mandates that servers adopt browser standards (an approach that successful APIs like fetch had already driven into de facto standards). It creates a kind of universal standard library for JavaScript that exists outside the browser but provides the same services.

The convergence

To understand what this new standardization means for developers, we can look at the code. For a long time, server-side and client-side code relied on different dialects:

  • Browser: fetch for networking, EventTarget for events, and web streams.
  • Node: http.request, EventEmitter, and Node streams.

The server has gradually absorbed the browser way, and is now standardized by WinterTC:

  • fetch: The universal networking primitive is now standard on the back end.
  • Request / Response: These standard HTTP objects (originally from the Service Worker API) now power server frameworks.
  • Global objects: TextEncoder, URL, Blob, and setTimeout work identically everywhere.

This convergence ultimately leads to the realization of the “isomorphic JavaScript” promise. Isomorphic, meaning the server and client mirror each other. You can now write a validation function using standard URL and Blob APIs and run the exact same file on the client (for UI feedback) and the server (for hard security).

I thought isomorphic JavaScript was on the horizon when Node came out, and I was not alone. Better late than never.

The new server battlefields

When every runtime is trending toward supporting the same APIs, how do they continue to distinguish themselves? If code is really portable, the runtimes can no longer compete on API availability (or even worse, on API lock-in). Instead, much like web frameworks, they must compete on the basis of developer experience.

We are seeing distinctive profiles emerge for each runtime:

  • Bun (tooling + speed): Bun isn’t just a runtime; it’s an all-in-one bundler, test runner, and package manager. Its other selling point is raw speed.
  • Deno (security + enterprise): Deno focuses on security (with its opt-in permission system) and a “zero-config” developer experience. It has found a strong niche powering the so-called Enterprise edge. It also has the Deno Fresh framework.
  • Node (familiarity + stability): Node’s asset is its massive legacy ecosystem, reliability, and sheer familiarity. It is catching up by adopting WinterTC standards, but its primary value proposition is boring reliability—a feature that holds considerable weight in the development world.

The cloud operating system

WinterTC also has implications for the deployment landscape. In the past, you chose an operating system; today, you choose a platform.

Platforms like Vercel and Netlify are gradually becoming a new OS layer. WinterTC acts as the POSIX for this emerging cloud OS. Just as POSIX allowed C code to run on Linux, macOS, and Unix, WinterTC allows JavaScript code to run on Vercel, Netlify, and Cloudflare without much finagling.

However, developers should be wary of the new lock-in. Platforms can’t really lock you in with the language anymore (WinterTC makes it easier to swap deployment engines), but they can still trap you with data. Services like Vercel KV, Netlify Blobs, or Cloudflare D1 offer incredible convenience, but they are proprietary. Your compute might be portable, but your state is not. Not that this is anything new—databases, especially managed ones, are inherently a point of lock-in.

The poster child: Hono

If you want to see the standardized server in action today, look no further than Hono. Hono is the Express.js of the WinterTC world. It’s a lightweight web framework that runs natively on Node, Deno, Bun, Cloudflare Workers, and Fastly, or even straight in the browser.

It’s important to note that, while Hono has similarities to Express, it does not use the familiar Express req and res objects. Express objects are wrappers around Node-specific streams, IncomingMessage, and are mutable and closely tied to the Node runtime. Hono objects, by contrast, are the standard Fetch API Request and Response objects. They are immutable and universal. Because it is built on these standards, a Hono router looks familiar to anyone who has used Express, but it is infinitely more portable:

import { Hono } from 'hono'
const app = new Hono()

app.get('/', (c) => {
  return c.text('Hello InfoWorld!')
})

export default app

You could deploy this code to a $5 DigitalOcean droplet running Node, move it to a global edge network on Cloudflare, or even run it inside a browser service worker to mock a back end, all without changing anything.

The universal adapter: Nitro

While Hono represents the “pure” approach (writing code that natively adheres to standards), as developers, we often need more power and greater abstraction—things like file-system routing, asset handling, and build pipelines. This is where Nitro comes in.

Nitro, which is part of the UnJS ecosystem, is a kind of universal deployment adapter for server-side JavaScript. It is the engine that powers frameworks like Nuxt and Analog, but it also works as a standalone server toolkit.

Nitro gives you a higher order layer atop WinterTC. Nitro gives you extra powers while smoothing out some of the quirks that distinguish runtimes. As an example, say you wanted to use a specific Node utility, but you were deploying to Cloudflare Workers. Nitro would automatically detect the target environment and poly-fill the missing features or swap them for platform-specific equivalents during the build process.

With Nitro, you can build complex, feature-rich applications today that are ready for the universal, WinterTC driven future.

Conclusion

By acknowledging the browser as the baseline, we might finally fulfill the promise of “write once, run anywhere.” We’ll finally get our isomorphic JavaScript and drop the mental overhead of context switching. There will always be a distinction between front-end and back-end developers, with the former being involved with view templates and reactive state and the latter touching the business logic, file system, and datastores. But the reality of the full-stack developer is becoming less divisive at the language level.

This movement is part of an overall maturation in the language, web development in general, and the server-side in particular. It feels like the JavaScript server is finally catching up with the browser.

Go to Source

Author: