Ship Faster, Break Less: sre With 0.1% Regret
Practical guardrails to make reliability pay for itself.
Reliability Is a Product Feature, Not a Slogan
We don’t ship a feature until it’s reliable enough to keep the lights on for the next one. That’s the simplest way we explain sre to folks who think reliability is a checkbox. Users don’t buy our uptime; they buy outcomes that require uptime. So we treat reliability as a first-class product feature with acceptance criteria, budgets, and trade-offs. When the pager goes off at 3 a.m., it’s not because “production is fragile,” it’s because we made a product decision—usually unconsciously—to overdraw the reliability account. Let’s make it conscious.
SRE gives us a few habits that compound: define what “good” looks like with SLIs and SLOs; enforce a limit with an error budget; manage change so we don’t set fire to that budget; and automate toil so humans do less repetitive work and more system design. None of this is mystical. It’s plumbing and discipline. We measure, we set thresholds, and we link those thresholds to how and when we ship. We choose boring, repeatable releases over adrenaline-fueled heroics.
If you want the long-form, the Google crew wrote the classic on this mindset; start with their overview of modern reliability practice in the Site Reliability Engineering book. What we’ll do here is cut to the bits that change teams quickly: SLOs that bite back, release gates that respect error budgets, observability that finds the story in the noise, and incident playbooks that calm everyone down. Then, because bills arrive right on time, we’ll cover cost-aware reliability. Let’s trade surprise outages for boring dashboards and surprisingly happy users.
Craft SLOs That Bite Back (And Stay Measurable)
SLOs only work when they’re simultaneously boring and painful. Boring because they’re simple and measurable. Painful because breaching them actually changes behavior. Start with two or three SLIs that match user perception: request latency, availability, and perhaps something domain-specific like “time to first byte on search results.” Define what success feels like from the browser or client perspective. Percentiles beat averages; p95
tells us what our real humans are feeling on a grumpy day.
Link your SLIs to SLOs that a product manager can defend to a customer: “99.9% of requests complete under 400 ms over 30 days.” Then wire up burn-rate alerts so we don’t wait for the month to end to discover we’re underwater. The trick is multi-window burn detection (fast and slow) so we catch both raging fires and slow leaks. The Google SRE chapter on SLIs, SLOs, and SLAs is a practical reference.
Here’s a small Prometheus example that assumes a RED-style HTTP counter and latency histogram:
# Recording: error rate and latency
record: job:http_request_error_ratio
expr: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
record: job:http_request_latency_p95
expr: histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
# Alerts: fast and slow burn (assuming 99.9% target => 0.1% budget)
- alert: SLOFastBurn
expr: job:http_request_error_ratio > (0.001 * 14.4) # ~14.4x means 2% budget gone in 10m
for: 10m
labels: {severity: page}
- alert: SLOSlowBurn
expr: job:http_request_error_ratio > (0.001 * 6)
for: 1h
labels: {severity: ticket}
Keep the math visible and the thresholds agreed. If product, ops, and engineering can’t explain why the budget is 0.1% instead of 1%, we’re cargo-culting, not engineering.
Wire Error Budgets Into the Pipeline (Release Gates, Not Red Tape)
SLOs matter when they change how we ship. If we keep deploying full speed into a burning budget, we’re just stylish arsonists. Let’s feed error budget burn into CI/CD. The constraint is simple: high burn slows or stops risky changes; healthy budget lets us ship faster. Releases become a function of reality, not the calendar.
We like a simple gate: a job that queries an SLO API (Prometheus, Datadog, whatever), computes burn rate, and sets an output that decides whether to proceed. Small changes (docs, non-production-only code) can bypass, but anything touching request paths or state must respect the budget. We don’t need five committees; we need one Boolean.
Here’s a GitHub Actions sketch. It calls a tiny script that checks burn rates and returns non-zero when we’re overspending:
name: deploy
on:
push:
branches: [main]
jobs:
gate-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check SLO Burn
run: |
./scripts/check_burn.sh --slo=checkout-latency --max-burn=6 --window=1h
- name: Deploy
if: ${{ success() }}
run: ./scripts/deploy.sh
And check_burn.sh
can be a 30-line curl + jq that hits Prometheus or your metrics backend. When the gate trips, we don’t yell; we switch to budget repair work: rollbacks, feature flags, traffic shaping, caching, or capacity adjustments. We keep ownership by keeping the gate in code and the config in the repo, reviewable like everything else. It’s not red tape; it’s the speed limit sign that keeps our bumper attached.
Observability With Opinions: Signals, Not Souvenirs
Observability isn’t about hoarding metrics like Pokémon. It’s about seeing the right signals at the right time so we can explain “why now?” in under five minutes. We aim for three layers: metrics for fast feedback and SLO math; traces to follow user-critical requests across services; logs for high-fidelity details when we need to zoom in. Start with the RED/Golden signals—Rate, Errors, Duration—and add saturation for backend components. If your dashboards require a tour guide, they’re too smart by half.
Prometheus is an excellent baseline for service-side metrics and alerting, with sane data models and language features that do SLO math well. Their docs are refreshingly direct; if you’re new, skim the Prometheus overview. For traces and logs, OpenTelemetry gives us a standardized way to instrument once and swap backends later. Fewer client libraries, fewer surprises. The OpenTelemetry docs have good examples for common frameworks.
A pragmatic rule: every alert should map to an action, and every dashboard should start with a one-screen overview. We like an SLO top panel (error rate and p95 latency) and a bottom panel of the usual suspects: dependency health, resource saturation, and recent change events (deploys, feature flags). Annotate deploys; deploys move needles. Finally, add exemplars on latency histograms so we can jump from a slow bucket straight into a trace. It reduces guesswork and the kind of Slack archaeology that ruins afternoons.
Capacity Planning You Can Explain to Finance
Capacity planning often sounds like a séance: lots of hand-waving, oracles in spreadsheets, and a vague promise that we “should be fine.” Let’s keep it measurable. We start with SLO targets and observed demand. If the SLO is 99.9% under 400 ms, then p95 latency under typical concurrency needs plenty of headroom; p99 during flash sales shouldn’t tip over. Model peak by multiplying your normal peak by a realistic surge factor—we like 1.5x unless history or marketing tells us otherwise—and set scaling rules to keep utilization in the Goldilocks zone.
Queueing is the silent killer. Watch the backlog and service time; they explode nonlinearly as utilization approaches 100%. Give yourself a budget for saturation, like “CPU below 70% during sustained peak,” not because CPUs panic at 71% but because queueing theory stops being polite. If the math makes your eyes glaze over, the AWS Well-Architected capacity and performance pillars have short, readable guidance we can adopt without reinventing the wheel.
We also treat caches, databases, and message brokers as first-class citizens in capacity planning. Just because autoscaling works for stateless services doesn’t mean your primary can grow a new index at 3 p.m. Give the storage layer its own SLOs and saturation limits. Finally, make capacity reviews part of sprint rhythm. Ten minutes, a shared graph, and a pre-commit to any upgrades. Nothing ruins a sprint like surprise re-shards.
Incidents That Don’t Melt Slack
Incidents are where reliability habits pay rent. Our aim is calm, fast recovery with minimal self-inflicted wounds. First, name a single Incident Commander (IC) every time. The IC isn’t the smartest person in the room; they’re the radio operator. They set priorities, avoid thrash, and keep the channel clear. Everyone else either works a task or stays quiet. We also appoint a scribe for timestamps and actions; today-you will thank past-you when writing the report.
Severity should match user impact, not our stress level. SEV definitions that mention “how loud the VP is” will eventually erase credibility. We measure the right things: time to user-impact detection, time to mitigation, and time to full recovery. MTTR is cute on a quarterly slide but lousy in the heat of the moment; we care about “time to reduce pain,” which often comes from traffic shifting, feature flags, or toggling degraded mode instead of fixating on root cause.
We ban blame in post-incident reviews and hunt for systemic contributors: missing guardrails, brittle scripts, unclear runbooks, blind spots in observability, unsafe defaults. Two practical rituals help: freeze on “risky actions” until the IC approves them, and announce every command before execution. Even a “kubectl get pods” at the wrong time can fan the flames. Keep the fixes small and reversible. And for the love of uptime, remember to hydrate the humans; a five-minute break is often worth more than a clever grep.
Cost-Aware Reliability Without Drama
Great reliability that costs triple isn’t great. Our job is to keep the SLO while not teaching Finance new swear words. We do this with guardrails and visibility, not heroic penny-pinching. Tag resources by service and environment, publish a weekly cost-by-SLO report, and make cost a first-class dimension in performance reviews for systems, not people. If a feature needs 3x cache to hold p95 under 400 ms, fine—until we’re back within budget. Costs should rise with intentional decisions, not entropy.
We like codified budgets with alerts that page teams, not just finance. Here’s a tiny AWS example in Terraform for monthly spend alarms:
resource "aws_budgets_budget" "monthly" {
name = "prod-monthly-budget"
budget_type = "COST"
limit_amount = "25000"
limit_unit = "USD"
time_unit = "MONTHLY"
notification {
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = ["sre-alerts@example.com"]
}
}
Tie scaling policies to SLOs, not vibes. If p95 latency is fine, we can be more aggressive on scale-in; if burn accelerates, we pause scale-in before we gift ourselves a 3 a.m. page. For storage, cap retention by use-case and keep raw logs in cold storage if we truly need them. The AWS Well-Architected Cost Optimization pillar has a simple checklist; we cherry-pick and automate anything repeatable. Cost is just another SLO: dollars per satisfied request.
The Boring Habits That Make Teams Fast
If we want speed, we need trust. Trust shows up when the system behaves predictably and we have confidence the guardrails will catch us. That means release trains instead of mystery launches, feature flags over toggling code paths in prod, and post-incident fixes that remove whole classes of problems. We cut toil by automating resets, rollbacks, runbook steps, and all the tiny “someone should click this” rituals. If a step is predictable and reversible, it belongs in a script, not a brain.
We write runbooks with one task per page, not novels. We embed links to dashboards next to every step and show an example command for any risky action. We put rehearsal on the calendar: chaos drills for graceful failure, failover tests that exercise the control plane, and “pager practice” so new folks don’t meet the on-call phone for the first time at 2 a.m. We don’t need Hollywood chaos; we need realistic, reversible faults that teach. To keep us honest, we track a small set of team health metrics: pages per on-call shift, toil hours, number of auto-remediations that actually worked this month, and change failure rate linked to real user impact.
SRE isn’t a badge. It’s a bag of habits that make the whole team faster because reliability is no longer a mystery. We measure what matters, we ship according to the budget, we watch the right signals, and we keep incidents humane. When it’s boring, we’re winning.