Deps
Deps
Deps
express: The Ghost Ship
S1 E331m · Mar 17, 2026
On February 27, 2016, the sole maintainer of JavaScript's most popular web framework posted four sentences and walked away, leaving millions of developers' projects orphaned for a decade.

express: The Ghost Ship

Four Sentences That Orphaned a Framework

This is episode three of What Did I Just Install.

On February twenty-seventh, twenty sixteen, the sole remaining maintainer of the most popular web framework in JavaScript posted four sentences.

I am closing down Express five point zero. I am quitting the Express TC now. I cannot trust anyone anymore.

His name was Doug Wilson, and he had been responsible for over eighty percent of all commits to Express over the preceding years, doing this work alone, without compensation, while employed by a company that competed with the corporation that had acquired ownership of the project. The framework used by millions of developers was now effectively orphaned. Its creator had left two years earlier for a different programming language. Its maintainer had been, in his own words, forced out.

Express five point zero, the major version Wilson had been building, would take another eight years to ship. Between the release of Express four in twenty fourteen and Express five in twenty twenty-four, the most widely used web framework in JavaScript sat in maintenance mode while the rest of the ecosystem evolved around it. This is a story about three departures, a progress bar on Flickr, a middleware pattern invented in nineteen sixty-eight, and the question of what happens when one person is holding up a significant fraction of the internet and decides to put it down.

A Progress Bar on Flickr

The story of Express begins before Express, because it begins before Node.js, and Node.js begins with a man staring at a progress bar.

Ryan Dahl grew up in San Diego. His mother bought him an Apple two C when he was six years old. He attended community college, transferred to UC San Diego where he studied mathematics, then went to graduate school for math at the University of Rochester. He was not a web developer by training. He was a mathematician who happened to write code.

In two thousand five, Dahl was trying to build a file upload progress bar. He had seen one on Flickr and realized the browser did not know how much of the file had been uploaded. It had to query the web server. The technologies available at the time offered only unsatisfying solutions. The problem stuck with him. He started thinking about how web servers handle requests, and he noticed something. Most web workloads are not computationally heavy. They are waiting. Waiting for a database to respond. Waiting for a file to finish writing. Waiting for another server to reply. The traditional approach, one thread per request, meant thousands of threads sitting idle, consuming memory, doing nothing but waiting.

Dahl was inspired by how Nginx, the Russian web server, handled this problem, using an event-driven model where a single thread managed many connections by reacting to events rather than blocking. He thought: what if you built a server-side runtime around this idea, using JavaScript, a language that already understood event-driven programming from the browser?

On November eighth, two thousand nine, at JSConf EU in Berlin, Dahl presented Node.js to an audience of about a hundred and fifty people. He demonstrated a fully functional web server in a few lines of JavaScript and an IRC chat server in about four hundred lines. He received a standing ovation, a first for that conference. Within months, tens of users became tens of thousands.

Joyent, a cloud infrastructure company, stepped in as sponsor and hired Dahl. In the process, Dahl gave them everything. The website, the credentials, the copyrights. He later said:

I gave them the ability to apply for the trademark. We changed all of the copyrights in the source code from copyright Ryan Dahl to copyright Joyent.

It was a decision he would come to regret, and its consequences would ripple through the Express story years later.

The Canadian Machine

Into this raw, new ecosystem walked TJ Holowaychuk, and the ecosystem would never be the same.

Holowaychuk lived in Victoria, British Columbia. He was entirely self-taught in both design and programming, starting as a graphic designer before writing Drupal modules and web applications. He had worked with Ruby and admired Sinatra, the micro web framework, before discovering Node.js. He was about twenty-four years old when the community started noticing him, and what they noticed was output at a scale that made people suspect he was not a single person.

