17 views
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 viaNODE_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.
Node Module Compile Cache: production guidance
bash
# ✅ 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
typescript
// 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");
}
bash
# ❌ 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 justrequire count.
typescript
// 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
yaml
# .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.
typescript
// ✅ 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
Watch: 100 Days of AI on YouTube
Short videos from CheatCoders, one AI engineering topic per day.
Most viewed
- Python Decorators Explained: From Simple Wrappers to Production Patterns
- Claude Code vs Cursor vs GitHub Copilot: Choose by Workflow, and Set Up One Repo That Works With All Three
- AI Agent Frameworks in 2025: LangGraph vs CrewAI vs AutoGen vs Raw API
- Python String Methods: Every str Method With Real Production Examples
- Css Animation Performance: Performance-Optimized Motion for Modern UIs
Newly added
- AWS Systems Manager Parameter Store: Hierarchical Runtime Config for Coding Agents
- Amazon Cognito Identity Pools: Federate Coding-Agent Tool Callers Without Long-Lived Keys
- Amazon Athena for Coding Agents: Query Tool Audit Logs and Eval Traces on S3 Without a Warehouse
- AWS Batch for Coding Agents: Parallel Overnight PR Reviews and Long-Running Jobs Without Lambda Timeouts
- Amazon Bedrock Prompt Management: Version and A/B Test Coding-Agent System Prompts
Deep-dive PDF
Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.
Already subscribed? or open the subscribe page.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
