Why Deno Over Bun: A Conservative Choice

I wanted to move away from Node.js and the practices that come with it. I wanted TypeScript to just work without configuration, JSX/TSX support out of the box, and better developer experience through unified tooling, no more juggling npx, yarn, tsc, tsx, and whatever else the ecosystem requires this year.

Both Deno and Bun solve these problems. Both run TypeScript natively. Both handle JSX/TSX without build steps. Both provide unified tooling.

I went with Deno.

(note: Node v22.18+ now supports TypeScript out-of-the-box without the need to transpile it, see: Running TypeScript Natively)

Conservative Technical Choices

V8 versus JavaScriptCore

Deno uses V8, the same high-performance engine that powers Node.js. Bun uses JavaScriptCore. Both are battle-tested and high-performance. JavaScriptCore powers Safari/WebKit, while V8 powers Chrome.
But I know V8 better from years of Node.js. Here, familiarity matters when debugging obscure runtime behavior.

While I don't use anything specific to V8, I wanted some guarantee old code would run without new low-level unexpected behavior.

Rust Tokio versus Zig shiny engine

Deno's async runtime is built on Rust's Tokio (epoll-based on Linux systems). Bun uses a io_uring-based engine written in Zig, which I believe to be derived from Tigerbeetle's high-performance IO engine. Bun's IO engine is more cutting-edge and seems very fast on some IO-heavy workloads (thanks to io_uring, I guess), much faster than Deno's Tokio-based engine, according to some benchmarks.

Sure, Bun is definitely funnier and cooler here, but I felt safer with the "boring" choice: good old Tokio-based IO engine (which is already faster than Node's IO engine anyway).

Ryan Dahl's Second Attempt

Ryan Dahl created Node.js, and Deno is like his second attempt to make it right.
In his 2018 JSConf talk, Dahl listed some regrets, like no security sandboxing (more on this below), the complexity around node_modules and package.json, and a needlessly complicated module loading system.
Deno addresses all of these directly.

He knows where Node.js went wrong and designed Deno to fix those mistakes with hindsight, so again, I felt more confident with Deno which looks like an evolution rather than a revolution.

Bonus Features

OpenTelemetry Native Integration

Deno has native OTEL support (including logs). With a free-tier Grafana account, I was able to set up OTEL in a few minutes and it just worked. When enabled, standard console.log (and friends) calls are hooked and attached to the same request trace ID. All that with zero config, without any package to install like @opentelemetry/something.

Permission System

Deno is sandboxed by default: no network calls, no filesystem operations, nothing. You need to explicitly set the permissions you need, or grant everything with --allow-all/-A.

To be honest, I think most people grant all permissions, even in production, myself included. But during development, the permission system is still useful, e.g., when evaluating a new dependency, I can run it with restricted permissions to see what it tries to access: network, file system, environment variables, etc. It's like a sandbox to audit third-party code behavior before using it.

Conclusion

I still follow Bun's development. It's an exciting project, and I guess competition is a good thing in this space.