The numbers are genuinely difficult to believe. At his peak, Holowaychuk had created or contributed to over five hundred npm packages. Over a million lines of code. He wrote Express, the most popular web framework for Node.js. He wrote Mocha, the most popular JavaScript testing framework. He wrote Jade, the templating engine later renamed Pug. He wrote Commander, the standard tool for building command-line interfaces in Node. He wrote debug, a tiny logging utility that would become one of the most depended-upon packages in the entire npm ecosystem. He wrote co, which brought generator-based flow control to JavaScript before async and await existed. He wrote n, a Node version manager. He wrote Connect, the middleware framework that Express was built on top of. Each of these would be a career-defining project for most developers. Holowaychuk produced them in what appeared to be his spare time.

The running joke in the Node community was that on his typical Sunday, TJ would write more JavaScript code than you would write in a week. Some people genuinely asked whether he was a real person or multiple people using one account. He did not attend conferences. He did not cultivate a personal brand. He did not write blog posts about his morning routine. He was also a photographer, though he rarely talked about that either. When asked about his productivity, he said the biggest thing was learning, and that there was no way he could have learned everything he had without open source.

He valued strong documentation and would add active contributors as maintainers on his repositories, but typically kept the core teams small. His approach to open source was matter-of-fact: anything that can be open source probably should be, because peer scrutiny maintains higher quality code.

The Micro Framework from Ruby Land

Express was born in late two thousand nine and early two thousand ten. Node.js itself was only months old. There were no established patterns for building web applications in JavaScript on the server. The language choice still seemed like a joke to many experienced developers.

Holowaychuk was a Ruby developer before he was a JavaScript developer, and he admired a Ruby framework called Sinatra. Sinatra was created by Blake Mizerany in two thousand seven and named after the musician Frank Sinatra. It was the anti-Rails. Where Ruby on Rails was opinionated and comprehensive, a full framework with conventions for everything from database access to email templates, Sinatra was minimal and flexible. A route and its handler could fit on a single line. No generators, no scaffolding, no magic. Just a direct mapping from URL to function.

Express was Sinatra for JavaScript. The original tagline in the GitHub repository was "Sinatra inspired web development framework for node.js, insanely fast, flexible, and simple." Define a route, write a handler, start the server. Three concepts, five lines, a working web application.

But the real genius was not Express itself. It was the layer underneath.

The Assembly Line

Before Express, there was Connect. Also written by Holowaychuk, hosted under the Sencha Labs GitHub organization. To understand what Connect did, you need to know that the concept it brought to JavaScript had been evolving for nearly half a century.

In October nineteen sixty-eight, at the NATO Software Engineering Conference in Garmisch, Germany, a conference attendee used the term "middleware" to describe software that sits between an operating system and an application. His reasoning: no matter how good the manufacturer's software for things like file handling, it was unsuitable, and they usually had to rewrite it. Middleware was the layer that bridged the gap between the low-level machinery and the high-level code that actually did useful work.

The concept traveled through decades of computer science and arrived in the web world through Python first. In two thousand three, Phillip J. Eby wrote PEP three thirty-three, defining WSGI, the Web Server Gateway Interface. The problem WSGI solved was that Python web frameworks were written against specific servers, and your choice of framework limited your choice of server. WSGI defined a simple universal calling convention between the two.

In two thousand seven, a developer named Christian Neukirchen, who later transitioned and now goes by Leah Neukirchen, created Rack for Ruby. Rack provided a minimal interface: a Ruby object that responds to a call method, takes an environment hash, and returns status, headers, and body. One method. Three return values. Everything else was middleware layered on top. Rack inspired similar frameworks across many languages, including Clojure, Perl, and JavaScript.

Holowaychuk brought this pipeline pattern to Node.js through Connect. The idea translates to a simple metaphor. Imagine an assembly line. A request comes in and passes through a chain of functions, one after another. Each function can read or modify the request or response, do some work like parsing cookies or checking authentication, and then call next to hand off to the next function in line. Or it can send a response, which stops the chain. Stack these functions together, each doing one small job, and you have a complete web application built from composable pieces.

