CONCEPT Cited by 1 source
Dead-code elimination¶
Dead-code elimination (DCE) is a linker / compiler pass that drops symbols the reachability analysis proves no entry-path reaches. The result is a smaller binary with the same behaviour.
Two levels:
- Function-level DCE — remove functions / procedures no call site can reach from the entry point.
- Method-level DCE — remove methods that no typed call site can resolve to. Harder — requires proving every method lookup is statically decidable.
Why method-DCE is fragile¶
Method-level DCE is safe only when the linker can prove every method invocation's target at build time. Any form of dynamic dispatch whose target isn't statically known defeats the proof and forces the linker to conservatively keep every candidate.
Go-specific load-bearing examples (from Datadog 2026-02-18):
reflect.MethodByName(name)with non-constantname→ linker retains every exported method of every reachable type. Canonical offenders:text/template,html/template.- Stdlib
pluginimport → linker enters dynamic-link mode, disables method-DCE AND keeps every unexported method. Cost in the Datadog Agent: 245 MiB.
Scale of impact¶
Re-enabling method-DCE in the Datadog
Agent required patching ~a dozen dependencies' reflect call
sites and forking stdlib text/template + html/template into
pkg/template/. Result: 16-25 % per binary / ~100 MiB total
on Linux amd64. Kubernetes adopted it
and reports 16-37 % of its own.
Safety caveat¶
Aggressive method-DCE is a correctness risk: dropping a method that's actually called at runtime → panic. Static proofs that the linker can verify (non-reflective dispatch) are the only safe path. Datadog considered a different approach — "instrument binaries to emit a list of all methods used at runtime, then edit the compiler artifacts to force the linker to remove all other methods" — but rejected it because "it would have introduced runtime panics if we removed a method that ended up actually being called". Source-level patches the linker can statically reason about are strictly safer.
Not Go-only¶
Every mature toolchain has an analogous pass:
- C/C++ link-time optimization (
-flto) +--gc-sections. - Rust
panic=abort+-Z build-std+ release-profile defaults. - JVM tree-shaking via ProGuard / R8.
- JavaScript bundler tree-shaking (webpack, Rollup, Vite, esbuild, Turbopack).
Each has its own defeat conditions (eval, reflection, class-loader), the same mechanism class, and the same cure (constrain the dynamic surface so the linker can prove reachability).
Seen in¶
- sources/2026-02-18-datadog-how-we-reduced-agent-go-binaries-up-to-77-percent
— Go-specific canonical instance: method-DCE +
plugin-mode + contest space of workarounds.