Docs Build cost

DocsReference

#What a build actually costs

Measured for issue #58, 2026-09-06. The question behind it: does a managed control plane have to avoid compiling per customer? The answer is no.

Host: Mac16,5, 14 cores, rustc 1.98.1, examples/venture (core, Cloudflare runtime, Resend adapter, email-signup, waitlist, one sample module).

CaseWall clockwasm
Cold, no cache at all26.7 s2.66 MB
Warm, nothing changed3.2 s2.66 MB
Warm, composition file touched (a module toggled)4.2 s2.66 MB
Warm, one new module crate added5.3 s2.69 MB
Warm, codegen-units = 16 + opt-level = "s"17.3 s †1.86 MB

† Confounded, and not comparable to the rows above. Changing a profile invalidates every artifact, so this number contains a full recompile caused by the measurement itself. The useful figure in that row is the size: opt-level = "s" takes 2.66 MB down to 1.86 MB, a 30% cut. Re-measure the time on a second run before drawing any conclusion about it.

#The finding

Adding one module to a warm build costs about five seconds. A cold build, with nothing cached at all, is under thirty. Compilation is not a reason to avoid anything.

Post-processing dominates, not cargo. With a warm cache, cargo build --release --target wasm32-unknown-unknown is 1.0 s unchanged and 1.3 s with a new module crate. The rest of the 4.2 s and 5.3 s is wasm-bindgen plus wasm-opt inside worker-build, and neither benefits from cargo caching. Any future effort to make builds faster belongs there, not in the compiler.

#What this means for the epic (#56)

  • #59, caching the artifact on the module set, is an optimisation and not a prerequisite. It saves seconds, not minutes. Build it when there is a measured reason, not on principle. The reason to key artifacts on the module set instead of the customer remains good design; it is no longer urgent.
  • Sidecar modules (#60 and after) are justified by confidentiality alone. "It avoids a rebuild" is not an argument worth making at five seconds, and no documentation or site copy should make it. The argument is that a customer can run a module we never see.
  • The 1 s startup budget is not at risk: 2.66 MB parses well inside it, and opt-level = "s" is in reserve if a large module set ever pushes on it.

#Recipe

Reproduce with a throwaway module crate, so the measurement is "one more module" and not "one more line":

  1. Create a crate named module-spike-probe under crates/ implementing Module with requires() empty, Migrations::EMPTY, and a router with one GET /ping. Nothing else; the point is to measure linking a crate, not compiling a feature. It is deliberately not checked in — a crate that exists is a crate the build pays for.
  2. cargo clean, then worker-build --release in examples/venture for the cold row.
  3. Re-run untouched for the warm row; touch src/lib.rs for the toggle row.
  4. Add the probe crate as a path dependency and .module(SpikeProbe::new()) to the composition for the new-module row.
  5. Time cargo build --release --target wasm32-unknown-unknown -p venture separately to split compilation from wasm-bindgen and wasm-opt.

Use the rustup toolchain explicitly (~/.cargo/bin/cargo). A Homebrew rustc on PATH shadows it and is too old for this workspace's edition.

#Re-measured 2026-09-07

Same host and method, examples/venture as it stands after the UI epic (#69). The binary has grown; the build has not become a problem.

Casewasm
2026-09-06, as measured above2.66 MB
Today, without the UI renderer mounted2.98 MB
Today, as shipped3.50 MB

cratefield-ui and its ui.json account for 0.52 MB of that, measured by dropping the .ui(..) call and the dependency and rebuilding. The 0.32 MB between the two older figures is everything else landed since: schemars for the module surface, the sidecar dispatcher and mount, the admin pages.

Nothing here changes the finding above: a warm build with a new module crate is still seconds. If the size ever matters — a cold-start budget, not a build budget — opt-level = "s" is the first lever, and mounting the UI is optional per venture.

Mirrored from docs/BUILD-COST.md at b6f50cd. The harness is the source of truth: a correction goes there first.