Express was built on top of Connect. It added routing, the ability to map specific URLs to specific handler functions, and a view system for rendering templates. But the middleware pipeline was the foundation. Every Express application is, at its core, a stack of functions that a request passes through one by one.

This pattern became so fundamental that when you use FastAPI in Python, you are using a descendant of the same idea. Starlette, the framework underneath FastAPI, has its own middleware system that works on the same principle. The lineage runs from the nineteen sixty-eight NATO conference through WSGI through Rack through Connect through Express and into practically every web framework written in the last fifteen years.

American Independence Day

On July fourth, twenty fourteen, American Independence Day, Holowaychuk published a blog post on Medium titled "Farewell Node.js." The opening line was blunt. He had been fighting with Node.js long enough in production that he did not enjoy working with it anymore.

His complaints were specific and technical. The more he worked with distributed systems, the more frustrated he became by Node's direction, which favored performance over usability and robustness. Error handling was broken. Node did not have separate stack error handling, making reporting mediocre. The Streams API, supposed to simplify working with flowing data, was unstable. He described rewriting a large distributed system in Go and finding it robust, better performing, easier to maintain, and with better test coverage because synchronous code is generally nicer and simpler to work with.

He praised Go's standard library as powerful enough to write complete programs without third-party packages. He liked that Go's conventions eliminated debates over frameworks and standards. And he admired the Go team's eagerness to reach version two, their willingness to break things in the name of getting them right.

The Hacker News thread exploded. But here is the critical detail. When Holowaychuk left, he did not leave one project. He left hundreds. Express, Mocha, Jade, Commander, debug, co, n, Connect, and hundreds of smaller packages. It was as if a single person had been holding up a significant fraction of the Node.js ecosystem, and then that person walked out of the room.

He kept one project. Koa. It was his reimagining of Express, built with generators and later async and await, designed to fix the architectural problems he saw in Express. Koa was the framework he wished Express had been. He maintained it from Go land, which was an ironic arrangement.

After leaving Node.js, Holowaychuk founded Apex Software, a self-funded solo venture with no VC money. He built tools for serverless deployment on AWS Lambda and a structured log management service. He previously worked on the backend team at Segment, helping scale their infrastructure to billions of events per day. He did not return to the JavaScript ecosystem.

The Deal

The story of how Express changed hands is more complicated than most accounts suggest, and the details matter because they set up everything that went wrong afterward.

In July twenty fourteen, just before Holowaychuk's farewell post, a company called StrongLoop approached him about sponsoring Express. StrongLoop had been founded in twenty twelve by Node.js experts Bert Belder, Ben Noordhuis, and Al Tsang. They offered enterprise Node.js support and had built LoopBack, a commercial API framework that ran on top of Express. The sponsorship meant branding on the Express website and effectively owning the project's public-facing identity, not the source code itself.

Holowaychuk's intent was to share whatever compensation came from the deal with Doug Wilson, who had already been doing the bulk of the maintenance work. Holowaychuk wrote to Wilson that StrongLoop wanted to sponsor, meaning put branding on expressjs dot com. Wilson's response was encouraging: sponsoring seemed fine, and more guides and user-friendly content would be welcome.

But the transfer came as a shock to other Express contributors who had not been informed and lost access to the repository during the handover. When Holowaychuk later addressed the controversy, he downplayed any financial windfall.

You make it sound like I got rich or something. It was like half a month's worth of pay.

Half a month's pay for the branding rights to the most popular web framework in JavaScript. StrongLoop now had Express as a marketing asset for their enterprise products. The actual work of maintaining Express continued to fall almost entirely on one person: Doug Wilson.

The Squeeze

Douglas Christopher Wilson worked at Sencha Labs, a JavaScript tooling company that made Ext JS and Sencha Touch. Sencha Labs was also the GitHub organization that hosted Connect, the middleware framework underneath Express. This would become important.

Wilson was not a StrongLoop employee. He was an independent volunteer whose day job happened to be at a company that competed with StrongLoop in the JavaScript tooling market. By twenty fourteen, he was responsible for over eighty percent of all commits to the Express master branch. He had effectively become the sole meaningful contributor to a framework used by millions of developers worldwide. He did this work without compensation and largely without recognition.

