There is a figure in Hindu cosmology who doesn’t get nearly enough credit.
Visvakarma (विश्वकर्मा) — the divine architect and craftsman of the gods. He built Lanka, the golden city that Ravana ruled. He forged the Sudarshana Chakra, Vishnu’s spinning discus of light. He crafted the vimanas, the flying vehicles of the devas. He shaped divine weapons that couldn’t be made by ordinary hands. He was not described as ten specialists handing work off to each other. He was one craftsman, complete in himself, who could build anything, forge anything, imagine anything — and deliver it faster than you could ask.
When the gods needed something built, they went to Visvakarma.
I think about Visvakarma a lot when I think about Bun.
The Maya of the JavaScript Toolchain — माया
In Advaita Vedanta, Maya is the cosmic illusion — the veil that makes multiplicity appear where there is really only unity. We see many things; the sage sees one.
For years, the JavaScript toolchain has been deep in Maya.
You needed Node.js to run your code. You needed npm (or yarn, or pnpm) to manage packages. You needed Jest (or Vitest, or Mocha) to test it. You needed Webpack (or Rollup, or Vite, or esbuild) to bundle it. You needed Babel or ts-node just to write TypeScript without a ceremony. You needed ts-node, or tsx, or a tsconfig with fifteen uncommented options, just to start.
Each of these tools is individually reasonable. Collectively, they form a fog of configuration. The average JavaScript project’s package.json devDependencies section has become longer than the actual features list. We called this “the ecosystem.” What we were really experiencing was Maya — complexity masquerading as necessary structure.
Bun is the stripping away of that veil.
What Bun Actually Is
Bun is a JavaScript runtime, package manager, bundler, and test runner — all in one binary. It is written in Zig, a systems programming language that gives you C-level control without C’s footguns, and it runs JavaScript on Apple’s JavaScriptCore engine (the same one that powers Safari) rather than V8.
That engine choice matters. JavaScriptCore starts faster. It uses less memory. For startup-heavy workloads — scripts, CLIs, test runners — the difference is immediately felt.
The result is a tool that, when you first use it, makes you feel like you’ve been paying an unfair tax for years and didn’t know it.
1. The Package Manager — इंस्टॉल करो, क्षण में
bun install is at least 10x faster than npm. On a clean install of a moderately sized project, npm might take 25–40 seconds. Bun does it in 1–3 seconds.
This isn’t a benchmark trick. It’s the result of Bun parallelising network requests aggressively, using a binary lockfile format (bun.lockb) instead of the verbose JSON that npm and yarn write, and caching packages globally in a content-addressed store.
# First install — still faster than npm's cached install
bun install
# Add a package
bun add hono
# Remove a package
bun remove lodash
# Run a script
bun run dev
Full compatibility with the npm registry, package.json, .npmrc, workspaces, and peer dependencies. It’s a drop-in replacement. The only cost is the three seconds you gain back every time.
2. The Web Server — बिना बोझ के
Bun.serve({
port: 3000,
fetch(req) {
const url = new URL(req.url);
if (url.pathname === '/') {
return new Response('Hello from Bun', {
headers: { 'Content-Type': 'text/plain' },
});
}
return new Response('Not Found', { status: 404 });
},
});
That’s a web server. No express import. No middleware chain. No app.listen. The fetch handler receives a standard Request and returns a standard Response — the same Web API your browser uses. This isn’t a framework; it’s the platform.
bun.serve supports:
- Native TypeScript (no transpilation step, no ts-node)
- Hot module reloading with
bun --hot - TLS/HTTPS out of the box
- Static file serving
- UNIX domain sockets
The web server benchmark numbers are striking — Bun consistently outperforms Node.js + Express by a significant margin on raw throughput. More importantly: you can go from zero to a running TypeScript HTTP server in about fifteen lines of code, and those fifteen lines are readable to anyone who’s used the Fetch API.
3. TypeScript — नेटिव, बिना बाबेल के
Bun runs TypeScript natively. Not via ts-node. Not via a pre-compilation step. Not via Babel stripping types. Bun reads your .ts files directly, transpiles them internally at the speed of a JIT, and runs them.
# This just works — no tsconfig required to get started
bun run server.ts
You get:
- Full TypeScript support in scripts, tests, and servers
bun typesto install@types/*packages- JSX and TSX natively (configure the runtime once in
tsconfig.json, not in every tool)
For the first time in my career, “add TypeScript to this project” is a five-second operation.
4. Content & HTML Rewriting
Bun ships a built-in Markdown parser and an HTML rewriter. These aren’t third-party packages — they’re part of the runtime.
import { Bun } from 'bun';
// Convert Markdown to HTML
const html = Bun.Transpiler.transformMarkdown(markdownContent);
// Rewrite HTML on the fly (Cloudflare Workers HTMLRewriter API)
const rewriter = new HTMLRewriter()
.on('title', {
text(element) {
element.replace(`${element.text} — My Site`);
},
})
.transform(response);
The HTMLRewriter API (borrowed from Cloudflare Workers) is particularly powerful for server-side templating without a full framework. Parse a response, modify nodes, stream it back — no DOM instantiation, no cheerio, no heavy HTML parsing library.
5. Built-In Database Bindings — डेटा, सीधे
import { Database } from 'bun:sqlite';
const db = new Database('app.db');
const query = db.query('SELECT * FROM users WHERE active = ?');
const users = query.all(1);
bun:sqlite is a native SQLite driver. No better-sqlite3. No build step for native modules. It’s synchronous by design (because SQLite is, and pretending otherwise adds overhead), and it’s fast — benchmarks show Bun’s SQLite driver outperforming better-sqlite3 by a significant margin on common operations.
Beyond SQLite, Bun ships with first-class APIs for:
- Redis —
import { createClient } from 'bun:redis' - S3 —
import { S3Client } from 'bun:s3' - PostgreSQL — via
bun:postgres
No more npm install pg redis @aws-sdk/client-s3. The data layer is part of the runtime.
6. Authentication Primitives — Built In
Bun ships the pieces you need for auth without reaching for a library:
// UUID generation
const id = crypto.randomUUID(); // Web Crypto API, native
// Password hashing (Argon2id by default — the right choice)
const hash = await Bun.password.hash('my-secret-password');
const valid = await Bun.password.verify('my-secret-password', hash);
// Cookies (via Response/Request headers — Web standard)
const response = new Response('Logged in', {
headers: {
'Set-Cookie': 'session=abc123; HttpOnly; Secure; SameSite=Strict',
},
});
Bun.password.hash uses Argon2id by default — the winner of the Password Hashing Competition, the current recommendation for new projects. Switching to bcrypt is a single parameter. No bcryptjs, no argon2 npm package, no native build step.
7. WebSockets — 7x Faster Than Node
Bun.serve({
fetch(req, server) {
if (server.upgrade(req)) return; // upgrade to WebSocket
return new Response('Expected a WebSocket', { status: 400 });
},
websocket: {
message(ws, message) {
ws.send(`Echo: ${message}`);
},
open(ws) {
ws.subscribe('chat-room');
},
},
});
Bun’s WebSocket server is built on uWebSockets.js under the hood — the same library that powers some of the highest-throughput real-time servers in production. Benchmarks show up to 7x higher message throughput compared to Node.js + ws.
The API is clean. Pub/sub is built in (ws.subscribe, ws.publish). No separate socket.io layer for rooms. No manual client tracking arrays.
8. FFI — The Forge
This is where Bun starts to feel genuinely unusual.
import { dlopen, FFIType, suffix } from 'bun:ffi';
// Load a shared library (C, Rust, Zig — anything that exports C ABI)
const { symbols } = dlopen(`libmath.${suffix}`, {
fast_sqrt: {
args: [FFIType.f64],
returns: FFIType.f64,
},
});
const result = symbols.fast_sqrt(144); // calls native code directly
Bun’s Foreign Function Interface lets you call native code from any language that compiles to a C-compatible ABI — C, Rust, Zig, Go. No N-API, no node-gyp, no node-addon-api boilerplate. Just a type declaration and a dlopen.
And for quick native experiments, Bun ships with TinyCC embedded:
import { cc } from 'bun:ffi';
const { symbols: { add } } = cc({
source: /* c */ `
int add(int a, int b) { return a + b; }
`,
symbols: {
add: { args: ['int', 'int'], returns: 'int' },
},
});
console.log(add(2, 3)); // 5 — compiled and called at runtime
You write a C string literal in TypeScript. Bun compiles it with TinyCC, links it, and returns callable functions. This shouldn’t work this cleanly, and yet it does.
9. The Bundler — 200x
bun build ./src/index.ts --outdir ./dist --target browser
That’s the entire command. No webpack.config.js. No Rollup plugin chain. No vite.config.ts with four resolve.alias entries that only one of your colleagues understands.
Bun’s bundler is reported to be ~200x faster than Webpack on equivalent workloads. It handles:
- Tree shaking
- Code splitting
- CSS bundling
- Asset hashing
- Source maps
- Multiple targets (
browser,node,bun)
For most projects that aren’t doing exotic things, bun build is sufficient and it finishes before you finish reading the command you typed.
10. The Test Runner — Jest, But Fast
import { test, expect, describe } from 'bun:test';
describe('auth utils', () => {
test('hashes and verifies password', async () => {
const hash = await Bun.password.hash('hunter2');
expect(await Bun.password.verify('hunter2', hash)).toBe(true);
expect(await Bun.password.verify('wrong', hash)).toBe(false);
});
});
Run it:
bun test
The API is Jest-compatible. describe, test, expect, beforeAll, afterEach, mock, spyOn — it’s all there. If you’re migrating from Jest, most test files will work unchanged.
The difference is speed. Tests run concurrently across files by default. There’s no --maxWorkers configuration to tune. Bun figures out the parallelism. On large test suites, the speedup over Jest is typically 5–15x.
The Dharma of the Tool — औजार का धर्म
Dharma has many meanings. One of them is simply: the essential nature of a thing. The dharma of fire is to burn. The dharma of water is to flow. The dharma of a good tool is to get out of your way.
Every second you spend waiting for npm install, every minute you spend configuring a Babel preset, every hour you spend debugging why your Jest and TypeScript configs are disagreeing — that is time the tool is taking from you. It is the tool failing at its dharma.
Bun understands its dharma.
This isn’t just about benchmark numbers. It’s about the texture of the development experience. When bun install finishes before you’ve looked away from the screen, something cognitive shifts. The tool stops being a thing you manage and becomes a thing you use. There’s a difference. Every craftsperson knows it.
Visvakarma didn’t make the gods wait. He built what was needed, with perfect craft, without fuss. The tool served the work. That’s the standard.
Is It Production Ready?
Bun reached 1.0 in September 2023. It’s been moving quickly since.
Where it works brilliantly right now:
- Scripts and automation (replace ts-node, tsx entirely)
- Package management (drop-in npm/yarn/pnpm replacement)
- Backend APIs and web servers (bun.serve is solid)
- Test suites (Jest migration is largely painless)
- CLI tools
- Monorepo tooling
Where to still be careful:
- Bun does not yet support all Node.js native modules. Compatibility has improved substantially but some packages that use native bindings (N-API modules) will require testing.
- The
bun buildbundler is fast but not yet at feature parity with Vite for complex frontend builds. For most backend or library bundling, it’s excellent. For complex React apps with advanced Vite plugins, test before committing.
The honest answer: if you’re starting a new backend project, a new CLI, or a new monorepo toolchain today, Bun is the correct choice and the compatibility concerns are unlikely to affect you. For migrating existing large projects, do a thorough dependency audit first.
The Offering
I’ve been using Bun as my primary runtime for new projects since 1.0. The experience is consistently the same: I start a project, and then a few days in I realise I haven’t thought about my toolchain once. There have been no configuration frustrations. No version conflicts. No “why is ts-node not picking up my tsconfig” moments.
That absence of friction is the feature.
The JavaScript ecosystem built extraordinary power over twenty years. Node.js changed server-side development. npm enabled an ecosystem of millions of packages. Jest made testing approachable. Webpack made complex frontend builds possible. None of this is nothing.
But they were built separately, by separate teams, in separate eras, with separate design philosophies. The integration layer — the glue, the config files, the compatibility shims — became a second job.
Bun is what happens when someone builds all of it together, from scratch, with modern tooling, and asks: what if it were one thing?
Visvakarma didn’t subcontract.
Using Bun? Hit me up at @deva.code.karma — I’m curious what edge cases you’ve run into and whether the reality matches the benchmarks.