SRE Anti-Patterns & Pitfalls
SRE doesn't usually fail with a dramatic mistake. It fails the way most disciplines fail: one quiet compromise at a time, each one small enough to excuse in the moment, until the mechanisms that made the discipline real are still technically present — an SLO exists, an error-budget policy is written down, postmortems are labeled blameless, alerts fire, a toil number gets reported — but none of them do the job they were built for anymore. This page is a field guide to five of the most common ways that happens. Each one maps directly to a mechanism you've already met earlier in this course, and each gets the same treatment: what the compromise looks like from the inside, the specific tell that exposes it, and the fix.
Imagine a car built to sail through its yearly safety inspection: the mechanic quietly retapes the sensor that would trip the check-engine light, writes "no issues found" a little faster whenever the same driver's name comes up twice, and swaps the fire extinguisher for an empty one because nobody's needed it yet and it saves weight. On inspection day, the car passes every single check. It'll keep passing right up until the day something actually goes wrong on the highway, at which point the empty extinguisher and the retaped sensor matter a great deal. SRE has the exact same failure mode. An SLO, an error budget, a blameless postmortem, an alert, a toil cap — every one of them can stay technically present on the page while being quietly hollowed out underneath. This lesson is how to find the retaped sensor before the highway finds it for you.
Why these five, and why they're dangerous precisely because they pass inspection
☺ Like you're 10: A team that's obviously skipping SRE gets caught fast. A team that's technically doing every step, just doing each one a little dishonestly, can go a long time before anyone notices.
Every anti-pattern on this page shares the same shape: it doesn't remove a mechanism, it hollows it out. A team running none of these mechanisms at all is an easy problem — anyone auditing the setup can see the gap immediately, the same way a missing production readiness review shows up as a missing checkbox. A team with an SLO dashboard, an error-budget doc, a blameless-postmortem template, an alerting stack, and a toil metric can look, from the org chart and the wiki, exactly like a mature SRE practice — and still be one incident away from discovering that none of it actually constrains anything. The tell is rarely in the artifact itself; it's in whether the artifact has ever changed anyone's behavior when it mattered.
For any mechanism on this page, ask: has this ever, even once, made someone do something they didn't want to do? An SLO that's never prompted a conversation about slowing down, an error-budget policy that's never actually blocked a launch, a postmortem process that's never survived an uncomfortable repeat name, an alert nobody's disabled because nobody knows who owns it, a toil number that's never triggered pushback — all five failures answer that question the same way: no. A mechanism that never bites isn't a mechanism. It's decoration.
Vanity SLOs: targets picked to always stay green
☺ Like you're 10: Setting your goal to whatever you already do, instead of what your users actually need, is like grading your own homework and only ever giving yourself A's.
SLIs, SLOs & error budgets defines the right SLO as the loosest target users won't notice you missing — loose enough that you're not burning engineering effort chasing invisible reliability, tight enough that breaching it actually correlates with something a real user felt. The vanity-SLO anti-pattern inverts that logic entirely: instead of deriving the target from user pain, a team derives it from whatever the service already happens to do, usually by pulling up a dashboard, eyeballing the worst week in recent memory, and setting the target a comfortable margin below it. The result is an SLO engineered, whether anyone admits it out loud or not, to never be breached — which means it's also engineered to never trigger the one behavior an SLO exists to trigger: slowing down.
This shows up in a few recognizable shapes. A latency SLO measured at p50 instead of a tail percentile like p95 or p99 will look pristine even while the slowest 5% of requests — often the ones a real customer is actually complaining about — sail past comfortably. An availability SLO copied from a template without checking it against real support-ticket volume can sit at 99.9% for a service whose users start filing tickets well before that line is crossed, meaning the "budget" the team thinks it has left has already been spent in the form of real, uncounted customer pain. And an SLO set once at launch and never revisited drifts further from reality every quarter the service changes underneath it, quietly becoming vanity even if it wasn't chosen that way originally.
The tell: the SLO dashboard has been green for as long as anyone can remember, nobody on the team can recall the last time it was actually reviewed or renegotiated, and — the sharpest test — the service still generates a normal volume of support tickets or complaints despite the SLO showing comfortable margin. If reliability looks perfect on the dashboard and imperfect to the people using the service, the dashboard is measuring the wrong thing. The fix: derive the target from evidence outside the SLI itself — support-ticket volume, churn correlated with slow periods, direct user research — not from what the service already achieves, and revisit it on a fixed cadence rather than treating it as chosen once and settled forever. SLO windows & composite SLOs covers how the measurement window itself can be gamed the same way a threshold can, and the SLO & error-budget drill is a fast way to practice the arithmetic of setting one correctly.
The error budget policy that only exists on paper
☺ Like you're 10: A rule that gets an exception every single time it would actually apply isn't a rule anymore — it's a suggestion with extra paperwork.
SLIs, SLOs & error budgets is explicit that an error-budget policy only works if it's agreed before it's needed and actually enforced the moment it's exhausted. This anti-pattern is what happens when an organization writes that policy, publishes it, even points to it proudly in onboarding docs — and then, every single time the budget actually runs out and a launch is genuinely on the line, grants an exception. The first exception feels reasonable: the feature really is important, the burn was a one-off, everyone agrees this is a special case. The second and third exceptions feel reasonable too, for the same reasons. By the tenth, the "special case" has quietly become the actual policy, and the written one is theater nobody enforces and increasingly nobody even remembers exists — right up until a postmortem asks "didn't we have a rule about this?" and the honest answer is that the rule was never once allowed to bite.
The most reliable fix is to stop trusting the enforcement step to a human's willingness to say no under pressure, and encode it instead. A deploy pipeline can check remaining error budget the same way it checks test results, and refuse to proceed without an explicit, logged override:
# A minimal budget gate, run as a required CI step before deploy.
# It queries the same burn-rate math from multi-window burn-rate
# alerting and fails the pipeline — loudly — if budget is gone.
- name: Check error budget before deploy
run: |
REMAINING=$(curl -sf "$PROM_URL/api/v1/query" \
--data-urlencode 'query=slo:checkout_requests:budget_remaining_ratio30d' \
| jq -r '.data.result[0].value[1]')
if (( $(echo "$REMAINING <= 0" | bc -l) )); then
echo "::error::Error budget exhausted ($REMAINING). Deploy blocked."
echo "Override requires an approved, logged exception — see the policy doc."
exit 1
fiA gate like this doesn't remove the possibility of an exception — sometimes a fix genuinely needs to ship through a freeze — but it changes the default from "ships unless someone objects" to "blocked unless someone explicitly, visibly overrides it," and every override becomes a data point you can review later, the way Google & the error-budget policy describes the practice actually working at the org that invented it: a small number of overrides is healthy; overrides as the normal path is the policy failing in exactly the way this section describes. Tools built specifically around reliability policy enforcement, such as Nobl9, or burn-rate rule generators like Sloth feeding a gate like the one above, exist precisely because this check is tedious enough to hand-roll that most teams that rely on a human remembering to run it eventually stop.
Track your exception rate as its own metric, the same way you'd track SLO attainment. If more than roughly one exception in five or ten actually-triggered freezes gets approved, the policy isn't being applied with judgment anymore — it's being applied with a rubber stamp. Set that ratio as a number someone owns before you need it, exactly the way the SLO itself has to be agreed before an incident, not argued about during one.
Blameless postmortems that turn blame-aware on a repeat name
☺ Like you're 10: Staying kind the first time someone makes a mistake and getting cold the third time isn't blameless — it's just blame with a delay timer on it.
Postmortems & blameless culture makes the case that blamelessness isn't about being nice — it's an information-gathering strategy, and it only works if engineers genuinely believe that reporting the full, honest, embarrassing truth costs them nothing. This anti-pattern is the specific way that belief gets quietly disproven: the process stays formally blameless for a first incident, but the moment the same engineer's name shows up in a timeline a second or third time, the tone shifts without anyone officially changing the policy. Phrasing that was carefully systemic — "the deploy lacked a canary step" — drifts back toward personal — "we need to talk about what keeps happening here." Side conversations start happening outside the review. The person quietly gets more scrutiny on their next action items, or gets rotated off high-risk on-call "for a while," or a manager mentions "a pattern" in a context that isn't the postmortem doc at all.
Nobody has to announce this shift for it to work. Every other engineer in the room notices it happen to a colleague once, and the lesson lands immediately: recurrence gets treated differently than a first offense, no matter what the template says. From that point on, the same hedging behavior postmortems & blameless culture warns about — omitting the detail that you were tired, that you'd noticed the same near-miss before and said nothing, that the runbook step you followed was already known to be stale — comes back, specifically among the engineers whose names are most likely to recur, which in most rotations means whoever's been on call the longest and carries the most institutional exposure.
| What a genuinely blameless review sounds like | What the same review sounds like once a name has recurred |
|---|---|
| "The deploy pipeline allowed this to reach prod without a canary step." | "This is the third time a change from this team has skipped canarying." |
| "The runbook step was stale and led the responder to the wrong mitigation." | "We need to make sure whoever's on call actually reads the runbook this time." |
| Action item: fix the CI gap, owned by the platform team, due next sprint. | Action item: "increase oversight" on the engineer's next few changes, unowned, no date. |
| The engineer volunteers the detail they'd noticed a near-miss two weeks earlier. | The engineer says the incident "came out of nowhere" and leaves it there. |
The tell: an engineer whose name has appeared in more than one recent postmortem starts writing noticeably vaguer timelines, stops volunteering for on-call, or a "pattern" gets referenced somewhere that isn't the postmortem document itself — a 1:1, a performance conversation, a hallway comment. The fix: when a name genuinely recurs, the correct first question isn't about the person — it's whether the action items from their previous incident were actually closed. Postmortems & blameless culture is direct about this: an action item that never gets done is worse than no postmortem at all, and a recurring failure mode is overwhelmingly more likely to be evidence that a fix from last time never landed than evidence that one person keeps failing independently. Firewall postmortem content from performance review explicitly and in writing — not as an unstated norm, which is exactly the kind of unenforced mechanism this whole page is about — and Etsy & the origin of blameless postmortems is worth reading for how deliberately that firewall was built the first time this discipline was tried at scale. The postmortem-writing drill is a good place to practice keeping language systemic under exactly this kind of pressure.
A recurring name is data about your fixes, not about your people. If the same failure mode keeps reaching the same person, ask what protected everyone else from it, and why that protection doesn't exist yet for this case. That question almost always leads back to an open, unclosed action item — the actual root cause five whys was supposed to find the first time.
Alert fatigue from dashboards nobody owns
☺ Like you're 10: An alarm nobody claims is an alarm nobody's brave enough to turn off, even long after it's stopped meaning anything.
Alert design & alert fatigue covers the general mechanics of noisy alerting and the quarterly audit that cuts a configuration back down to size. This anti-pattern is the specific, structural reason noise accumulates in the first place and never gets pruned: alerts and dashboards created in the heat of an incident — reasonably, at the time — get attributed to no team, no service owner, no line in the catalog. The engineer who wrote the rule moves teams, or leaves. A reorg redraws service boundaries and nobody re-homes the monitoring that came with the old ones. Nobody deletes an alert or dashboard they don't recognize, because deleting the wrong thing during an outage is a much worse mistake than tolerating one more line of noise — so the safe, individually rational move, repeated by everyone, is to leave it exactly where it is, forever.
The result compounds in a specific direction: the monitoring surface only ever grows. Every incident adds a new ad hoc alert or panel; almost none ever get removed, because removal requires someone with the authority and the confidence to say "this is mine, and I've decided it's safe to delete" — and an orphaned alert has, by definition, no one who can honestly say that. A team can end up on-call for a wall of dashboards where a genuine majority of the panels track services that no longer exist, thresholds nobody has revisited since a since-departed engineer set them, or duplicate coverage of the same condition under two different names.
# Ownership is a required field, not a comment — a linter run in CI
# on every PR to the alerting repo rejects any rule missing it.
- alert: CheckoutPaymentGatewayErrors
expr: sre:checkout_requests:error_ratio5m > 0.01
for: 5m
labels:
severity: page
owner_team: checkout # required — CI fails the PR without it
annotations:
summary: "Checkout payment gateway error rate above 1%"
runbook_url: "https://runbooks.acme.io/checkout-payment-errors"
owner_slack: "#team-checkout" # who to actually ask before deleting thisThe tell: during an incident, ask out loud "who owns this alert" or "who owns this dashboard," and nobody in the room can answer with a name or a team, only a guess. A service-catalog sweep finds alerting rules or Grafana panels that reference a service that was decommissioned months ago. The fix: make an owner field mandatory at creation time — the same way production readiness reviews gate a launch on monitoring existing at all, gate it on monitoring having a named, reachable owner — and run a scheduled sweep, on the same cadence as the actionability audit in alert design & alert fatigue, that treats any alert or dashboard with no matching, still-employed owner as an automatic deletion candidate rather than something it's safer to leave alone. An alert nobody owns isn't neutral; it's a coverage gap and a source of fatigue wearing the appearance of monitoring, which is precisely the false-coverage failure mode the deep-dive page on alerting warns about applied one layer up, at the ownership level instead of the individual-rule level.
"Nobody's complained about it" is not evidence an orphaned alert is safe — it's usually evidence nobody's paying close enough attention to complain. The default assumption for anything with no listed, reachable owner should be delete-and-see-what-breaks on a controlled schedule, not leave-forever-just-in-case. A dashboard that's genuinely still needed will get rebuilt by whoever needs it, this time with an owner attached.
Toil absorbed by a team's goodwill instead of pushed back
☺ Like you're 10: A team that quietly does extra chores every week instead of asking for help looks fine on the weekly report — right up until the people doing the chores burn out or quit.
Toil & automation and what is SRE both describe the 50% cap as a hard operational rule with a defined response when it's breached: push the excess back to the owning team, freeze new toil-generating launches, or add headcount — not simply work harder. This anti-pattern is the softer, harder-to-spot version of blowing through that cap: instead of escalating, a conscientious team just absorbs the overflow. Nobody wants to be the person who says no to an on-call request, files a formal complaint about workload, or looks like they can't keep up, so the extra manual restarts, the unscripted provisioning steps, the weekend babysitting of a flaky pipeline all get done anyway — quietly, on personal time, off the books.
This is more dangerous than an openly-blown cap, not less, because it produces no signal for anyone above the team to notice. A team that formally reports 70% toil triggers exactly the response toil & automation describes — it becomes a staffing and prioritization conversation. A team that's actually running at 70% toil but reports 45%, because half of it happened as unlogged weekend work nobody billed to a ticket, shows up on every dashboard as healthy. The organization has no way to intervene in a problem it can't see, and the team most likely to hide the number this way is the same team most likely to burn out from it — the conscientious ones who'd rather absorb the pain than escalate it.
| What the sprint board shows | What's actually happening | |
|---|---|---|
| Toil ratio reported | 45% — comfortably under the 50% cap | Closer to 70%, once unlogged weekend and off-hours work is counted |
| Sprint velocity | On target, most sprints | Only on target because manual work isn't tracked against capacity at all |
| On-call load | Standard rotation, nothing flagged | The same two engineers quietly pick up most of the unscripted fixes |
| Leading signal of trouble | None — the dashboard looks fine | Attrition, once someone finally leaves and says why on the way out |
The tell: toil measurement depends on self-reporting with no independent check, sprint velocity looks suspiciously stable given a rotation everyone privately describes as brutal, and the first real signal of trouble is a resignation rather than a number on a dashboard. The fix: make toil measurement independent of the incentive to under-report it — pull from ticket tags and on-call shift logs rather than relying purely on self-reported time, the same blind spot toil & automation already names as the most common way a team reports 0% toil while carrying plenty of it. Then give the 50% number the same teeth the error-budget policy needs: a pre-agreed response — push back to the owning team, freeze toil-generating launches, add headcount, per the mechanisms in SRE team topologies and reliability economics — that actually gets invoked when the number crosses the line, and treat "we're over the cap" as a routine engineering-management conversation to normalize, not a complaint that reflects badly on the team raising it. The toil-audit drill is a fast way to practice finding the gap between reported and real toil on a concrete example before you have to find it on your own team.
Goodwill is not a load-bearing part of an operating model. A team that stays under its toil cap only because engineers are quietly donating weekends doesn't have a healthy toil ratio — it has an invisible one, and invisible toil doesn't get fixed, staffed, or pushed back on, because officially, on every report that matters, it doesn't exist.
The whole rogues' gallery, at a glance
☺ Like you're 10: One table with all five traps, the giveaway that exposes each one, and the fix — the fastest audit you can run on a real SRE practice.
| Anti-pattern | The tell | The fix |
|---|---|---|
| Vanity SLOs | Dashboard's always green; support tickets keep coming anyway; nobody remembers the target ever being reviewed. | Derive the target from real user pain (tickets, churn), not from current performance; revisit on a fixed cadence. |
| Paper error-budget policy | Every budget exhaustion gets an exception; nobody can recall a launch actually blocked by it. | Automate enforcement in the deploy pipeline; track the exception rate as its own metric. |
| Blame creeping into postmortems | Recurring names get vaguer timelines, quiet oversight, or a "pattern" mentioned outside the doc. | Treat recurrence as evidence a fix didn't land; firewall postmortems from performance review, explicitly. |
| Unowned dashboards & alerts | Nobody in the room can name who owns an alert firing right now; the catalog has orphaned rules. | Require an owner field at creation; sweep and delete anything unowned on a fixed schedule. |
| Toil absorbed by goodwill | Reported toil looks fine; velocity looks stable; the real signal is attrition, not a metric. | Measure toil independently of self-reporting; give the 50% cap a pre-agreed, enforced response. |
Pick a real SRE practice you know — your own team's, or one you can reconstruct from memory of a past job. Walk the five rows above and score each one honestly: has this mechanism ever actually changed a decision, or has it only ever agreed with what the team was already going to do? For any row that scores "never bit anyone," write down the single most likely reason — is it the incentive (nobody wants to be the one who says no), the tooling (enforcement depends on a human remembering), or the ownership (nobody's clearly on the hook for it)? That diagnosis is usually most of the way to the fix already; the rest is building the automated check or the firewall this page describes for that specific row.
Foxy: Our checkout SLO dashboard has been green for eleven straight months. That's either great engineering or a very suspicious target.
Ellie the Elephant: I pulled the history — the SLO was set at 99.0% the week it launched, and we've never once dipped below 99.4% since. It was never in danger.
Sol the Sloth: ...Then it's not measuring anything. A target that never gets tested isn't a target — it's a number that happens to sit near the line.
Pip the Hummingbird: While you're at it — who owns the "checkout-legacy-v1" alert I keep getting paged for? That service was decommissioned in March.
Benny the Beaver: Not me. I've been meaning to ask the same thing for two months and just... kept eating the page instead of chasing it down.
Timmy the Turtle: That's the whole pattern in one sentence, Benny — quietly absorbing it instead of pushing back. Delete the alert if nobody claims it by Friday, and fix the SLO target properly this quarter. Both of those were always allowed to exist. Neither one was ever allowed to actually do anything.
Notice what every fix on this page has in common: it doesn't add a new mechanism to SRE's toolkit — it makes an existing one capable of saying no. A vanity SLO, an unenforced budget, a postmortem that goes quiet on a repeat name, an orphaned alert, and toil nobody escalates are all the same failure wearing five different costumes: a real constraint quietly turned into a formality. Once you can see the shape, you'll find it faster than the five specific examples here — and the reverse of this page, the operating model that keeps every one of these mechanisms honest for the long haul, is best practices & the SRE operating model.
1. What's the difference between an SLO chosen to reflect user pain and a vanity SLO, and what's the fastest tell that a green dashboard is hiding the latter? 2. Why does an error-budget policy with a 100% exception-approval rate function the same as having no policy at all — and what's the fix that doesn't depend on a human saying no under pressure? 3. In the blameless-postmortem anti-pattern, what should a recurring name in the incident log actually make you investigate first? 4. Why is an alert or dashboard with no listed owner a bigger risk than one that's simply noisy, and what's the default action for anything unowned? 5. Why is toil absorbed quietly by a team's goodwill more dangerous than toil that's openly over the 50% cap and reported as such?
Check your answers
- A user-derived SLO is set from evidence outside the SLI itself — support tickets, churn, direct feedback — so breaching it correlates with something a real user felt; a vanity SLO is set from the service's own recent performance, so it's structurally almost impossible to breach. The fastest tell: the dashboard has been green for a long stretch and the service still generates a normal volume of user complaints — reliability looking perfect on the dashboard while looking imperfect to users means the dashboard is measuring the wrong thing.
- A policy that grants every exception never actually enforces the freeze it promises, so in practice it behaves exactly like no policy — teams learn the real rule is "ask and you'll get it," not the written one. The fix is to encode enforcement into the deploy pipeline itself (a budget-check gate that blocks by default), so bypassing it requires an explicit, logged override rather than simply a human declining to object.
- Whether the action items from that engineer's previous incident were actually closed. A recurring failure mode is far more likely to mean a fix from last time never landed — a systemic gap — than that one person is independently failing repeatedly; the investigation belongs on the unclosed action item, not on the person.
- A noisy-but-owned alert has someone who can evaluate and fix it; an unowned one has no one with the standing or the confidence to decide it's safe to delete, so it accumulates forever and nobody prunes it even once it's stopped meaning anything — a coverage gap wearing the appearance of monitoring. The default for anything with no reachable, still-employed owner should be scheduled deletion, not indefinite tolerance.
- Openly over-cap toil produces a visible number that triggers the organization's defined response (push back, freeze, add headcount). Toil absorbed quietly by goodwill produces no such signal — it shows up as healthy on every dashboard because it was never logged, so the organization has nothing to react to, and the first real evidence of the problem is usually an engineer burning out and leaving rather than a metric anyone could have acted on sooner.