StrongLoop, for its part, contributed fewer than five commits to Express's core over approximately eighteen months of ownership. They focused on marketing, documentation, and promoting LoopBack, their commercial product that sat on top of Express. By October twenty fourteen, LoopBack was prominently featured on the Express website. The company was building a business around a project maintained by a volunteer who worked at their competitor.

Then IBM entered. In September twenty fifteen, IBM acquired StrongLoop to add Node.js capabilities to its Bluemix cloud platform. The terms were not disclosed. Now Express, the open source project maintained by one unpaid volunteer, was nominally managed by one of the largest technology corporations on earth. And that volunteer's employer, Sencha Labs, was a direct competitor of IBM. Wilson could no longer contribute to a repository owned by his employer's competitor without facing a conflict of interest that bordered on untenable.

This is the background to Wilson's February twenty sixteen statement. It was not a sudden outburst. It was the final step in a months-long squeeze. He had been doing eighty percent of the work on critical infrastructure, for free, while a company that contributed almost nothing built a business on his labor, and then that company was acquired by a corporation that competed with his employer.

He did not voluntarily give up on Express. IBM forced me out.

The Scramble

Wilson's departure triggered a crisis. The most popular web framework in JavaScript had no active maintainer and a major version in limbo. The community scrambled.

IBM had already announced in January twenty sixteen that it would place Express under the stewardship of the Node.js Foundation incubator, a neutral governance body created after the community's own crisis, the io.js fork. The Foundation would provide a home for the project that was not controlled by any single company. Wilson transferred ownership of all his Node.js modules that Express depended on to the Foundation. His tweet was terse:

I am donating all of my Node.js modules to the Node.js foundation, to go along with Express.

The transfer happened. IBM allocated two full-time engineers to Express. Wilson eventually re-engaged after governance became transparent. But Express five, the major version upgrade, went into a limbo that would last nearly a decade.

Ten Years Between Versions

Express four was released in April twenty fourteen. The initial pull request for Express five was opened in July of the same year. What followed was one of the longest version gaps in the history of major open source frameworks.

The reasons were structural, not technical. Express four had decoupled from Connect entirely, splitting every bundled middleware into its own npm package with its own repository and maintainers. This was architecturally sound but meant that coordinating a major version across dozens of independent packages required governance that did not exist during the Wilson crisis. The alpha releases trickled out over years, from alpha one through alpha eight, sporadic and undermaintained.

Meanwhile, the ecosystem Express had created evolved around it. Node.js itself went through its own governance crisis. Ryan Dahl had left Joyent and Node.js core development around twenty twelve. Frustration with Joyent's slow governance, the long delay between Node versions, the lag in integrating V8 updates, led a developer named Fedor Indutny to create io.js in December twenty fourteen as an open-governance alternative. He was tired of waiting, and those who were close enough saw it and jumped on. io.js one point zero shipped in January twenty fifteen. By June, the communities voted to merge under the new Node.js Foundation. By September, Node.js four point zero combined both codebases. The Foundation later merged with the JS Foundation to become the OpenJS Foundation, which is where Express lives today.

Express five finally reached general availability in October twenty twenty-four, ten years after the initial pull request. The Sovereign Tech Fund, a German government initiative, provided funding through the OpenJS Foundation for a performance working group, security improvements, and preparation for Express six. The release added automatic async error handling, improved route matching with protections against denial-of-service attacks on regular expressions, and dropped support for Node.js versions below eighteen.

By March twenty twenty-five, Express five point one was tagged as the default latest version on npm, officially moving Express four to maintenance status. As of twenty twenty-six, Express five point two is current with about thirty-four million weekly downloads.

The decade-long gap between major versions stands as one of the most striking examples of what happens when critical infrastructure depends on individuals who can leave and corporations who can acquire without contributing.

