← Blog

Zero Percent, Exactly

Every Node app I’ve ever deployed ships more code than it runs. You install one package, npm installs forty, and somewhere in there is a polyfill for a browser that stopped existing in 2016. Bundlers already attack this, but they do it by guessing: tree-shaking assumes your dependencies don’t do anything clever, and when that assumption breaks you find out in production.

So I wanted to know whether you could do it without guessing. Proven unreachable, or it stays. I built a thing called reducer to find out, and the answer came back 0.0% on every application I measured, to the byte.

Ground rules

The project ran on one invariant, written down before any code existed:

Proven reachable   → KEEP
Possibly reachable → KEEP
Unknown            → KEEP
Proven unreachable → MAY REMOVE

A failure to find a path is never proof that no path exists. If the analyzer can’t tell, the code ships.

I also wrote a negative result is a successful experiment into the first prompt, along with “do not manipulate scope or analysis rules to hit these thresholds.” That’s easy to write and harder to honor a day later when the number comes back bad, which is why it goes in a file first.

Then I pre-registered the rest: 19 real applications pinned to exact commit SHAs, a package population of 6,603 unique name@version pairs frozen from lockfile walks, and a CVE set of 314 qualifying advisories narrowed by a pre-declared cap to 15 CRITICAL ones, giving 24 app-by-advisory rows. Kill threshold ~10–15% median reduction on target apps. All of it committed before the analyzer that would produce the numbers was written.

The first zero

An early calibration run against five real apps returned 0.0% incremental reduction on all five. My first reaction was that my analyzer was too dumb.

Two packages sitting in ordinary production dependency trees, optional-require and require-at, both ship this:

var xrequire = eval("require"); // Using eval to avoid tripping bundlers like webpack

Someone obfuscated a require call so that static analysis tools would fail to see it. Once one module in your tree can reconstruct an arbitrary loader, a sound analyzer has to assume anything might be loaded, so everything stays. On a clean, hazard-free ESM fixture the same analyzer removed 41.6% of the bytes, so the capability was there. Real dependency trees just don’t look like that fixture.

Which left the question of whether 0% was an engineering gap I could close with a better model or a semantic ceiling of real Node code.

The analyzer

I rebuilt the hazard model around capabilities rather than scary-looking syntax. Not “this file contains eval, panic” but “which module-loading powers can flow to which code, resolving to what set of targets.” Lexical require, createRequire, process.getBuiltinModule, dynamic import(), a loader injected into a vm context: each one tracked as a value that flows through aliasing and closures, with a domain on its target of exact, finite, package-scoped, or unbounded.

The analyzer transforms nothing. It reports the ceiling, what each conservative gate costs you, and the set of approximations jointly responsible for retaining each symbol. The measurement can kill the transform before I write it.

To check the analyzer itself I built what I called the Torture Garden: a real HTTP service whose dependency tree is 47 fabricated packages, each exercising one mechanism, with per-symbol ground truth declared by construction. Plus pinned real hazard controls, a native-module control, a WASM control, and a reducible positive control. 58 recorded HTTP contracts replayed byte-for-byte.

Then the mutation check. It’s not enough for a test to pass against the reduced app; you have to break each symbol in the original app and confirm the test notices.

The Garden caught 17 critical defects in my own pipeline, including source maps that were still shipping the full text of the code I’d claimed to remove. If you’re producing security attestations that say the vulnerable code isn’t present, and it’s sitting in a .map file next to them, you’re lying in a machine-readable format.

Analyzed in isolation, the analyzer removes 12 of 16 fabricated must-remove symbols. Put those same packages together into one dependency tree and it removes 0 of 16.

The result

Sixteen of the nineteen apps completed. (Three timed out: n8n’s 2,040-package universe burned about twelve hours against a roughly thirty-configuration counterfactual sweep before I killed it. I recorded those as a tooling deviation rather than a result.)

Every measured app: 0.0%. Median, P25, P75, targets, controls. Zero apps above 10%.

I’d built in an escape hatch so a pessimistic default couldn’t kill the thesis by itself: switch off each conservative gate one at a time and see how much reduction it was costing. Also every pair of gates, because gates interact and two individual zeros can hide a large joint gain. And four named sound refinements, real ones with stated proof obligations rather than “assume dynamic require is harmless.”

All of them: 0.0%. Every gate, every pair, every refinement.

