Incident Response
Handle production incidents systematically.
Severity Levels
| Level |
Definition |
Response Time |
Examples |
| SEV1 |
Service down, all users affected |
Immediate |
Database crash, DNS failure, auth broken |
| SEV2 |
Major feature broken, many users affected |
< 30 min |
Payments failing, search not working |
| SEV3 |
Minor feature broken, workaround exists |
< 4 hours |
Export button broken, slow dashboard |
| SEV4 |
Cosmetic or low-impact issue |
Next business day |
Typo in UI, minor styling bug |
Incident Workflow
1. Detect & Triage (first 5 minutes)
- Acknowledge the incident — "I'm looking into this"
- Determine severity level
- Check monitoring dashboards (error rates, latency, status page)
- Check recent deployments:
git log --oneline -10 — was anything deployed recently?
2. Mitigate (next 15-30 minutes)
The goal is to stop the bleeding, not find the root cause.
Quick mitigations:
- Rollback:
git revert <commit> && deploy — fastest option if a deploy caused it
- Feature flag: Disable the broken feature
- Scale up: Add more instances if it's a capacity issue
- Failover: Switch to backup/secondary if primary is down
- Block traffic: Rate-limit or block specific abusive traffic
3. Communicate
Internal:
- Open an incident channel (
#incident-2026-04-10)
- Post status updates every 15-30 minutes
- Assign roles: Incident Commander, Communicator, Engineers
External:
- Update status page
- Send email/notification to affected users if the outage is extended
- Be honest: "We're experiencing issues with X. We've identified the cause and are working on a fix."
4. Resolve
- Deploy the fix
- Verify the fix works in production (check metrics, not just absence of errors)
- Close the incident channel with a summary
5. Postmortem (within 48 hours)
Write a blameless postmortem:
# Incident: Payments failing for Stripe webhook
**Date:** 2026-04-10
**Duration:** 45 minutes (14:30 — 15:15 UTC)
**Severity:** SEV2
**Impact:** ~200 users unable to complete purchases
## Timeline
- 14:30 — Alert fires: payment success rate drops to 20%
- 14:35 — On-call engineer acknowledges, begins investigation
- 14:40 — Identified: Stripe webhook endpoint returning 500
- 14:45 — Root cause: migration added NOT NULL column without default
- 14:50 — Fix deployed: added default value to migration
- 15:00 — Payment success rate recovering
- 15:15 — Metrics back to normal, incident closed
## Root Cause
Database migration #47 added a `currency` column with NOT NULL
but no DEFAULT value. Existing rows were fine (backfilled), but
new webhook events failed because the insert didn't include `currency`.
## What Went Well
- Alert fired within 5 minutes of the issue starting
- Rollback was considered but the fix was faster
## What Went Wrong
- Migration wasn't tested with live webhook payloads
- No staging test for the webhook flow
## Action Items
- [ ] Add webhook integration test to CI (@alice, due 2026-04-17)
- [ ] Require DEFAULT for all new NOT NULL columns in migration review (@bob)
- [ ] Add runbook for payment failures (@charlie, due 2026-04-14)
Tips
- Rollback first, investigate later — speed matters more than elegance
- The most recent deploy is the most likely cause
- Don't assign blame in postmortems — focus on process improvements
- Maintain a runbook for common failure modes
- Practice incident response with game days before real incidents happen
1---2name: incident-response3description: Handle production incidents — triage, mitigate, communicate, and write postmortems.4---5
6# Incident Response
7
8Handle production incidents systematically.
9
10## Severity Levels
11
12| Level | Definition | Response Time | Examples |
13|-------|-----------|---------------|----------|
14| SEV1 | Service down, all users affected | Immediate | Database crash, DNS failure, auth broken |
15| SEV2 | Major feature broken, many users affected | < 30 min | Payments failing, search not working |
16| SEV3 | Minor feature broken, workaround exists | < 4 hours | Export button broken, slow dashboard |
17| SEV4 | Cosmetic or low-impact issue | Next business day | Typo in UI, minor styling bug |
18
19## Incident Workflow
20
21### 1. Detect & Triage (first 5 minutes)
22
23- Acknowledge the incident — "I'm looking into this"
24- Determine severity level
25- Check monitoring dashboards (error rates, latency, status page)
26- Check recent deployments: `git log --oneline -10` — was anything deployed recently?
27
28### 2. Mitigate (next 15-30 minutes)
29
30**The goal is to stop the bleeding, not find the root cause.**
31
32Quick mitigations:
33- **Rollback**: `git revert <commit> && deploy` — fastest option if a deploy caused it
34- **Feature flag**: Disable the broken feature
35- **Scale up**: Add more instances if it's a capacity issue
36- **Failover**: Switch to backup/secondary if primary is down
37- **Block traffic**: Rate-limit or block specific abusive traffic
38
39### 3. Communicate
40
41**Internal:**
42- Open an incident channel (`#incident-2026-04-10`)
43- Post status updates every 15-30 minutes
44- Assign roles: Incident Commander, Communicator, Engineers
45
46**External:**
47- Update status page
48- Send email/notification to affected users if the outage is extended
49- Be honest: "We're experiencing issues with X. We've identified the cause and are working on a fix."
50
51### 4. Resolve
52
53- Deploy the fix
54- Verify the fix works in production (check metrics, not just absence of errors)
55- Close the incident channel with a summary
56
57### 5. Postmortem (within 48 hours)
58
59Write a blameless postmortem:
60
61```markdown
62# Incident: Payments failing for Stripe webhook
63**Date:** 2026-04-10
64**Duration:** 45 minutes (14:30 — 15:15 UTC)
65**Severity:** SEV2
66**Impact:** ~200 users unable to complete purchases
67
68## Timeline
69- 14:30 — Alert fires: payment success rate drops to 20%
70- 14:35 — On-call engineer acknowledges, begins investigation
71- 14:40 — Identified: Stripe webhook endpoint returning 500
72- 14:45 — Root cause: migration added NOT NULL column without default
73- 14:50 — Fix deployed: added default value to migration
74- 15:00 — Payment success rate recovering
75- 15:15 — Metrics back to normal, incident closed
76
77## Root Cause
78Database migration #47 added a `currency` column with NOT NULL
79but no DEFAULT value. Existing rows were fine (backfilled), but
80new webhook events failed because the insert didn't include `currency`.
81
82## What Went Well
83- Alert fired within 5 minutes of the issue starting
84- Rollback was considered but the fix was faster
85
86## What Went Wrong
87- Migration wasn't tested with live webhook payloads
88- No staging test for the webhook flow
89
90## Action Items
91- [ ] Add webhook integration test to CI (@alice, due 2026-04-17)
92- [ ] Require DEFAULT for all new NOT NULL columns in migration review (@bob)
93- [ ] Add runbook for payment failures (@charlie, due 2026-04-14)
94```
95
96## Tips
97
98- Rollback first, investigate later — speed matters more than elegance
99- The most recent deploy is the most likely cause
100- Don't assign blame in postmortems — focus on process improvements
101- Maintain a runbook for common failure modes
102- Practice incident response with game days before real incidents happen