Large TypeScript-authored CLIs spend a humiliating amount of time parsing and compiling JS on every cold invoke. Node’s module compile cache (and related bytecode caching) stores the results on disk so the second — and every teammate’s first after a shared cache — starts closer to “already warmed.” Used poorly, you cache the wrong tree and ship stale bytecode; used well, you shave hundreds of milliseconds off DX and Lambda-style warmers.
⚡ TL;DR: Enable compile cache via
NODE_COMPILE_CACHE(or version-appropriate module compile cache flags) pointing at a writable, per-version directory; key the cache on Node version + lockfile hash; never share caches across major Node upgrades; measureprocess.uptime()to first productive output. Related: AWS Lambda Cold Start Fix and Lambda Warm Pools.
Turn it on with a deterministic cache root
# ✅ Per-Node, per-project cache path
export NODE_COMPILE_CACHE="$HOME/.cache/node-compile/$(node -v)/acme-cli-$(sha256sum pnpm-lock.yaml | cut -c1-12)"
mkdir -p "$NODE_COMPILE_CACHE"
node ./dist/cli.js --help
// src/cli-entry.ts
if (!process.env.NODE_COMPILE_CACHE) {
// Soft warn in dev — hard fail in perf CI
console.warn("NODE_COMPILE_CACHE unset; cold starts will parse everything");
}
# ❌ Single global cache across Node 20 and 22 and every repo
export NODE_COMPILE_CACHE=/tmp/node-cache
Measure the only metric that matters
Time from process start to first actionable output (help text, resolved config, or first network call), not just require count.
// src/boot-metrics.ts
const t0 = performance.now();
export function markReady(label: string) {
const ms = performance.now() - t0;
if (process.env.CLI_BOOT_METRICS) {
console.error(JSON.stringify({ event: "cli_ready", label, ms }));
}
}
// after config load
markReady("config_loaded");
CI: warm the cache once, then assert
# .github/workflows/cli-boot.yml
- name: Warm compile cache
run: |
export NODE_COMPILE_CACHE=$RUNNER_TEMP/cc
mkdir -p "$NODE_COMPILE_CACHE"
node dist/cli.js --help >/dev/null
CLI_BOOT_METRICS=1 node dist/cli.js --help
- name: Cold-ish second process should be faster
run: |
export NODE_COMPILE_CACHE=$RUNNER_TEMP/cc
CLI_BOOT_METRICS=1 node dist/cli.js --help | tee boot.json
# assert ms under budget with jq in a real job
Interaction with bundlers and ESM
If you already bundle the CLI into a single file with esbuild/ncj, compile cache helps less — the bottleneck moved to your bundle strategy. Prefer: tree-shake heavy deps, lazy-import() subcommands, then compile cache for whatever remains as plain node_modules during local ts-node/tsx paths.
// ✅ Lazy subcommands — biggest win before caching
export async function run(argv: string[]) {
if (argv[0] === "deploy") {
const { deploy } = await import("./cmds/deploy.js");
return deploy(argv.slice(1));
}
if (argv[0] === "sync") {
const { sync } = await import("./cmds/sync.js");
return sync(argv.slice(1));
}
}
Lambda and container notes
Ephemeral filesystems make caches less sticky unless you attach a volume or rely on provisioned concurrency warmers. For Lambda, prefer smaller bundles + provisioned concurrency over compile cache on /tmp unless you control reuse patterns carefully.
Closing checklist
✅ Dos
– ✅ Set NODE_COMPILE_CACHE to a version+lockfile keyed directory
– ✅ Lazy-load CLI subcommands before tuning caches
– ✅ Record time-to-ready in CI budgets
– ✅ Wipe caches on Node major upgrades
– ✅ Combine with bundling for distributed binaries
❌ Don’ts
– ❌ Don’t share one cache across Node majors
– ❌ Don’t commit cache directories to git
– ❌ Don’t expect compile cache to fix multi-hundred-MB dependency graphs alone
– ❌ Don’t enable experimental flags in prod without a rollback env knob
– ❌ Don’t confuse compile cache with V8 code cache inside bundled singles
Related reading
- AWS Lambda Cold Start Fix at Zero Cost
- Lambda Warm Pools: Low-Latency Backends for Coding Agent Tools
- Node.js Event Loop Lag: Catch P99 Stalls
- Node Flamegraphs on ECS: Continuous Profiling That Survives Scale
Last updated on September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
