All posts
6 min readDavid Nussio

From 417 to 32 milliseconds: envsec on Effect 4 and Bun

envsec is a CLI: you run it, it does one thing, and it exits. You run it dozens of times a day, sometimes inside a script or a shell prompt. For a tool like that, startup time is the performance that matters, because it is the only one you actually feel.

The new beta (1.1.0-beta.1) wraps up a three-step journey: migrating from Effect 3 to Effect 4, trimming dependencies, and shipping a standalone binary built with bun build --compile. I measured every step on the same machine. The result: startup went from 417 ms to 32 ms (12.9× faster), and memory from 207 MB to 36 MB (5.8× less).

At a glance

Effect 3 · Node (1.0.0-beta.18)Effect 4 · Node (1.0.2)Effect 4 · Node (1.1.0-beta.1)Effect 4 · Bun binary (1.1.0-beta.1)
envsec --version416.7 ms318.2 ms192.3 ms32.4 ms
envsec --help430.1 ms324.2 ms196.8 ms34.0 ms
envsec list443.4 ms319.2 ms199.4 ms33.7 ms
Memory (max RSS, list)207 MB154 MB97 MB36 MB
Installed packages4219100 (single file)
Disk footprint106 MB100 MB58 MB61 MB (runtime incl.)
Minified JS bundle516 KB371 KB371 KBembedded
Times: median of 40 runs after 5 warm-up runs.

The three steps

1. Effect 3 → Effect 4 (1.0.0-beta.18 → 1.0.2)

On Effect 3, a CLI like envsec pulled in a constellation of packages: effect, @effect/cli, @effect/platform, @effect/platform-node and, transitively, @effect/cluster, @effect/rpc, @effect/sql, @effect/workflow, @effect/printer, @effect/typeclass and more. 42 packages in total.

Effect 4 folds almost all of that into the effect package: the CLI module is now imported from effect/cli. The migration notes mention a fiber runtime rewritten for lower memory overhead and faster execution, and a core built for aggressive tree-shaking. For envsec, that meant:

None of the application logic changed. That gain comes from the library upgrade alone.

2. Leaner dependencies (1.0.2 → 1.1.0-beta.1 on Node)

Two targeted changes:

Result: startup 318 → 192 ms (−40%), memory 154 → 97 MB, packages 19 → 10, and the disk footprint nearly halved.

3. A standalone Bun binary

bash
bun build src/main.ts --compile --minify --sourcemap --outfile envsec

--compile produces a single executable with the Bun runtime embedded: no Node.js, no node_modules, no module resolution at startup. The CLI code ships pre-bundled and minified in one file.

Result: startup 192 → 32 ms (5.9× faster), memory 97 → 36 MB.

The gain has two sources:

  1. The runtime. On this machine an empty process takes 76.5 ms with Node (node -e 0) and 4.5 ms with Bun (bun -e 0). That alone is worth about 72 ms.
  2. Loading the code. Subtracting the runtime cost, envsec on Node spends about 116 ms loading and initialising modules; the binary needs about 28. Reading one pre-bundled file is far cheaper than resolving and evaluating hundreds of files spread across 10 packages.

Where the 384 milliseconds went

Startup time
  • Effect 3 · Node
    417 ms
  • Effect 4 · Node
    318 ms−99 ms
  • Effect 4 · leaner deps
    192 ms−126 ms
  • Effect 4 · Bun binary
    32 ms−160 ms

Median of envsec --version, 40 runs. Each step builds on the previous one.

Peak memory (RSS)
  • Effect 3 · Node
    207 MB
  • Effect 4 · Node
    154 MB
  • Effect 4 · leaner deps
    97 MB
  • Effect 4 · Bun binary
    36 MB

Median of 15 runs of envsec list, measured with /usr/bin/time -l.

Every step delivered a real gain, and each one enabled the next: without the move to node:sqlite the binary could not even be compiled, because the sql.js WASM file could not be located at run time.

Installation and distribution

Effect 3 · npmEffect 4 · npm 1.0.2Effect 4 · npm 1.1.0-beta.1Bun binary
npm install, cold cache (median of 3)3.0 s2.0 s1.6 s—
Download (compressed)~23 MB~21 MB~10 MB25 MB (tar.gz)
Needs Node.jsyesyesyes (≥ 22.13)no

The binary is not the lightest download: of its 61 MB, about 59 are the Bun runtime, so envsec and its dependencies weigh just over 1.5 MB. In exchange you don't need Node installed, there is no dependency tree to resolve, and startup is an order of magnitude faster. The npm package is still there for anyone who prefers Node, and it is 54% faster than the Effect 3 version too.

Methodology

Caveats

Reproduce it

Terminal
$export ENVSEC_DB=$PWD/db/store.sqlite && mkdir -p db
$hyperfine -N --warmup 5 --runs 40 \
$ -n "Effect 3" "effect3/node_modules/.bin/envsec list" \
$ -n "Effect 4" "effect4/node_modules/.bin/envsec list" \
$ -n "Bun" "./envsec list"

Grab the binary from the v1.1.0-beta.1 release, or install it from the npm and Homebrew beta channels.

Conclusion

Effect 4 pulled its weight on its own: a quarter less startup time and memory just by upgrading the library. The real leap, though, came from three things together: a more compact Effect core, dependencies cut to the bone, and a runtime that starts in 4 ms. For a CLI you run all day, going from nearly half a second to 32 ms is the difference between a delay you notice and one you don't.