The dominant blocking sets are unions of thirteen to fifteen gates at once, so relaxing one or two leaves the rest still widening the universe. Across the corpus the analyzer saw roughly 300,000 module-load sites, and about 294,000 of them were exact-tier, a static known target. The specifier side is fine. But every app also contained a handful of reach-any capabilities (~6,590 unbounded sinks), and 88,599 of 140,165 export surfaces were open rather than statically closed. Nearly 88,000 of those came from one shape: the exports.default / __esModule interop pattern that every Babel- and TypeScript-compiled package emits.

One reach-any capability plus an open export surface is enough to make the whole production universe possibly-reachable at full surface. Production Node trees contain both by construction. The proof obligation can’t be discharged.

The vulnerability angle went the same way: all 24 CVE rows present in the universe, 0 removable. That measurement inherited the same whole-app widening, which makes it a foregone conclusion rather than independent evidence, and it’s the claim most likely to get overstated. Whether a vulnerable function can sit in a hazard-free island inside a saturated app is still unresolved, pinned as a future experiment rather than a falsified one.

Some notes on how it ran

Most of this was built by Claude Code running on a long leash. I set a goal with a stop condition (it wasn’t allowed to finish until it had reached a pursue/refine/retire decision) and it ran about five hours overnight with no input from me, orchestrating workflows that fanned out parallel agents: implementation on one tier of model, adversarial review on a stronger one.

The adversarial passes were panels of agents whose only job was to attack the analyzer’s soundness, a couple dozen at a time, each assigned a distinct lens, then a re-attack phase where a fresh agent had to replay every previous attack and invent new ones at the seams between the fixes. One campaign found 16 real defects. Every fix widened the tool: made it keep more code and report a smaller possible reduction. That was the rule the reviewers were given. Retaining too much didn’t count as a finding; only unsoundness did, meaning cases where executing code looked removable.

const r = require; eval("r('x')"), where the aliased loader was invisible because free identifiers inside generated source were being resolved against an empty scope. A with statement, which makes scope resolution dynamic and should force the whole module opaque. Reflect.get(globalThis, 'eval'). And one that went at the experiment rather than the analyzer: my gate-off counterfactuals, the escape hatch meant to stop a pessimistic default from killing the thesis on its own, were computed by deleting load sites rather than relaxing them. Those numbers fed the kill decision, and one of the reviewers called them, correctly, fantasy.

Another finding eliminated a rescue path. I’d wanted property projection (require(x).foo proving only foo is reachable) to narrow the export surface in general; the analysis showed it’s only sound when the module’s identity is known exactly.

One defect wasn’t in my code at all. An agent chasing a nondeterministic result root-caused it to a loop-binding miscompilation in the JS engine I was running the harness on, where a property read returned two different values inside one iteration. Package N’s analysis was being scored against package N−2’s ground truth. Fixed by isolating each package into its own process.

Nearly all of the rigor came from decisions made before the interesting part: freezing the corpus before seeing results, declaring the threshold in advance, separating “diagnostic upper bound” from “thing that can rescue the thesis,” committing the raw first-run output before touching anything that might improve it. In review I caught that my first calibration numbers weren’t pinned to commits at all, which would have made them unreproducible. I also chased down why a scorer reported Node 24 when the real runs were on 25 and re-ran everything pinned.

After all that there are only nineteen source files in the repo, about 6,800 lines. Everything else, a couple hundred files, is corpus definitions, frozen package populations, garden fixtures, and result JSON. The report for one application is 68 MB. Most of the work was apparatus.

What I got

I killed the thesis. The whole-application, zero-transformation, sound size-reduction idea has no ceiling worth chasing, and I know that now for about two days of work instead of finding out after building a transform, a runtime guard, and a product around it.

If correctness has to live inside the analyzer, uncertainty must widen retention, and real dependency trees supply enough uncertainty to saturate everything. But that’s only forced if removal is permanent. If every omitted file stays byte-for-byte recoverable, a wrong prediction costs a hydration miss (latency) instead of a broken application. Correctness moves out of the analysis and into the storage model, and hazards stop being global blockers and become planning signals: a dynamic require might keep a package hot, widen a cluster, or lower a confidence score.

So the next question isn’t “can we prove this dependency permanently unnecessary.” It’s “can we predict a much smaller local working set, given that being wrong is recoverable.”

I don’t think I’d have gotten there by succeeding.