~/sumit/portfolio — a-weekend-with-bun-deno-and-node.md
writing.md/A weekend with Bun, Deno, and Node
---
title: "A weekend with Bun, Deno, and Node"
date: 2024-10-04
tags: [engineering]
reading_time: 15 min
slug: a-weekend-with-bun-deno-and-node
---

A weekend with Bun, Deno, and Node

15 min read engineering by sumit

I took a small Saturday project — a Markdown-to-HTML renderer with a file watcher — and built it three times, once on each major JavaScript runtime. Same input, same output, just the runtime swapped.

The results are less "which one wins" and more "which one loses the least."

The project

~350 lines. Reads a directory of .md files, renders them to HTML with a naive pipeline, writes them to dist/. Watch mode on, incremental by mtime. Nothing fancy.

Dependencies: just a Markdown parser (the one with the cleanest install for each runtime — marked for Node, native imports for Deno, Bun's built-in parser).

Startup

node:  168ms
deno:   94ms
bun:    38ms

Bun wins cold-start by a wide margin, as everyone says. What surprised me was how much of Node's startup is module resolution and not V8 warmup — running the same script under node --experimental-wasm-modules shaved ~40ms.

For a daemon that runs once, startup doesn't matter. For a CLI I invoke 50 times a day in a file watcher, it matters a lot.

Watch mode

Node's fs.watch is famously unreliable on macOS — debouncing events is on you. I ended up using chokidar.

Deno's Deno.watchFs is cleaner API, but it still emits 4 events for one save on my Mac.

Bun's Bun.file + watch is the nicest API of the three, and also the most opaque when it misbehaves. I lost 40 minutes to a watcher that silently stopped firing after a directory rename.

Winner on ergonomics: Bun. Winner on "I can actually debug when it breaks": Node.

Ecosystem

This is the thing nobody writing about alternatives wants to say out loud: Node's ecosystem is a moat the others won't close for years.

Every runtime had to install at least one npm package. Bun handled that transparently. Deno 2's Node compat layer mostly worked. But the moment I hit a native module (sharp), the gradient was steep.

What I'd actually pick

  • One-off CLI tool: Bun. The install-to-first-run loop is half the time of Node.
  • Long-lived service: Node. Boring, stable, every library works.
  • Edge/serverless: Deno Deploy or Cloudflare Workers. Neither is on my list above, but that's the real niche both are winning.

Three runtimes; use cases don't overlap as much as the marketing suggests.