Frontend Bundle Slimming Cycle Workflow
Set a byte budget per route, measure the shipped bundle continuously, remove the biggest offenders, and hold the line with CI gates.
Objective
Reduce and then hold frontend payload size through per-route budgets, offender analysis, and budget-enforcing CI checks.
Environment
- Bundle analysis tooling in the build (analyzer plugin)
- CI able to fail on size thresholds
- A budget policy agreed with product and design
Steps
1. Set budgets per route, not per app
Define gzip byte budgets per route (landing, app, admin) reflecting their value and traffic, rather than one global number.
Expected state: A budget table exists per route with current size and target.
2. Measure what actually ships
Generate the bundle report in CI for every build, tracking per-route gzip size and module attribution (what each dependency costs).
Expected state: Every build produces a size report with per-module bytes.
3. Attack the biggest offenders
From the report, remove the top costs: duplicate libraries, full-package imports where submodules suffice, eagerly-bundled rarely-used features.
Expected state: The offender list shrinks with named removals and their byte savings.
4. Move what remains heavy to lazy
Code-split heavy-but-rare paths (admin, editors, charts) behind dynamic imports with prefetched weights for likely use.
Expected state: Initial route payloads drop while heavy paths load on demand.
5. Enforce with CI gates
Fail the build when any route exceeds its budget, with a documented override for accepted exceptions carrying an expiry.
Expected state: A PR that adds 40KB to a budgeted route fails CI automatically.
Expected outcome
Route payloads within declared budgets, offenders removed with evidence, lazy boundaries in place, and CI holding the line against regression.
Verification
The size report history shows monotone reduction to budget; an intentional +40KB change fails CI and requires an explicit waiver.
Bytes are a budget line item, not an accident
Bundle size grows by a few kilobytes per feature until the product loads like wet cement. Per-route budgets make size a visible decision, the analyzer attributes every byte, and the CI gate converts the budget from aspiration to enforcement.
route budgets (gzip):
/ 130KB current: 214KB VIOLATION
/app 260KB current: 241KB ok
/admin 180KB current: 412KB VIOLATION
offenders (analyzer, route /):
moment + locale data 68KB -> date-fns (12KB)
lodash full 24KB -> lodash-es tree-shaken (4KB)
chart lib (eager) 51KB -> lazy on chart open
offenders (/admin):
two editor libs bundled -> keep one, lazy other
after: / = 122KB (under budget), /admin = 171KB
CI: route budget check FAILS any +40KB regression
Route budgets localize the argument
A global 150-kilobyte limit punishes the admin console for the landing page’s promise and gets waived into uselessness. Per-route budgets sized by traffic and value let each surface negotiate its own number, which is why they survive contact with reality.
The waiver must be visible and mortal
Budget gates without a waiver path get disabled the week before a launch. The durable pattern is the CI failure plus a documented waiver: reason, owner, expiry. Size debt becomes explicit and reviewable instead of an accumulating silent leak.