A build fails because a genuinely needed feature pushes a route's JavaScript over its size budget. What policy around bundle budgets keeps that failure useful, rather than something teams routinely override?
answer
- a budget forces a decision
- give the menu, not the veto
- never raise it in the same commit
- warn-only decays into noise
- overrides need an owner and a reason
basics
~20 sTreat the breach as a forced decision with named options — pay for the feature by cutting or deferring something else, ship it lighter, or raise the budget deliberately with an owner and a recorded reason — and never let the raise happen silently in the same change that caused it.
solid answer
~50 sA budget's job is not to block features; it is to make the cost of a feature visible at the moment someone is deciding to ship it. So when it breaks, the policy should present a short menu: defer the new code off the critical path, find a lighter way to do it, remove something of comparable weight, or raise the budget on purpose. All four are legitimate — the failure mode is the fifth option, bumping the number in the same commit so nobody has to choose. I keep budgets as hard failures, because warn-only budgets get ignored within a release or two, but I pair that with an override that is cheap, explicit and visible: a separate change to the committed budget file, an owner who approves it, and a one-line reason. And I tie the number back to a user-facing goal, so raising it is a product tradeoff with a stated cost rather than a build-tooling formality.
go deeper
Know that a failing size budget is a prompt to look at what your change added, and that quietly raising the number to make the build pass is not the expected response.
Be able to list the real options when a budget breaks — defer the code, choose something lighter, remove comparable weight, or raise the number deliberately — and explain why the raise belongs in its own change.
Argue for hard failure with a cheap documented override, and show that you watch override frequency as a signal about whether the number itself is still right.
Own the anchoring: tie the byte figure to a stated experience goal on a real device profile so that raising it is an explicit product tradeoff with a named approver, not a build-tooling formality nobody can argue against.
## What a budget is actually for A size budget does not make code smaller. It converts an invisible, gradual cost into a discrete event at a moment when someone still has options. Every kilobyte of page weight is added by someone who had a reason, and each addition is individually defensible; the harm is cumulative and shows up months later as a slow product nobody chose. The budget's function is to interrupt that drift with a decision point. This framing determines the policy. If the goal were prevention, overrides would be failures. Since the goal is a decision, overrides are fine — as long as they are decisions, made by someone accountable, and visible afterwards. ## The menu when it breaks A good policy tells the engineer who hit the wall what their options are, in rough order of preference: 1. **Defer it.** Move the new code off the initial payload so it loads on interaction or on navigation. Often the cheapest fix and usually the right one for features not everyone uses. 2. **Ship it lighter.** A smaller dependency, a narrower import, dropping a feature of the library that is not needed, or implementing the small piece actually required. 3. **Pay for it.** Remove or defer something of comparable weight from the same route. This is the option that keeps a fixed budget honest, and it is the one that quietly forces cleanup of things nobody would have prioritised otherwise. 4. **Raise the budget deliberately.** Sometimes the feature is worth the bytes. Then the increase is a real decision: separate change, named approver, stated reason, and ideally a note about what would let it come back down. What is not on the menu is editing the number inside the same commit that broke it. Done routinely, that turns the budget into a log of whatever shipped — it records history instead of constraining it, and reviewers stop reading the change because it always accompanies a feature they already approved. ## Hard fail versus warning Warn-only budgets are attractive because nothing gets blocked, and they almost always decay. A warning that appears in logs with no failing status competes for attention with everything else in a build log and loses; after a few releases it fires constantly and means nothing. Hard failure with a cheap, documented override is the more durable arrangement. The failure guarantees a human looks; the override guarantees the team is never actually stuck. What makes the override healthy is that it costs a small, visible action — a separate commit to a tracked file, an approval from whoever owns the number — rather than a flag someone can set in passing. The corollary is that override *frequency* is itself a metric worth watching. If the budget is raised most sprints, either the number was wrong or the product has genuinely changed shape; both are worth an explicit conversation. A budget nobody has ever hit is also suspicious — it is probably set above where the code is heading and constraining nothing. ## Ownership A budget with no owner is a budget that gets raised by whoever is most inconvenienced by it. Name someone — a tech lead, a performance working group, the team that owns that route — who approves increases. Their job is not to say no; it is to make sure the tradeoff was stated. That means the reason recorded alongside the new number should be readable a year later: what was added, why deferring or trimming was rejected, and what would make it reducible again. ## Anchor the number to something outside the build The strongest version of this policy connects the byte number to a user-facing goal: the route should be usable within a stated time on a stated device and connection, and the budget is the byte figure that supports it. Then raising the budget is not a build-tooling adjustment, it is a decision to spend part of a user-experience commitment on a feature — a conversation product people can participate in, with a cost stated in terms they care about. Without that anchor, a budget is an arbitrary number defended on principle, and arbitrary numbers lose arguments against concrete features every single time. With it, the question stops being "can we raise this?" and becomes "is this feature worth being that much slower for the people who use this screen?", which is the question the budget existed to force in the first place.
- A team raises its route budget in most sprints, always with a reason attached. Is the process working?The mechanics are working and the number is not. Routine increases mean the budget is not constraining anything, so either it was set below where the product legitimately needs to be, or the product's weight is growing faster than anyone intends. Treat override frequency as a signal and re-derive the number from the user-facing goal rather than continuing to approve raises.
- Why not simply make budgets warn-only, so nobody is ever blocked?Because warnings decay. A non-failing message competes with everything else in a build log and loses, and once it fires on most builds it conveys nothing. Hard failure with a cheap, visible override achieves the same non-blocking outcome while guaranteeing a human actually looks at the number once.
- What makes a recorded reason for a budget increase actually useful later?That it is readable without the surrounding context: what was added, what it cost in bytes, why deferring or trimming it was rejected, and what would let the number come back down. Reasons like "needed for launch" tell a future reader nothing and make the increase impossible to revisit.
saying these in an interview costs you the question
- Bumps the budget in the same commit that breaks it
- Treats every breach as a reason to block the feature
- Makes budgets warn-only so nobody is inconvenienced
- Leaves the budget number with no named owner
- Defends the number as a principle with no user-facing basis