CONCEPT Cited by 3 sources
Binary-size bloat¶
Binary-size bloat is the monotonic growth of a compiled artifact over time as features + dependencies accumulate, without a commensurate removal discipline. It's a "nobody's fault" bug: every individual commit adds a little, nobody's PR is obviously unreasonable, and the artifact size curve compounds.
Why it matters¶
- Network costs and transfer time balloon (cloud billing, slow bootstraps).
- Resource usage grows even when you don't use the new features — RSS + executable pages are a function of binary size.
- Deployability shrinks — size-sensitive targets (serverless platforms, IoT devices, containerized workloads with tight image layer budgets) stop working.
- Developer perception worsens, "harder to use the Agent on resource-constrained platforms" (Datadog 2026-02-18).
Canonical datum¶
Datadog Agent Linux amd64 .deb:
| Version | Compressed | Uncompressed |
|---|---|---|
| 7.16.0 (2019) | 126 MiB | 428 MiB |
| 7.60.0 (Dec 2024) | 265 MiB | 1,248 MiB |
+110 % compressed, +192 % uncompressed over 5 years — every component (features, dependencies, build matrix) growing together with no counterweight.
Mechanisms (Go-specific, but transferable)¶
- Transitive-dependency reachability. A single direct import drags its full transitive closure into the binary (concepts/transitive-dependency-reachability). Adding a trivial helper can pull tens of MiB of unrelated library code if that helper's package happens to import it.
- Dead-code-elimination defeat. The
linker's DCE pass only prunes what it can
prove is unused. Any code that makes static proof impossible
— e.g.
reflect.MethodByName(concepts/reflect-methodbyname-linker-pessimism), importingplugin(concepts/go-plugin-dynamic-linking-implication) — forces retention of everything. - Accidental imports. A dependency's
init()function or global-variable side effects can force symbol retention even when the declared API surface isn't used.
Cure: audit + prune¶
Datadog's 2026-02-18 program cut the Agent by up to 77 % over 6 months without removing any features. The recipe is measurement-driven:
- Map the binary:
go list -f '{{ .Deps }}'+ systems/goda (why each dep) + systems/go-size-analyzer (byte cost). - Prune at the edge: build tags exclude files + their imports; move single offending functions into their own packages.
- Re-enable DCE by patching
reflect.MethodByName
sites and removing unused
pluginimports. - Upstream the fixes so everyone benefits and no long-lived fork is maintained.
Result¶
Agent binaries shrank 56-77 %; .deb package halved. Features
preserved. Kubernetes — picking up Datadog's method-DCE
enablement — reports 16-37 % on its own binaries.
Seen in¶
-
sources/2026-02-18-datadog-how-we-reduced-agent-go-binaries-up-to-77-percent — canonical wiki instance: 5-year growth + 6-month cure.
-
sources/2026-01-28-meta-rust-at-scale-an-added-layer-of-security-for-whatsapp — mobile / client-side variant where binary-size is a distribution-channel constraint (APK / IPA size). Meta flags "initial binary size increase due to bringing in the Rust standard library" as one of two named hurdles of its wamedia C++→Rust rewrite (the other is build-system support across Android/iOS/Mac/Web/Wearables). Counter-intuitive source-code result: 160K LoC C++ (no tests) → 90K LoC Rust (with tests) — code size inverted. Binary output still grows initially from stdlib inclusion; the mitigation playbook is language-specific (panic=abort, LTO, strip, feature-gated std) and not detailed in the post. Canonical illustration that the cross-platform memory-safety win from Rust comes with a concrete size-budget cost that mobile apps have to solve for — see concepts/cross-platform-client-library.
-
sources/2026-04-09-meta-escaping-the-fork-webrtc-modernization — shim-layer-placement variant: when statically linking two copies of a large library into the same binary to support dual-stack A/B testing, the layer at which you interpose the shim has order-of-magnitude size consequences. Meta's WebRTC shim data point: call-orchestration-layer duplication would have added ~38 MB uncompressed; lowest-layer shim (against
webrtc::*directly) added ~5 MB uncompressed — an 87% reduction from layer choice alone. The subsequent upstream WebRTC migration itself delivered 100–200 KB compressed net savings per-app from upstream's own efficiency wins — stacked on top of the shim tax. Canonical wiki statement that shim layer selection is a binary-size decision, especially for mobile apps (Quest, Messenger, Instagram) with app-store distribution budgets.