The runtime under almost everything we ship
Technology
Node is a runtime, not a framework. Next.js, SvelteKit, Astro, and Remix all run on it, the build toolchain is Node even when what deploys is static HTML, and most of the glue we write (webhooks, route handlers, sync jobs, CLIs) is a Node process somewhere.
So the interesting question is never whether to use Node. It is where the process lives: a serverless function that starts per request and forgets everything, or a long lived server holding connections open. Those are different programs with different failure modes, and we pick per workload rather than per company.
Why
The commitments that come with the runtime
- One language across the whole stack
The same TypeScript types describe the form on the page, the route handler that receives it, and the job that pushes it to a CRM. That removes a class of bug at every boundary where two languages would otherwise have to agree on a shape.
- Serverless means no memory between requests
A function starts, handles one request, and may never see the next. Anything cached in a module level variable is a coincidence, not a cache. Database connections are the usual casualty: one pool per instance, and instances multiply exactly when traffic does.
- Cold starts are a budget, not a bug
The first request into a cold instance pays for boot and module loading, and every dependency imported on that path is part of the bill. We keep hot paths thin, defer heavy imports, and accept a cold start only where a visitor will not feel it.
- Some work needs a process that stays up
Live collaboration, websockets, and anything holding state between clients want a long lived server instead. Building real time collaboration into Google's cloud IDE meant profiling memory per connection and swapping ports for Unix domain sockets, the kind of work that has no serverless equivalent.
- The event loop punishes CPU bound work
Node waits on many things at once very well and does one long computation very badly. Image pipelines, huge parses, and local model inference block every other request on that instance. Those belong in a queue, a worker, or a managed service, not in the handler.
- node_modules is most of your attack surface
The dependency tree is larger than the code you wrote, and every package in it runs with the same privileges as your application. We pin versions, keep the tree small, and treat adding a dependency as a decision that needs a reason, not a convenience.

