Part 0 · 3 chapters · ~18 min

What Node Actually Is

The runtime, not the language: where Node came from, the parts it is built from (V8, libuv, OpenSSL, zlib, c-ares, llhttp and the bindings that join them), how its release lines work, how it compares with Deno and Bun, and how it is built from source.

1

The 2009 origin and the C10K problem

Node exists because of one engineering observation: most server time is spent waiting on IO, and threads are an expensive way to wait. A thread-per-connection server holds a stack (often 1 to 8 MB of reserved address space) and a kernel scheduling entry for every idle connection. An event loop holds a few hundred bytes of state per connection and asks the kernel which ones are ready.

code
// the C10K idea in eight lines: one thread, many sockets, nobody blocks
import net from 'node:net';
let open = 0;
net.createServer(sock => {
  open++;
  sock.on('data', d => sock.write(d));            // echo, never blocking
  sock.on('close', () => open--);
}).listen(7000);
setInterval(() => console.log('open connections', open, 'rss MB', (process.memoryUsage().rss / 1e6).toFixed(1)), 2000);
try it
Open a few thousand idle connections against this server (a small loop of net.connect in another process) and watch RSS. Memory grows by kilobytes per connection, not megabytes. That is the whole original pitch.
FROM C10K TO TODAY
the decisions that shaped Node
1999C10K problemnamed
swipe the figure sideways, or tap expand for full screen
1/6
C10K
Dan Kegel named the C10K problem: serving ten thousand concurrent connections on one machine. Thread-per-connection servers ran out of memory and context-switch budget; event-driven servers using epoll and kqueue did not.
ten thousand connections, one machinethreads per connection did not scale
2

The architecture: V8, libuv and the C libraries

layerwhere in the repowhat it does
your code + lib/lib/*.jsthe standard library, written mostly in JavaScript
bindingssrc/*.ccC++ that exposes native functionality to lib/ via internalBinding()
V8deps/v8parsing, compiling, executing JS, heap and GC
libuvdeps/uvevent loop, non-blocking sockets, timers, threadpool, signals, child processes
OpenSSLdeps/opensslTLS, hashing, ciphers, key generation
zlib, brotlideps/zlib, deps/brotlicompression streams
c-aresdeps/caresasynchronous DNS for dns.resolve* (not dns.lookup, see part 2)
llhttp, nghttp2, ngtcp2deps/llhttp, …HTTP/1.1 parsing, HTTP/2 framing, QUIC work
ICUdeps/icu-smallIntl, Unicode normalisation, time zones
code
// what your Node was built with
console.log(process.versions);   // { node, v8, uv, openssl, zlib, ares, llhttp, nghttp2, icu, ... }
console.log(process.config.variables.node_shared_openssl);   // vendored or system OpenSSL?
THE NODE ARCHITECTURE
your JavaScript on top of bindings, V8, libuv and a handful of C libraries
your codeJSnode:* moduleslib/*.jsbindingssrc/*.cc (C++)V8JS enginelibuvloop + threadpoolOpenSSLcrypto, TLSzlibcompressionc-aresDNSllhttpHTTP parser
swipe the figure sideways, or tap expand for full screen
1/6
your code
You write JavaScript against Node's standard library (node:fs, node:http, node:crypto). Those modules are themselves mostly JavaScript, living in lib/ in the Node repository.
your code calls lib/*.js, which is JavaScript toonode:fs, node:http, node:crypto are thin JS wrappers
3

Release lines, governance, and Node vs Deno vs Bun

Node ships a new major every six months. Even-numbered majors become LTS in October: about 6 months as Current, then roughly 30 months of Active and Maintenance LTS. Odd majors never become LTS. Production should run Active LTS, and plan the upgrade before Maintenance ends.

NodeDenoBun
engineV8V8JavaScriptCore
runtime languageC++ (+ JS in lib/)Rust (Tokio)Zig
event looplibuvTokiocustom, io_uring/kqueue
security modelopt-in permission modeldeny by default, flags to allownone built in
TypeScripttype stripping (erasable syntax)built inbuilt in transpiler
npm compatibilitynativenpm: specifiers, node: moduleshigh, Node APIs reimplemented

The architectural point: Deno and Bun replace libuv and the C++ glue, so the event loop details in part 3 differ, while V8 knowledge (part 2) transfers to Deno but not to Bun.

code
# building Node from source (what a build produces)
git clone https://github.com/nodejs/node && cd node
./configure            # Python script: detects the platform, writes config.gypi
make -j8               # GYP generates Makefiles; outputs out/Release/node
./out/Release/node -p process.versions.node
# configure flags worth knowing: --debug, --without-intl, --shared-openssl, --enable-lto
staff framing
A senior engineer knows the event loop exists. A staff engineer can name the thread a given operation runs on (main, threadpool, V8 GC helper, kernel) and therefore predict what happens to latency when it slows down.