Most incident response plans look great on paper. They fail in the first 5 minutes of a real incident. Here is why, and how to fix it before your next 3am page.
The 5-Minute Failure
When production goes down, your team has 5 minutes to:
- Detect the problem
- Acknowledge the alert
- Assess severity
- Start mitigation
- Communicate to stakeholders
Most plans handle steps 1-2 fine. They fall apart at 3-5. Here is where they break.
Failure 1: Nobody Knows Who Is On Call
import json
from datetime import datetime, timedelta
class OnCallRotation:
def __init__(self, team):
self.team = team
self.rotation_days = 7
def get_oncall(self, date=None):
if date is None:
date = datetime.now()
week_num = date.isocalendar()[1]
person = self.team[week_num % len(self.team)]
return {
"primary": person["name"],
"phone": person["phone"],
"backup": self.team[(week_num + 1) % len(self.team)]["name"],
"week": week_num
}
The fix: automate on-call rotation and display it in a dashboard that everyone can check without logging into anything.
Failure 2: The Runbook Is 47 Pages Long
Nobody reads a 47-page runbook at 3am. They Google the error message instead.
The fix: create one-page runbooks for each alert type.
ALERT: Database Connection Pool Exhausted
1. Check active connections: SELECT count(*) FROM pg_stat_activity
2. If > 80: Kill long-running queries: SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'active' AND query_start < now() - interval '5 minutes'
3. If still high: Restart connection pooler: systemctl restart pgbouncer
4. If still failing: Page DBA, escalate to SEV1
That is it. One page. Actionable. No theory.
Failure 3: Nobody Knows It Is Happening
The alert fires, the on-call engineer sees it, but nobody else does. Then the CEO asks at 9am why the site was down for 2 hours.
The fix: automated status page updates.
import json
import urllib.request
def update_status_page(page_id, incident_id, status, message):
payload = {
"incident": {
"name": message,
"status": status,
"impact": "major"
}
}
# Post to status page API
req = urllib.request.Request(
f"https://status.example.com/api/v1/incidents/{incident_id}",
json.dumps(payload).encode(),
{"Content-Type": "application/json", "Authorization": "Bearer TOKEN"}
)
urllib.request.urlopen(req)
Failure 4: The Post-Mortem Never Happens
Everyone agrees to do a post-mortem. Nobody does. The same incident happens again 3 months later.
The fix: automate post-mortem creation.
def create_postmortem(incident_id, timeline):
template = f"""# Post-Mortem: Incident {incident_id}
## Summary
[One paragraph summary]
## Timeline
{chr(10).join(f'- {t["time"]}: {t["event"]}' for t in timeline)}
## Root Cause
[To be filled]
## Action Items
- [ ] [Action] - Owner: [name] - Due: [date]
## What Went Well
-
## What Went Wrong
-
## Where We Got Lucky
-
"""
return template
The 5-Minute Checklist
Before your next incident, make sure you have:
- [ ] Automated on-call rotation (nobody has to guess who is responsible)
- [ ] One-page runbooks for top 5 alert types (readable at 3am)
- [ ] Automated status page updates (stakeholders know without asking)
- [ ] Post-mortem template ready (not created from scratch each time)
- [ ] Chat channel for incident response (not email, not DMs)
- [ ] Test the plan with a fire drill (not just on paper)
The Fire Drill
Run a simulated incident once per quarter:
- Pick a scenario (database down, API timeout, disk full)
- Start a timer
- Follow the plan exactly
- Note where the plan fails
- Fix those gaps
- Update the plan
This is the single most effective thing you can do. Plans that are never tested will fail.
For complete incident response templates and automation scripts, check out the Ops Starter Kit.
Top comments (0)