le Cmd) | 4 (LLIntβFTL) | Low |
| Node/npm | ~50ms | Shared Runtime | Multi-step (Node+npm+PATH) | 2 (IgnitionβTurboFan) | High |
| Rust Binary | ~10ms | ~5-15MB | Single Cmd | N/A (AOT) | Very Low |
| Go Binary | ~20ms | ~15-20MB | Single Cmd | N/A (AOT) | Moderate |
Key Findings:
- Startup Sweet Spot: JavaScriptCore (JSC) achieves ~5ms startup vs V8's ~50ms due to 4 JIT tiers (LLInt, Baseline, DFG, FTL), allowing near-instant execution for CLI workloads.
- Size vs. UX Trade-off: The 100MB binary is acceptable in modern contexts (VS Code ~300MB, Slack ~500MB) when weighed against zero-dependency installation and auto-updates.
- Controlled Environment: Eliminates environment variance, ensuring reproducible bugs and streamlined support.
Core Solution
The native build leverages bun build --compile to package TypeScript source code with the complete Bun runtime into a single executable. This is not AOT compilation; it is a bundled runtime architecture.
Technical Implementation
1. Bundling
Bun acts as a bundler, resolving imports, tree-shaking dead code, and minifying the result into a single JavaScript file.
2. Embedding
The JavaScript bundle is embedded alongside the Bun runtime. The executable structure is:
βββββββββββββββββββββββββββββββββββ
β Bun Runtime (~95MB) β
β βββ JavaScriptCore engine β
β βββ Native APIs (fs, http) β
β βββ Zig runtime β
β βββ Static libc β
βββββββββββββββββββββββββββββββββββ€
β Your bundled code (~5MB) β
β βββ Minified JavaScript β
βββββββββββββββββββββββββββββββββββ
At runtime, the executable extracts the JavaScript code and executes it via the embedded JSC engine.
3. Cross-Compilation
Bun includes precompiled runtimes for multiple platforms, enabling cross-compilation without target-specific machines.
Code Examples
Basic Compilation:
bun build ./src/index.ts --compile --outfile claude
Cross-Compilation Targets:
bun build ./src/index.ts --compile --target=bun-linux-x64 --outfile claude-linux
bun build ./src/index.ts --compile --target=bun-darwin-arm64 --outfile claude-mac
bun build ./src/index.ts --compile --target=bun-windows-x64 --outfile claude.exe
Architecture Decisions
JavaScriptCore vs. V8:
- JSC (Bun): 4 JIT tiers enable faster startup. Lower memory footprint. Optimized for short-lived processes.
- V8 (Node): 2 JIT tiers. Higher startup latency. Optimized for long-running server workloads.
Why Zig?
Bun is written in Zig (with C++ for JSC interaction) to achieve:
- C Interoperability: Direct linking with C++ (JavaScriptCore) without FFI overhead.
- Memory Control: Manual memory management without Rust's borrow checker complexity.
- Cross-Compilation: Trivial compilation across platforms.
- Binary Size: Zig produces smaller binaries compared to Go or Rust for equivalent functionality.
Pitfall Guide
-
Misinterpreting AOT Compilation:
The binary does not convert TypeScript to CPU instructions. The code remains JavaScript and is interpreted by JSC at runtime. Expectations of Rust/Go performance characteristics for compute-heavy tasks are misplaced; the benefit is startup speed and distribution, not raw execution speed.
-
Ignoring Binary Bloat Constraints:
The ~95MB runtime is fixed. Attempts to reduce binary size by optimizing user code only affect the ~5MB payload. Tree-shaking is critical, but the runtime floor cannot be eliminated. Ensure the value proposition justifies the download size.
-
Node.js C++ Addon Incompatibility:
JSC and V8 have different internal APIs. Native addons compiled for Node.js (V8) will not work in the Bun runtime. Any dependency relying on node-gyp or V8-specific bindings must be rewritten or replaced with Bun-compatible alternatives.
-
Cross-Compilation Target Drift:
When using --target, ensure the output filename and architecture match the deployment environment. Distributing a bun-darwin-arm64 binary to Linux users will result in immediate failure. Validate targets in CI pipelines.
-
Dynamic Import Tree-Shaking Failures:
Dynamic import() statements or conditional require() calls can prevent effective tree-shaking, causing unused dependencies to bloat the embedded bundle. Audit code for dynamic module resolution to minimize payload size.
-
Auto-Update Risks in CI/CD:
Background auto-updates ensure users have the latest version but can break pinned versions in automated pipelines. Implement version pinning mechanisms or disable auto-updates in headless/CI environments to maintain reproducibility.
-
Assuming Universal User Gain:
Users with existing Node.js installations may see minimal benefit beyond auto-updates. Forcing migration in environments where Node.js is already stable can introduce unnecessary disruption. Evaluate user segments before mandating the native build.
Deliverables
π Blueprint: Bun Native Build Architecture
- Detailed diagram of the embedded runtime structure.
- Data flow from
bun build to executable extraction.
- Comparison of JIT tier behaviors in CLI vs. Server contexts.
β
Checklist: Pre-Flight for Native Compilation
βοΈ Configuration Templates
build.sh: Script for multi-platform compilation.
ci.yml: GitHub Actions workflow for automated native build generation.
install.sh: Curl-based installation script for end-users.