Bytecode caching startup improvements by application size
Small CLI applications (< 100 KB) achieve typical startup improvements of 1.5-2x faster. Medium-large applications (> 5 MB) achieve 2.5x-4x faster startup. Larger applications benefit more because they have more code to parse.
--bytecode flag enables bytecode caching
Enable bytecode caching with the `--bytecode` flag when running `bun build`. Without `--format`, the output format defaults to CommonJS.
Bytecode build output files
When using `--bytecode`, Bun writes two files: the bundled JavaScript file (e.g., `dist/index.js`) and the bytecode cache file with `.jsc` extension (e.g., `dist/index.js.jsc`). At runtime, Bun automatically detects and uses the `.jsc` file.
ESM bytecode requires --compile flag
ESM bytecode requires `--compile` because Bun embeds module metadata (import/export information) in the compiled binary. Without `--compile`, ESM bytecode would still require parsing the source to analyze module dependencies, which defeats the purpose of bytecode caching. CommonJS bytecode works with or without `--compile`.
Bytecode with minification and source maps example
```bash
bun build --compile --bytecode --minify --sourcemap ./cli.ts --outfile=mycli
```
This combines bytecode caching with minification to reduce code size before generating bytecode, and source maps to preserve error reporting mapping to original source.
Bytecode not portable across Bun versions
Bytecode is not portable across Bun versions. The bytecode format is tied to JavaScriptCore's internal representation, which changes between versions. When you update Bun, you must regenerate bytecode. If bytecode doesn't match the current Bun version, Bun ignores it and falls back to parsing the JavaScript source.
Source code still required with bytecode
Bytecode doesn't replace your JavaScript. You must deploy both files: the `.js` file (your bundled source code) and the `.jsc` file (the bytecode cache). At runtime, Bun loads the `.js` file, sees a `@bytecode` pragma, and checks the `.jsc` file. Bun validates the bytecode hash matches the source, and if valid uses the bytecode, otherwise falls back to parsing the source.
Bytecode does not obscure source code
Bytecode does not obscure your source code. It is an optimization, not a security measure.
Bytecode file size ratio
Bytecode files are typically 2-8x larger than the source code. The `.jsc` file should be 2-8x larger than the `.js` file.
Bytecode cross-architecture portability
Bytecode is architecture-independent. You can build on macOS ARM64 and deploy to Linux x64, build on Linux x64 and deploy to AWS Lambda ARM64, or build on Windows x64 and deploy to macOS ARM64. The bytecode contains abstract instructions that work on any architecture. Architecture-specific optimizations happen during JIT compilation at runtime, not in the cached bytecode.
Verify bytecode is being used with environment variable
To log whether the bytecode is used, set `BUN_JSC_verboseDiskCache=1` in your environment. On a cache hit, Bun logs: `[Disk Cache] Cache hit for sourceCode`. On a cache miss, Bun logs: `[Disk Cache] Cache miss for sourceCode`. Several cache-miss lines are normal because Bun doesn't bytecode-cache the JavaScript in its builtin modules.
CommonJS bytecode basic usage command
```bash
bun build ./index.ts --target=bun --bytecode --outdir=./dist
```
This enables bytecode caching for CommonJS output. Bun writes `dist/index.js` and `dist/index.js.jsc`. At runtime, `bun ./dist/index.js` automatically uses index.js.jsc.
Bytecode with standalone executables example
```bash
# ESM (requires --compile)
bun build ./cli.ts --compile --bytecode --format=esm --outfile=mycli
# CommonJS (works with or without --compile)
bun build ./cli.ts --compile --bytecode --outfile=mycli
```
When you create an executable with `--compile`, Bun embeds the bytecode in the binary. The resulting executable contains both the code and the bytecode.
Docker bytecode generation example
```dockerfile
FROM oven/bun:1 AS builder
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun build --bytecode --minify --sourcemap \
--target=bun \
--compile \
./src/server.ts --outfile=./dist/server
FROM oven/bun:1 AS runner
WORKDIR /app
COPY --from=builder /app/dist/server /app/server
CMD ["./server"]
```
This Dockerfile example includes bytecode generation in the build stage. The bytecode is architecture-independent.
GitHub Actions bytecode generation example
```yaml
- name: Build with bytecode
run: |
bun install
bun build --bytecode --minify \
--outdir=./dist \
--target=bun \
./src/index.ts
```
This GitHub Actions workflow example generates bytecode during the build pipeline.
When to use bytecode caching
Bytecode is great for CLI tools invoked frequently (linters, formatters, git hooks) where startup time is the entire user experience and users notice differences like between 90ms and 45ms. It is also good for build tools and task runners run hundreds or thousands of times during development where milliseconds saved per run compound quickly. Bytecode is useful for standalone executables distributed to users who care about snappy performance and single-file distribution is convenient. Skip bytecode for small scripts, code that runs once, development builds, and size-constrained environments.
.jsc file structure and contents
A `.jsc` file contains a serialized bytecode structure with: header section with cache version hash and code block type tag; SourceCodeKey with source code hash, source code length, and compilation flags; bytecode instructions with instruction stream, metadata table, jump targets, and switch tables; constants and identifiers with constant pool, identifier table, and source code representation markers; function metadata for each function with register allocation, code features bitmask, lexically scoped features, and parse mode; nested structures with function declarations, expressions, and exception handlers.
Bytecode does not embed source code
Bytecode does not embed your source code. The JavaScript source is stored separately in the `.js` file. The bytecode only stores a hash and length of the source. At load time, Bun validates the bytecode matches the current source code. This is why you need to deploy both the `.js` and `.jsc` files: the `.jsc` file is useless without its corresponding `.js` file.
Lazy parsing optimization with bytecode
Modern JavaScript engines use lazy parsing - they don't parse all code upfront but only parse functions when they're first called. With bytecode caching, all functions are pre-compiled, even ones the engine would otherwise parse lazily. This means parsing overhead isn't just a startup cost but happens throughout an application's lifetime as different code paths execute.
Unlinked bytecode in .jsc files
The bytecode saved in `.jsc` files is unlinked bytecode. It contains the compiled bytecode instructions, structural information about the code, constants and identifiers, and control flow information. However, it does not contain pointers to actual runtime objects, JIT-compiled machine code, profiling data from previous runs, or call link information. Unlinked bytecode is immutable and shareable - multiple executions of the same code can all reference the same unlinked bytecode.
Linked bytecode created at runtime
When Bun runs bytecode, it links it by creating a runtime wrapper that adds: call link information as the engine learns which functions call which; profiling data tracking execution counts and value types; JIT compilation state with references to baseline JIT or optimizing JIT compiled versions of hot code; and runtime objects including pointers to actual JavaScript objects, prototypes, and scopes. The linked representation is created fresh every time the code runs, allowing caching of expensive parsing and compilation work while still collecting runtime profiling data and applying JIT optimizations.
Bytecode compression efficiency
Bytecode compresses well with gzip or brotli, achieving 60-70% compression. The repetitive structure and metadata compress efficiently.
Bytecode generation as part of CI/CD best practice
Best practice is to generate bytecode as part of your CI/CD build process. Do not commit `.jsc` files to git. Regenerate them whenever you update Bun.
Bytecode compilation for faster startup
Enable bytecode compilation with `--bytecode` flag: `bun build --compile --minify --sourcemap --bytecode ./path/to/app.ts --outfile myapp`. In JavaScript API: `bytecode: true`. Moves parsing overhead from runtime to bundle time, making `tsc` start 2x faster. Supports both `cjs` and `esm` formats with `--compile`. Does not obscure source code.
bytecode option generates executable bytecode
The `bytecode` option generates bytecode for JavaScript/TypeScript entrypoints to improve startup times. Requires target: 'bun'. CommonJS works with or without compile: true (generates .jsc files). ESM requires compile: true (embeds bytecode in executable). Default is false.