SLOs, SLIs, and Error Budgets: A Practical Guide
SLOs, SLIs, and Error Budgets: A Practical Guide
Every team I've worked with eventually says the same sentence: "we need SLOs." Then they write down "99.9% uptime" somewhere, nobody revisits it, and six months later nobody can say whether it's being met, or what happens if it isn't. That's not an SLO. That's a number with good intentions.
At Parkar, defining real SLOs — ones with teeth — was one of the higher-leverage things we did for reliability. Here's the framework, with the actual math, not just the theory.
The Three Terms, Precisely
These get used interchangeably and shouldn't be:
- SLI (Service Level Indicator) — a measurement. "The percentage of requests that returned successfully in under 300ms over the last 5 minutes."
- SLO (Service Level Objective) — a target for that measurement. "99.5% of requests meet the SLI over a rolling 30 days."
- SLA (Service Level Agreement) — a contract with consequences, usually with a customer. Most internal teams don't need one; they need the first two.
If you only remember one thing: the SLI is what you measure, the SLO is the bar you set for it. Everything else follows from getting those two right.
Picking SLIs That Mean Something
The trap is picking SLIs because they're easy to measure, not because they reflect what users actually experience. CPU utilization is easy to measure and almost never what a user cares about.
Good SLIs are usually one of:
- Availability — proportion of successful requests (
good_requests / total_requests) - Latency — proportion of requests faster than a threshold (
requests_under_300ms / total_requests) - Freshness — for data pipelines, how stale is the data a user is looking at right now
- Correctness — proportion of requests/records that returned the right answer
Start with one, maybe two, per service. A service with twelve SLIs has zero SLIs anyone actually looks at.
Setting the Target
Don't start with 99.99%. Start by measuring what you're actually delivering today, for 2–4 weeks, with no target at all. Then set the SLO slightly below your current performance — tight enough to matter, loose enough to be achievable without heroics.
Measured: 99.87% success rate over the last 30 days
SLO target: 99.5%
# Gives headroom for normal variance without redefining "broken" every week
Every 9 you add costs more than the last one. Going from 99% to 99.9% is a different engineering problem than 99.9% to 99.99%. Pick the target your business actually needs, not the one that sounds impressive in a deck.
The Error Budget, With Real Numbers
The error budget is just 1 - SLO, expressed as an allowance:
SLO: 99.5% availability over 30 days
Error budget: 0.5% of requests may fail
Total requests in 30 days: ~12,960,000 (assuming 5M req/day)
Error budget: 64,800 failed requests allowed over 30 days
That budget is a resource your team spends, the same way you'd spend a sprint's capacity. Deploys, risky migrations, experiments — they all consume budget when they cause failures. The budget existing is what makes "should we ship this risky change" an actual conversation instead of a vibe.
Burn Rate: Catching Budget Loss Before the Budget Is Gone
A 30-day budget doesn't help if you burn all of it in six hours and nobody notices until the monthly review. Burn rate alerting solves this — alert on the rate you're consuming budget, not just the absolute remaining amount.
The pattern (borrowed from the Google SRE workbook, and it works): use two windows per alert, a long one for confidence and a short one for speed.
| Severity | Long window | Burn rate | Budget consumed in window | |---|---|---|---| | Page immediately | 1 hour | 14.4x | 2% of 30-day budget in 1 hour | | Page immediately | 6 hours | 6x | 5% of 30-day budget in 6 hours | | Ticket, not page | 24 hours | 3x | 10% of 30-day budget in 24 hours | | Ticket, not page | 72 hours | 1x | 10% of 30-day budget in 72 hours |
burn_rate = (bad_events / total_events) / (1 - SLO)
# burn_rate of 1 = consuming budget exactly on pace to exhaust it in 30 days
# burn_rate of 14.4 = exhausting the 30-day budget in ~50 hours if sustained
This is the difference between "we found out from a customer" and "we paged ourselves two hours before it became a customer problem."
An Error Budget Policy Needs Teeth
Defining the budget without a policy for exhausting it is theater. Ours is simple:
- Budget healthy (>25% remaining): ship normally
- Budget low (below 25% remaining): riskier changes need a second reviewer and a rollback plan reviewed before merge
- Budget exhausted: feature work pauses; the only merges allowed are ones that improve reliability, until the budget recovers
The exhausted state is the one that actually changes behavior — and the one teams are most tempted to skip. Without it, the SLO is just a graph nobody's incentivized to protect.
Mistakes I've Seen (and Made)
- Too many SLOs. One meaningful SLO per service beats five nobody checks.
- No burn-rate alerting. A 30-day rolling number tells you the budget is gone, not that it's about to be.
- No policy consequence. An error budget with no teeth is a chart, not a control.
- Never revisiting the target. Traffic patterns and architecture change; a target set a year ago may no longer reflect reality. Review quarterly.
Checklist
- [ ] Pick one SLI per service that reflects real user experience
- [ ] Measure for 2–4 weeks before setting any target
- [ ] Set the SLO slightly below current performance, not at an arbitrary round number
- [ ] Calculate the error budget in actual request counts, not just a percentage
- [ ] Set up multi-window burn-rate alerts, not just a monthly budget check
- [ ] Write an error budget policy with an actual consequence when it's exhausted
- [ ] Revisit the target every quarter
An SLO is only real once it can say no to a deploy. Everything before that is a dashboard.




