Why Node.js Internals Matter

Most developers use Node.js fluently but cannot explain what actually runs their code. Here is why that gap is worth closing, and what this series covers.

June 4, 20264 min read

I have written Node.js backends for years. Spun up Express servers, connected to databases, handled authentication, streamed files. All of it worked. And for a long time I thought that was enough.

Then I hit a production bug that I could not explain. Requests were piling up. The server was not throwing errors. Memory was climbing. Everything I knew about Node -- async/await, event emitters, connection pooling -- gave me no traction. I was debugging a system I did not actually understand.

That is what this series is about.

The Gap Between Using and Understanding

Using Node.js and understanding Node.js are two different skills. Most engineers develop the first. Very few go back for the second.

The reason to bother is not philosophical. It is practical.

Every software system has limitations. Node.js has them too. The event loop has phases that block each other. Worker threads have communication overhead. Streams have backpressure mechanics that bite you if you ignore them. The TCP layer underneath your HTTP calls has behavior that your application code inherits whether you know it or not.

If you do not know where the limitations are, you fight them by accident. Your code works against the system instead of with it. You optimize the wrong things. You reach for the wrong tools.

When you do understand how Node works internally, something shifts. You stop guessing. You write code that goes with how the runtime is designed, not against it.

The Two Pillars of Node.js

Node.js is built on two distinct libraries that do completely different jobs.

V8 is the JavaScript engine, the same one that runs in Chrome. It parses your JavaScript, compiles hot paths to native machine code, and manages memory. When you write a function and call it, V8 is what executes it.

libuv is the I/O library. It handles everything that touches the operating system: file system reads, DNS lookups, TCP connections, UDP sockets, timers. It is written in C and it is the reason Node can do asynchronous I/O without threads on a single JavaScript thread.

V8 handles JavaScript execution; libuv handles all I/O through the operating system ExpandV8 handles JavaScript execution; libuv handles all I/O through the operating system

These two libraries connect through the event loop -- the scheduling mechanism that decides what runs next on the single JavaScript thread. Understanding the event loop is understanding Node.

What This Series Covers

The series follows three stages.

Stage 1: Architecture. How V8 and libuv fit together. Every phase of the event loop in detail -- not just "it processes callbacks" but exactly what queue it reads from, when it yields, and what can block it.

Stage 2: Internals. What happens inside each protocol Node exposes. How does Node actually establish an HTTP connection? What does libuv do when you call fs.readFile? How does HTTPS work at the certificate level, not the options.rejectUnauthorized level? This covers HTTP, HTTPS, DNS, TCP, UDP, streams, worker threads, and child processes.

Stage 3: Optimization. What to do with the knowledge. How to debug Node at the process level. How to capture and inspect the actual network traffic your Node app produces (using Wireshark and a man-in-the-middle proxy). How to write C++ add-ons for the cases where JavaScript cannot deliver the performance you need.

Who This Is For

This is an intermediate series. It is not about learning how to use Node -- there are better starting points for that. This is for engineers who have already built things with Node and want to understand what is actually running underneath their application.

A working knowledge of operating system fundamentals helps significantly. Concepts like system calls, file descriptors, sockets, and process scheduling will come up constantly. They are not prerequisites in a strict sense, but familiarity with them makes the internals much easier to follow.

Prerequisites

  • Node.js v20 or later
  • Mac or Linux (Ubuntu recommended). Windows will work for most of the series, but the network-level debugging sections use tools like ss, tcpdump, and Wireshark that are native to Unix environments.

The Essentials

  1. Using Node and understanding Node are different skills. This series is about the second one -- the internals that explain why Node behaves the way it does.
  2. Node.js is two libraries: V8 and libuv. V8 runs your JavaScript. libuv handles all I/O through the operating system. The event loop connects them.
  3. Understanding limitations lets you write with the system, not against it. Every production performance problem is easier to diagnose when you know where the bottlenecks structurally live.
  4. Three stages: architecture, internals, optimization. Event loop phases first. Then each protocol (HTTP, TCP, streams, threads) in depth. Then performance tools (Wireshark, C++ add-ons, traffic capture).
  5. Node v20+, Ubuntu or Mac. The network debugging sections need Unix tooling.

Further Reading and Watching