The Ghost in the Machine

Here is a detail that captures the absurdity of the situation. In September twenty twenty-five, the debug package, originally created by Holowaychuk over a decade earlier but long since handed off to other maintainers, was compromised in one of the largest npm supply chain attacks ever recorded.

The attack was a phishing operation. A well-known npm developer who maintained debug, chalk, and several other packages in the color and string ecosystem received an email from what appeared to be npm support. The email came from a spoofed domain, support at npmjs dot help, designed to look like the real npm domain. The phishing page collected the developer's username, password, and a live two-factor authentication code. With those three pieces of information, the attacker fully took over the account.

At thirteen sixteen UTC, malicious versions of eighteen to twenty-five packages began appearing on npm. debug, chalk, strip-ansi, ansi-styles, supports-color, and more. The compromised packages collectively had over two billion weekly downloads. The malicious payload was a browser-only cryptostealer that hooked into cryptocurrency wallet interfaces, intercepted outgoing transactions, and silently redirected them to attacker-controlled addresses. It overrode browser fetch and XMLHttpRequest to scan API responses for blockchain addresses and replace them with visually similar alternatives.

The attack was detected within about two hours. Community members spotted suspicious code, alerts went up on GitHub, and maintainers reverted to clean versions and unpublished the compromised releases.

And here is the punchline. The attacker appears to have stolen approximately five cents of Ethereum and twenty dollars of a memecoin. The intersection of people using these npm packages in production and people making cryptocurrency transactions during those exact two hours was remarkably small. The industry, however, spent millions responding. Thousands of security teams worldwide spent hours analyzing exposure and rolling out emergency patches.

A package written by a developer who left the ecosystem eleven years earlier, as a small utility he probably spent an afternoon on, was still so deeply embedded in the infrastructure that compromising it triggered a global security response. The attack netted five cents. That is the long shadow of TJ Holowaychuk.

The Kingdoms

Express's long stagnation created a vacuum that competitors rushed to fill. The JavaScript server framework landscape in twenty twenty-six looks like a map of competing kingdoms, each with its own philosophy about what a web server should be.

Koa is Holowaychuk's own successor to Express. He rebuilt the middleware idea with modern JavaScript, replacing callbacks and the next function with async functions. The middleware stack runs downstream through each function and then flows back upstream, like a stack of nested cups. It is more elegant than Express but never achieved the same adoption. The ecosystem of pre-built middleware is smaller, the documentation assumes more expertise, and the name recognition is not there. About two point seven million weekly downloads, roughly a twelfth of Express.

Fastify was created in September twenty sixteen by Matteo Collina, an Italian Node.js core contributor working at NearForm, along with Tomas Della Vedova. They were frustrated that Express's middleware architecture made it inherently slow, and they set out to prove that a framework could be both developer-friendly and fast. It focuses on raw performance, using JSON Schema for validation and serialization to generate optimized code for specific request shapes. In benchmarks, Fastify handles about seventy-two thousand requests per second compared to Express's eighteen thousand, roughly four times the throughput. It has about two million weekly downloads.

NestJS was created in late twenty sixteen by Kamil Mysliwiec, a Polish developer. It brought a completely different philosophy, heavily structured and inspired by Angular's architecture. Decorators, dependency injection, modules, providers. Where Express says "here is a function, do whatever you want," NestJS says "here is an architecture, follow the pattern." It is built on top of Express or optionally Fastify as the HTTP adapter. It appeals to enterprise teams who want guardrails. About two million weekly downloads.

And then there is Hono, which means flame in Japanese. Created in December twenty twenty-one by Yusuke Wada, a Japanese developer who wanted to build applications for Cloudflare Workers but found the existing options insufficient. He was interested in a trie-tree-based router for speed and designed the framework to run on any JavaScript runtime: Cloudflare Workers, Deno, Bun, AWS Lambda, and Node.js. Hono represents a new generation of frameworks built for edge computing environments that did not exist when Express was created.

