An error budget makes a reliability agreement usable during planning. Product and operations choose an objective, measure failures against it, and agree what changes when the allowance is exhausted. The number supports that agreement; it does not predict whether tomorrow’s deployment will cause an incident. Shipping a release creates risk, while observed bad events consume the measured budget.
Give the account a measurement rule
For a request-based objective, allowed bad events equal eligible requests multiplied by one minus the target. A 99.9% objective over three million requests permits three thousand bad requests. Specify which requests count, which outcomes are bad, the measurement location, and the window before attaching a release rule. A traffic-weighted allowance is not automatically a fixed number of downtime minutes.
A rolling thirty-day account drops old observations as new ones arrive. Its denominator changes when traffic changes, so the allowance can change even when the target stays fixed. A calendar-month account resets at a calendar boundary instead. Those approaches answer different questions; switching between them during an incident makes an apparently precise balance difficult to audit. Keep the definition versioned with the policy and show the window beside the balance.
Decide the response before the argument
An example policy might hold nonessential feature releases when the budget is overspent, prioritize reliability work, and allow urgent security or incident fixes through review. Name who owns the decision, what evidence permits an exception, and when ordinary release review can resume. Google’s published example is a policy for its example service, not a universal requirement to copy every threshold or escalation rule.
Remaining budget alone does not establish release readiness. An open incident, missing monitoring, a failed regression, or unsafe rollback can still prevent a change. Conversely, a necessary repair should not be blocked merely because the incident it repairs exhausted the budget. Record these decisions explicitly. The useful result is a shared response to customer impact, not an automated mechanism for blaming the team whose dashboard turned red.
When it breaks
A team can appear to recover simply because yesterday’s large incident leaves the rolling window. The arithmetic is correct, but it says nothing about whether the defect was repaired. Changing exclusions, silently replacing unavailable samples with successes, or averaging differently sized service percentages can also create a misleading surplus. Display measurement coverage and investigate definition changes before interpreting an improved balance.
Short-window burn rate supplies another view: the observed bad-event fraction divided by the allowed fraction. A high burn can warn before the longer account is exhausted. Estimating time to exhaustion from that rate assumes comparable traffic and continued behavior; it is not a promised deadline. Pair actionable alerts with the long-window account and account for sparse traffic, rather than paging on every isolated failure.
Check it quickly
Save the following as budget-policy.py and run it with Python 3. It uses synthetic daily request counts to verify rolling-window accounting and a deliberately small example policy. It does not change a deployment system or estimate the risk of a real release.
from fractions import Fraction
target = Fraction(999, 1000)
# Every synthetic daily bin has 100,000 eligible requests.
days = [(100000, 1300)] + [(100000, 70)] * 29
def account(window):
eligible = sum(total for total, _ in window)
bad = sum(errors for _, errors in window)
allowed = eligible * (1 - target)
return bad, allowed, allowed - bad
def feature_policy(remaining, incident_open):
return "hold" if remaining < 0 or incident_open else "eligible for review"
before = account(days)
after = account(days[1:] + [(100000, 50)])
assert before == (3330, 3000, -330)
assert after == (2080, 3000, 920)
assert feature_policy(before[2], False) == "hold"
assert feature_policy(after[2], True) == "hold"
assert feature_policy(after[2], False) == "eligible for review"
for label, (bad, allowed, remaining) in [("before", before), ("after", after)]:
print(f"{label}: bad={bad}, allowed={float(allowed):.0f}, remaining={float(remaining):.0f}, spent={float(bad / allowed):.2%}")
print("after rotation, incident still open:", feature_policy(after[2], True))
print("after rotation, incident closed:", feature_policy(after[2], False))
The first account has 3,330 bad requests against an allowance of 3,000: 111% spent and a balance of minus 330. Removing the oldest day and adding the new day leaves 2,080 bad requests, 69.33% spent, and 920 remaining. Both windows contain the same three million eligible requests in this fixture; real traffic need not stay constant.
The policy still prints hold while an incident is open, despite the positive new balance. Only the final case becomes eligible for review, which intentionally stops short of granting deployment approval. This separates a verified calculation from the operational judgment that uses it. Review the definition, coverage, incident evidence, and release checks together before acting on the account.
Further reading
Sources
- Google SRE Workbook: Example Error Budget Policy, release restrictions, exceptions, and ownership, checked 2026-09-13.
- Google SRE: Service Level Objectives, measurement boundaries and reliability targets, checked 2026-09-13.
- Google SRE Workbook: Alerting on SLOs, burn rate and alerting windows, checked 2026-09-13.