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.