Despite all of these alternatives, Express remains the most downloaded web framework on npm by a factor of ten or more. The network effect is overwhelming. Every tutorial uses Express. Every course teaches Express. Every deployment platform has Express examples. Being the default is an almost unassailable advantage. The competitors are technically superior in many dimensions, faster, better typed, better architectured, more modern. None of them have Express's ecosystem or mind share. The English language of web frameworks.

Who Owns the Commons

The Express story is really three stories about the same question: what happens when community-built infrastructure meets corporate incentives?

Ryan Dahl gave Joyent the copyrights, the website, the trademark of Node.js. When governance stagnated, the community had to fork the project to regain control, then negotiate a merger. StrongLoop acquired Express's branding, contributed almost nothing to the code, and used the project to market their commercial products. When IBM acquired StrongLoop, the conflict of interest pushed out the one person who was actually doing the work. The Node.js Foundation provided neutral governance, but only after the crisis forced it.

The pattern repeats across the open source world. A person builds something. A company acquires influence over it. The person who built it, or the person maintaining it, is gradually squeezed out. The company extracts value. The community eventually reasserts control, but only after damage has been done.

Dahl acknowledged his own role in this dynamic. In his twenty eighteen talk "Ten Things I Regret About Node.js," he listed the decisions he wished he had made differently. Not sticking with promises. Not preserving V8's security sandbox. The build system, GYP, which Chrome abandoned, leaving Node as its sole user. The centrality of package dot json. The node underscore modules directory. The implicit index dot js resolution. At the end of the talk, he announced Deno, his new JavaScript and TypeScript runtime, designed to fix these mistakes. Deno has security by default, no node underscore modules, built-in TypeScript, and ES modules. It was his response to his own regrets.

The question of who owns the commons has no clean answer. Foundations help. Funding helps. But the underlying tension, between individuals who build things for love and corporations who acquire things for profit, is structural. It does not resolve. It just produces new incidents.

Where It Connects

Express might not appear directly in your dependency tree. Maybe you write Python, or Go, or Rust. But the influence is everywhere.

FastAPI sits on top of Starlette, which uses a middleware system descended from the same pipeline pattern that Connect and Express brought to the JavaScript world. When you write a middleware function that checks authentication or adds headers, you are using an architectural pattern that Holowaychuk adapted from Neukirchen's Rack, which was inspired by Eby's WSGI, which traces back to an idea first named at a NATO conference in a Bavarian ski town in nineteen sixty-eight.

And if any part of your stack touches JavaScript, even a frontend build tool or a documentation site, then somewhere beneath it is Node.js. The dependency tree underneath stretches into packages you have never examined.

Express did not just give JavaScript a web framework. It gave the entire web development world a vocabulary. Routes. Middleware. Request and response objects. The idea that a web server should be a stack of composable functions. These concepts are now so universal that it is easy to forget they had to be invented, that much of the inventing was done by a self-taught Canadian developer in Victoria, British Columbia, that he wrote over five hundred packages and walked away from all of them except one, and that the framework he left behind took a decade to release its next major version because no one could agree on who was responsible for it.

Holowaychuk is somewhere writing Go. Wilson stepped back from public open source. Dahl is building Deno. The middleware pattern they collectively popularized is running on every server in every language on every continent.

The Express repository has a new team now. The German government is funding its development through the Sovereign Tech Fund. Version six is being planned. But for ten years, the most popular web framework in JavaScript was a ghost ship, sailing on momentum and network effects, maintained by the fading contributions of people who had already left.

That was episode three.

You can build a working web server in about four lines. Open a terminal and run npm init dash y, then npm install express. Create a file called server dot js. In it, write const app equals require express and call it. Then app dot get, pass it a slash and a function that takes req and res, and inside it write res dot send with the string hello world. Last line, app dot listen three thousand. Save the file, run node server dot js, and open localhost colon three thousand in your browser. You just built the same thing that took TJ Holowaychuk's five hundred packages to make obvious. Four lines, one framework, a working server.