Preface#
You've built everything right. SLOs defined. Observability in place. Safe deployments. Capacity planned.
And yet, at 3 AM on a Tuesday, your phone still rings.
3:17 AM. Your phone buzzes.
PAGERDUTY: [CRITICAL] Payment service - Error rate 45%
Your heart rate spikes. You're awake now.
What happens next determines
- How long customers are affected
- How much revenue is lost
- Whether this becomes a headline
- Whether you learn and improve
- Whether it happens again
Bad incident response
- Panic
- Blame
- Multiple people changing things at once
- No communication
- "Fixed" but nobody knows how
- Happens again in a month
Good incident response
- Calm, structured approach
- Clear roles and responsibilities
- Coordinated troubleshooting
- Stakeholders informed
- Root cause identified
- Systemic fix implemented
- Lessons shared widely
The difference is not luck. It's preparation.
Today, we learn to handle the inevitable failures — and more importantly, to learn from them.
At 3:17 AM the payment service's error rate hits 45%, and preparation, not luck, decides whether the response is calm and structured or panicked: 5% errors against a 1% SLO is an incident, and severity from SEV1 (over $10K an hour, all hands) to SEV4 sets the response. An incident runs detect, triage, respond, mitigate, resolve and learn (SEV1 targets: detect within 5 minutes, engage within 15, mitigate within an hour), with an incident commander who coordinates, a technical lead, a communications lead, mitigation before root cause, updates every 15 minutes and a status page within 10. Blameless postmortems use five whys (down to missing N+1 detection) and owned action items within 48 hours, and on-call stays sustainable with at least 8 people and fewer than 5 pages a shift.
Part I: Foundations#
Chapter 1: Incident Response Fundamentals#
1.1 What Is an Incident?#
An incident is an unplanned disruption or degradation of service that requires an immediate response.
NOT every alert is an incident
| Alert | Classification |
|---|---|
| "CPU usage at 75%" | Monitoring, not incident |
| "Error rate 0.5% (SLO: 1%)" | Warning, not incident |
| "Error rate 5% (SLO: 1%)" | INCIDENT |
Severity levels
| Level | Description | Revenue impact | Response | Communication | Example |
|---|---|---|---|---|---|
| SEV 1 - CRITICAL | Complete service outage affecting all users | > $10,000/hour | Immediate, all-hands | Exec notification, status page | Checkout completely down |
| SEV 2 - HIGH | Major feature degraded or subset of users affected | $1,000-$10,000/hour | Immediate, on-call + backup | Internal stakeholders | Payment processing slow (2x latency) |
| SEV 3 - MEDIUM | Minor feature degraded, workaround exists | < $1,000/hour | Business hours OK | Team notification | Order history page slow |
| SEV 4 - LOW | Minor issue, no user impact | None | Normal ticket queue | None required | Internal tool slightly degraded |
Trade-off: fix it properly, or stop the bleeding?
It's 3:17 AM and the payment service is failing 45% of requests. A deploy went out an hour ago, and one engineer is sure they can find and patch the real bug in about 40 minutes. Do you let them, or roll back now without knowing the cause?
One strong answer
- Roll back now. The first job is to stop user impact. Every minute spent on the root cause is another minute of failed payments.
- Mitigation doesn't have to be the real fix. Impact stops (time to mitigate); the full fix can come later (time to resolve), and that's fine.
- The root cause still gets found, just after users are safe, and the postmortem makes sure it doesn't happen again.
1.2 Incident Lifecycle#
%% title: Incident lifecycle %% steps: %% D: Detection: how we know something is wrong %% D --> T: Triage: assess severity and assign people %% T --> R: Response: coordinate the fix %% R --> M: Mitigation: stop the bleeding first %% M --> RS: Resolution: confirm the service is restored %% RS --> P: Post-incident: learn and improve flowchart TD D["1. DETECTION<br/>How we know something is wrong"]:::bad T["2. TRIAGE<br/>Assess severity and assign resources"]:::service R["3. RESPONSE<br/>Coordinate the fix"]:::core M["4. MITIGATION<br/>Stop the bleeding"]:::hot RS["5. RESOLUTION<br/>Confirm service is restored"]:::ok P["6. POST-INCIDENT<br/>Learn and improve"]:::service D --> T --> R --> M --> RS --> P
- Detection: how we know something is wrong
- Triage: assess severity and assign people
- Response: coordinate the fix
- Mitigation: stop the bleeding first
- Resolution: confirm the service is restored
- Post-incident: learn and improve
- Detection: Automated alerts (preferred) · Customer reports · Internal reports
- Triage: What is the impact? · Who is affected? · What's the severity level? · Who needs to be involved?
- Response: Assign roles (IC, Comms, Technical) · Diagnose the issue · Implement mitigation · Communicate status
- Mitigation: Restore service (rollback, failover, etc.) · May not be a complete fix · Priority: users over root cause
- Resolution: Verify metrics are normal · Confirm no ongoing impact · Stand down responders
- Post-incident: Write postmortem · Identify root cause · Create action items · Share learnings
| Metric | Question | Target |
|---|---|---|
| TTD (Time to Detect) | How long until we knew? | < 5 minutes for SEV1 |
| TTE (Time to Engage) | How long until someone started working? | < 15 minutes for SEV1 |
| TTM (Time to Mitigate) | How long until impact stopped? | < 1 hour for SEV1 |
| TTR (Time to Resolve) | How long until fully fixed? | May be longer than mitigation (that's OK) |
v2.3.1 deploys at 14:30 and errors climb from 14:35. The alert fires at 14:42 (time to detect: 7 min), on-call engages at 14:45 (3 min), a rollback started at 14:55 mitigates by 15:00 (18 min from detection), and the service is confirmed restored at 15:15: 40 minutes in all.
Design it: who does what in a SEV1?
Five engineers jump into the incident channel, all eager to help. The status page hasn't been updated and two of them are already changing config. How would you organise these five people?
One strong answer
- One Incident Commander coordinates, makes or delegates decisions, and knows who is doing what. They don't debug.
- One Technical Lead diagnoses and proposes mitigations; only changes they've agreed run, one at a time and announced first.
- One Communications Lead updates the status page and stakeholders. The rest act as experts the Technical Lead calls on, not as five parallel fixers.
1.3 Incident Roles#
Incident Commander (IC)
The single point of coordination.
Responsibilities:
- Declare incident severity
- Coordinate response efforts
- Make decisions (or delegate clearly)
- Ensure communication is happening
- Know who's doing what
- Decide when incident is resolved
NOT responsible for:
- Debugging the issue themselves
- Making all technical decisions
Technical Lead
Leads the technical investigation.
Responsibilities:
- Diagnose the root cause
- Propose mitigation options
- Implement or delegate fixes
- Keep IC informed of progress
- Verify fix is effective
Communications Lead
Manages stakeholder communication.
Responsibilities:
- Update status page
- Notify internal stakeholders
- Draft customer communications
- Keep timeline of events
- Handle incoming questions
Subject Matter Experts (SMEs)
Brought in as needed for specific expertise.
Examples:
- Database expert for DB issues
- Payment team for payment issues
- Security team for security incidents
- Vendor contacts for third-party issues
For smaller incidents (SEV3/4): One person may fill multiple roles.
For larger incidents (SEV1/2): Clear role separation is critical.
What breaks with three people and 50 pages a week?
Your team's on-call rotation has three engineers, and the pager fires about 50 times a week, mostly for things that fix themselves. What happens to the team over the next few months, and to the one page that really matters?
One strong answer
- Each person is on call one week in three and is woken several times a night, so tiredness and burnout follow, then people leave.
- When most pages are noise, people stop trusting the pager; the real page gets slow responses or is ignored.
- The fix: a rotation of at least eight, a target of fewer than 5 pages a shift, and every alert actionable, urgent and backed by a runbook.
Chapter 2: On-Call Best Practices#
2.1 On-Call Structure#
Rotation structure
Primary on-call
- First responder for all alerts
- Available 24/7 during shift
- Response time: < 15 minutes
- Rotation: 1 week typical
Secondary on-call
- Backup if primary unavailable
- Escalation for complex issues
- Response time: < 30 minutes
- Less interrupted (only if needed)
Manager escalation
- For SEV1 incidents
- Business decisions
- Customer communication approval
Sustainable on-call
Rules for healthy on-call
- Minimum 8 people in rotation (Max 1 week in 8 weeks on-call)
- Target: fewer than 5 pages per week-long shift (Google's SRE book caps on-call at 2 incidents per 12-hour shift)
- Compensatory time off after busy shifts
- No on-call during vacation/PTO
- Handoff meetings between shifts
- Regular review of on-call load
Warning signs
- Same person on-call every week
- Consistently 5+ pages per week
- On-call during vacation
- Burnout, turnover
- People avoiding on-call duties
2.2 Alert Design for On-Call#
# incident/alerting.py
"""
Alert design for effective on-call.
Good alerts are actionable and important.
Bad alerts cause fatigue and get ignored.
"""
from dataclasses import dataclass
from typing import List, Optional
from enum import Enum
class AlertSeverity(Enum):
PAGE = "page" # Wake someone up
NOTIFY = "notify" # Slack/email during business hours
LOG = "log" # Just log it
@dataclass
class AlertDefinition:
"""A well-designed alert."""
name: str
description: str
severity: AlertSeverity
# The query/condition
condition: str
# How long condition must be true before alerting
duration: str
# Runbook for responders
runbook_url: str
# Who to notify
notify_channel: str
# Tags for filtering
tags: List[str]
# =============================================================================
# GOOD ALERT EXAMPLES
# =============================================================================
GOOD_ALERTS = [
# This alert is ACTIONABLE and IMPORTANT
AlertDefinition(
name="CheckoutErrorRateHigh",
description="Checkout error rate exceeds SLO, customers cannot complete purchases",
severity=AlertSeverity.PAGE,
condition='sum(rate(checkout_errors_total[5m])) / sum(rate(checkout_requests_total[5m])) > 0.01',
duration="5m",
runbook_url="https://wiki/runbooks/checkout-errors",
notify_channel="payments-oncall",
tags=["checkout", "revenue", "sev1"]
),
# This alert has clear user impact
AlertDefinition(
name="APILatencyP99High",
description="API p99 latency exceeds 1 second, users experiencing slow responses",
severity=AlertSeverity.PAGE,
condition='histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) > 1',
duration="10m",
runbook_url="https://wiki/runbooks/high-latency",
notify_channel="api-oncall",
tags=["latency", "user-experience"]
),
# This is a WARNING, not a page
AlertDefinition(
name="DiskSpaceLow",
description="Disk space below 20%, will need attention soon",
severity=AlertSeverity.NOTIFY,
condition='(node_filesystem_avail_bytes / node_filesystem_size_bytes) < 0.2',
duration="30m",
runbook_url="https://wiki/runbooks/disk-space",
notify_channel="infrastructure",
tags=["disk", "capacity"]
),
]
# =============================================================================
# BAD ALERT EXAMPLES (DON'T DO THIS)
# =============================================================================
BAD_ALERTS = """
❌ BAD: No runbook
AlertDefinition(
name="SomethingWrong",
runbook_url=None # Responder has no idea what to do
)
❌ BAD: Too sensitive
AlertDefinition(
condition='error_count > 0', # Any single error pages someone
duration="0m" # No buffer for transient issues
)
❌ BAD: Not actionable
AlertDefinition(
name="CPUHigh",
condition='cpu > 80%', # So what? What should they do?
# High CPU might be fine if latency is OK
)
❌ BAD: Missing context
AlertDefinition(
name="Error",
description="An error occurred" # Which error? Where? Impact?
)
❌ BAD: Pages for non-urgent issues
AlertDefinition(
name="SlowBackgroundJob",
severity=AlertSeverity.PAGE, # This can wait until morning
description="Nightly report generation is slow"
)
"""
# =============================================================================
# ALERT REVIEW CHECKLIST
# =============================================================================
ALERT_REVIEW = """
Before adding an alert, ask:
1. IS IT ACTIONABLE?
□ Is there a runbook?
□ Can the on-call actually fix it?
□ Or do they just have to wait?
2. IS IT URGENT?
□ Does it need to wake someone up?
□ Or can it wait until morning?
□ What's the actual user impact?
3. IS IT CLEAR?
□ Does the name explain the problem?
□ Does the description explain the impact?
□ Can someone unfamiliar understand it?
4. IS THE THRESHOLD RIGHT?
□ Will it fire for real problems?
□ Will it fire for non-problems? (false positives)
□ Is the duration appropriate?
5. DOES IT REDUCE NOISE?
□ Can it be consolidated with similar alerts?
□ Is it duplicating another alert?
□ Will it cause alert fatigue?
If you can't answer YES to all of these,
the alert needs more work.
"""2.3 On-Call Handoff#
The handoff between shifts is critical. Information lost in handoff causes longer incidents.
Handoff meeting agenda (15-30 minutes)
- Active issues (5 min)
- Any ongoing incidents?
- Any degraded services?
- Any issues being monitored?
- Handoff ownership of active issues
- Recent incidents (5 min)
- What happened this week?
- Any patterns or recurring issues?
- Any postmortems needed?
- Share context that might help
- Upcoming risks (5 min)
- Scheduled deployments?
- Maintenance windows?
- Expected traffic spikes?
- Anything to watch for?
- Tooling/access check (5 min)
- VPN working?
- PagerDuty configured?
- Access to dashboards?
- Runbooks accessible?
- Questions/concerns (5 min)
- Anything unclear?
- Any gaps in knowledge?
- Contact info confirmed?
Handoff document template
Week of: [DATE]
Outgoing: [NAME]
Incoming: [NAME]
## Active Issues
- [None / List of issues with status]
## Recent Incidents
- [DATE] SEV2: [Brief description] - [Status: Resolved/Monitoring]
## Upcoming Risks
- [DATE] Deployment of [service]
- [DATE] Marketing campaign expected to increase traffic 2x
## Notes
- [Anything else the incoming person should know]Incident roles and on-call
Q1/3 Mid-SEV1, the Incident Commander sees the Technical Lead is stuck on a database problem. What should the IC do?
Answer & explanation
Answer: C
The IC is the single point of coordination and is explicitly not responsible for debugging; bringing in SMEs is how they unblock the technical work. An IC who starts debugging stops coordinating, which is exactly when parallel conflicting changes and missed communication creep in.
Q2/3 Errors spike after a deploy. You roll back, impact stops, but nobody knows the actual bug yet. Which clock just stopped?
Answer & explanation
Answer: A
Mitigation means user impact has stopped, even if the fix is not complete; the lesson says TTR may be longer than TTM and that is OK. Treating the rollback as resolution skips confirming no ongoing impact and the follow-up root-cause work.
Q3/3 Which of these alerts should actually wake someone up (severity PAGE)?
Answer & explanation
Answer: D
A page must be actionable and urgent with clear user impact: customers cannot complete purchases right now. High CPU may be fine if latency is OK, low disk can wait for business hours (NOTIFY), and a slow nightly job can wait until morning.
Chapter 3: Runbooks#
3.1 Runbook Structure#
A runbook is a step-by-step guide for diagnosing and fixing an issue. It should be usable by someone at 3 AM who's never seen this issue before.
- Overview
- What is this alert?
- What is the user impact?
- How urgent is it?
- Quick assessment (< 2 minutes)
- Dashboard link
- Key metrics to check
- Is this real or false alarm?
- Common causes and fixes
- Cause 1: [Description] — how to identify · how to fix
- Cause 2: [Description] — how to identify · how to fix
- Cause 3: [Description] — how to identify · how to fix
- Escalation
- When to escalate
- Who to escalate to
- What information to provide
- Communication
- Status page update template
- Stakeholder notification list
- Post-incident
- What to document
- Follow-up actions
3.2 Example Runbook#
# Runbook: High Error Rate on Payment Service
## Overview
**Alert**: PaymentServiceErrorRateHigh
**Severity**: SEV1 (pages immediately)
**User Impact**: Customers cannot complete purchases
**Revenue Impact**: ~$10,000/hour during peak
## Quick Assessment (< 2 min)
1. **Open dashboard**: https://grafana.internal/d/payments
2. **Check error rate graph**: Is it actually elevated or a spike that resolved?
3. **Check scope**: All endpoints or specific one?
```
Query: sum by (endpoint) (rate(payment_errors_total[5m]))
```
4. **Check timeline**: When did it start? Any correlation with deployments?
If error rate is < 1% and stable, this may be a false alarm. Monitor for 5 min.
## Common Causes and Fixes
### Cause 1: Recent Deployment
**How to identify**:
- Check deployment history: https://deploy.internal/payments
- Did error rate spike after a deployment?
**How to fix**:
```bash
# Rollback to previous version
kubectl rollout undo deployment/payment-service -n production
# Verify rollback
kubectl rollout status deployment/payment-service -n production
```
**Expected result**: Error rate should drop within 2-3 minutes.
---
### Cause 2: Database Connection Pool Exhausted
**How to identify**:
- Check DB connection metrics in dashboard
- Look for "connection refused" or "timeout" in logs
```bash
kubectl logs -l app=payment-service -n production | grep -i "connection"
```
**How to fix**:
1. Scale up payment service (reduces load per pod):
```bash
kubectl scale deployment/payment-service --replicas=10 -n production
```
2. If that doesn't help, check if slow queries are holding connections:
```sql
SELECT pid, now() - pg_stat_activity.query_start AS duration, query
FROM pg_stat_activity
WHERE state = 'active' AND now() - pg_stat_activity.query_start > interval '30 seconds';
```
3. Kill long-running queries if safe:
```sql
SELECT pg_terminate_backend(pid) FROM pg_stat_activity
WHERE state = 'active'
AND now() - query_start > interval '2 minutes'
AND pid <> pg_backend_pid();
```
---
### Cause 3: Payment Provider Outage
**How to identify**:
- Check payment provider status page: https://status.stripe.com
- Check external API latency in dashboard
- Look for timeout errors in logs
**How to fix**:
1. If provider has fallback, enable it:
```bash
# Enable Adyen as fallback
kubectl set env deployment/payment-service PAYMENT_FALLBACK_ENABLED=true
```
2. If no fallback, consider enabling maintenance mode:
```bash
# Enable maintenance mode for checkout
kubectl set env deployment/api-gateway CHECKOUT_MAINTENANCE=true
```
3. Update status page with provider attribution.
---
### Cause 4: Traffic Spike Overwhelming System
**How to identify**:
- Check request rate: is it much higher than normal?
- Check if auto-scaling is running but not keeping up
**How to fix**:
1. Manually scale up:
```bash
kubectl scale deployment/payment-service --replicas=20 -n production
```
2. Enable rate limiting if needed:
```bash
kubectl set env deployment/api-gateway RATE_LIMIT_ENABLED=true
```
## Escalation
**Escalate if**:
- Issue not resolved within 15 minutes
- Root cause unclear
- Database expertise needed
- Customer communication needed
**Escalate to**:
- Primary: @payments-team-lead (Slack)
- Secondary: @platform-lead
- SEV1 escalation: Page secondary on-call
**Information to provide**:
- Current error rate and duration
- Steps already taken
- Suspected cause (if any)
- Links to relevant logs/dashboards
## Communication
**Status page update** (if > 10 min duration):
```
Title: Payment Processing Degraded
Body: Some customers may experience errors when completing purchases.
Our team is actively investigating. We will provide updates every 15 minutes.
```
**Internal notification**:
- Slack: #incidents
- Include: Error rate, start time, impact, current status
## Post-Incident
- [ ] Document timeline in incident channel
- [ ] Create postmortem if SEV1/SEV2
- [ ] Update runbook if new cause found
- [ ] Create follow-up tickets for improvementsChapter 4: Postmortems#
4.1 The Blameless Postmortem#
The goal of a postmortem: Learn and improve, not punish.
Blameless means
- People are not the root cause — Systems that allow human error are the cause
- "Why did the system allow this to happen?" — Not "Why did this person make a mistake?"
- Everyone operated with good intentions — Given what they knew at the time
- Focus on systemic fixes — Not "person X should be more careful"
Why blameless?
| If people fear blame | If people feel safe |
|---|---|
| They hide mistakes | They report issues early |
| Incidents go unreported | They share near-misses |
| Near-misses aren't shared | Root causes are found |
| Root causes stay hidden | Systems get better |
| The same incidents happen again | Incidents are prevented |
Example
- BLAME (no): "The engineer deployed without testing"
- BLAMELESS (ok): "Our deployment process allowed untested code to reach production. We need automated test gates."
The fix is not "tell engineers to test more". The fix is "make it impossible to deploy without tests".
Estimate: where did the incident's minutes go?
A deploy goes out at 14:30. Errors start at 14:35, the alert fires at 14:42, on-call acknowledges at 14:45, the rollback starts at 14:55 and errors are back to normal at 15:00. What are the time to detect, time to engage and time to mitigate? Which part of the timeline would you attack first?
One strong answer
- Detect: 14:35 → 14:42 = 7 minutes. Engage: 14:42 → 14:45 = 3 minutes. Mitigate: 14:42 → 15:00 = 18 minutes from detection.
- Users were affected for 25 minutes (14:35 → 15:00), and only the last 5 of those were the rollback itself.
- So the biggest wins are catching the bad deploy sooner (canary analysis) and linking errors to deploys faster, not a faster rollback.
4.2 Postmortem Template#
# Postmortem: [Incident Title]
## Incident Summary
| Field | Value |
|-------|-------|
| **Date** | YYYY-MM-DD |
| **Duration** | X hours Y minutes |
| **Severity** | SEV1 / SEV2 / SEV3 |
| **Authors** | [Names] |
| **Status** | Draft / In Review / Complete |
### One-Line Summary
[One sentence describing what happened and the impact]
### Impact
- **User Impact**: [What users experienced]
- **Duration**: [How long they were impacted]
- **Scope**: [How many users affected]
- **Revenue Impact**: [$X estimated]
- **Data Impact**: [Any data loss/corruption]
---
## Timeline (All times in UTC)
| Time | Event |
|------|-------|
| 14:30 | Deployment of v2.3.1 to production |
| 14:35 | Error rate begins increasing |
| 14:42 | **DETECTED**: Alert fires for high error rate |
| 14:45 | **ENGAGED**: On-call acknowledges and begins investigation |
| 14:50 | Identified correlation with recent deployment |
| 14:55 | Rollback initiated |
| 15:00 | **MITIGATED**: Error rate back to normal |
| 15:15 | **RESOLVED**: Confirmed service fully restored |
**Key Metrics**:
- Time to Detect (TTD): 7 minutes (14:35 → 14:42)
- Time to Engage (TTE): 3 minutes (14:42 → 14:45)
- Time to Mitigate (TTM): 18 minutes from detection (14:42 → 15:00); rollback started at 13 minutes
- Total Incident Duration: 40 minutes (14:35 → 15:15)
---
## Root Cause Analysis
### What Happened
[Detailed technical explanation of what went wrong]
### Why It Happened (5 Whys)
1. **Why** did the service start returning errors?
- Because database queries were timing out.
2. **Why** were queries timing out?
- Because the new code introduced N+1 queries.
3. **Why** did the new code have N+1 queries?
- Because the ORM change wasn't caught in code review.
4. **Why** wasn't it caught in code review?
- Because we don't have automated detection for N+1 queries.
5. **Why** don't we have automated detection?
- Because we haven't prioritized query analysis tooling.
**Root Cause**: Lack of automated N+1 query detection in our CI/CD pipeline.
### Contributing Factors
- Code review didn't catch the ORM change impact
- Load testing was skipped due to time pressure
- Staging environment doesn't have production-scale data
---
## What Went Well
- Alert fired quickly (7 min detection)
- On-call responded promptly
- Rollback procedure worked smoothly
- Communication to stakeholders was timely
- No data loss or corruption
## What Went Poorly
- N+1 query issue wasn't caught before production
- Took 5 minutes to link the errors to the deployment (the N+1 root cause was found later)
- There was no automated canary analysis, which would have caught this
- Staging data is not representative of production
---
## Action Items
| ID | Action | Owner | Priority | Due Date | Status |
|----|--------|-------|----------|----------|--------|
| 1 | Add N+1 query detection to CI | @alice | P1 | 2024-02-01 | TODO |
| 2 | Implement canary analysis for deployments | @bob | P1 | 2024-02-15 | TODO |
| 3 | Create production-like dataset for staging | @charlie | P2 | 2024-02-28 | TODO |
| 4 | Add ORM query patterns to code review checklist | @alice | P2 | 2024-02-01 | TODO |
| 5 | Document N+1 query patterns in eng wiki | @alice | P3 | 2024-02-07 | TODO |
---
## Lessons Learned
1. **Our deployment pipeline needs more automated checks**
- We should catch performance regressions before production
2. **Rollback speed is critical**
- 5-minute rollback time significantly reduced impact
3. **Staging != Production**
- Data scale differences hide performance issues
---
## Appendix
### Relevant Logs
```
[Link to log search for incident timeframe]
```
### Dashboards
- [Link to metrics dashboard during incident]
### Related Incidents
- [Link to similar past incidents if any]| Value | |
|---|---|
| Revenue at risk at peak (from the runbook, assumed steady) | ~$10,000/hour ≈ $167 a minute |
| Errors start → alert fires (time to detect) | 7 min → ~$1,200 |
| Alert → on-call engaged (time to engage) | 3 min → ~$500 |
| Engaged → rollback started (linking errors to the deploy) | 10 min → ~$1,700 |
| Rollback → errors back to normal | 5 min → ~$800 |
| Total user impact, 14:35 → 15:00 | 25 min → ~$4,200 |
| Same incident with no alert, noticed from customer reports at 15:05 (assumed) | 48 min → ~$8,000 |
Conclusion: 20 of the 25 minutes passed before the rollback even started, and the rollback itself took only 5 minutes (~$800). Good alerting already halved the cost; the action items go after the rest by catching the bad deploy before it reaches everyone (canary analysis, N+1 checks in CI).
4.3 The Five Whys Technique#
A technique for finding root causes by asking "why" repeatedly.
Example 1: Website outage
Problem: Website went down
- Why did the website go down? → The web server crashed.
- Why did the web server crash? → It ran out of memory.
- Why did it run out of memory? → A memory leak accumulated over time.
- Why wasn't the memory leak caught? → We don't have memory profiling in our test suite.
- Why don't we have memory profiling? → We never prioritized performance testing tooling.
ROOT CAUSE: Lack of performance testing tooling FIX: Add memory profiling to CI/CD pipeline
Example 2: Data corruption
Problem: Customer orders showed wrong prices
- Why were prices wrong? → The price update script had a bug.
- Why did the buggy script run in production? → It wasn't tested with production-like data.
- Why wasn't it tested with production-like data? → Our staging database is much smaller.
- Why is staging database smaller? → We don't have a process to sync production data.
- Why don't we sync production data? → Privacy concerns haven't been addressed.
ROOT CAUSE: No process for anonymizing and syncing prod data FIX: Create data anonymization pipeline for staging
Tips for five whys
- Don't stop at human error — "Engineer made a mistake" is never the final answer
- Look for systemic issues — "Process allowed X to happen" is better than "person did X"
- It's OK if it's not exactly 5 — Sometimes it's 3, sometimes it's 7
- There may be multiple root causes — Branch the analysis if needed
- End when you reach something you can fix — And something that will prevent recurrence
Runbooks and postmortems
Q1/3 The payment runbook's quick assessment shows the error rate jumped two minutes after a deployment. What is the first fix to apply?
Answer & explanation
Answer: B
The runbook maps "error spike right after a deploy" to Cause 1, whose fix is a rollback, expected to clear errors within 2-3 minutes. Scaling or killing queries targets connection-pool exhaustion, a different cause; maintenance mode is a last resort when no fix or fallback exists.
Q2/3 A Five Whys chain ends at "the engineer forgot to run the load test." What should the team do next?
Answer & explanation
Answer: D
Human error is never the final answer: the question is why the system allowed it, ending at something fixable that prevents recurrence (e.g. an automated gate). "Be more careful" fixes nothing systemic, and the lesson notes the chain can be 3 or 7 steps, not strictly five.
Q3/3 Postmortem timeline: deploy at 14:30, errors begin 14:35, alert fires 14:42, on-call acknowledges 14:45, rollback starts 14:55. What is the Time to Detect?
Answer & explanation
Answer: A
TTD runs from when the issue actually started (errors at 14:35) to detection (alert at 14:42). 12 minutes wrongly starts at the deploy; 3 minutes is Time to Engage and 13 minutes is detection to rollback start.
Part II: Implementation#
Chapter 5: Incident Management System#
5.1 Incident Workflow Implementation#
# incident/workflow.py
"""
Incident management workflow system.
Coordinates incident response from detection to resolution.
"""
from dataclasses import dataclass, field
from typing import List, Optional, Dict
from datetime import datetime, timezone
from enum import Enum
import uuid
class IncidentStatus(Enum):
DETECTED = "detected"
TRIAGING = "triaging"
INVESTIGATING = "investigating"
MITIGATING = "mitigating"
MITIGATED = "mitigated" # User impact stopped; the full fix may still be pending
RESOLVED = "resolved"
CLOSED = "closed"
class IncidentSeverity(Enum):
SEV1 = 1 # Critical - all hands
SEV2 = 2 # High - immediate attention
SEV3 = 3 # Medium - business hours
SEV4 = 4 # Low - normal queue
@dataclass
class IncidentRole:
"""A role assignment in an incident."""
role: str # "incident_commander", "technical_lead", "comms_lead"
user_id: str
assigned_at: datetime
@dataclass
class TimelineEvent:
"""An event in the incident timeline."""
timestamp: datetime
event_type: str # "status_change", "action", "communication", "note"
description: str
user_id: str
metadata: Dict = field(default_factory=dict)
@dataclass
class Incident:
"""An incident record."""
id: str
title: str
description: str
severity: IncidentSeverity
status: IncidentStatus
# Timing
detected_at: datetime
engaged_at: Optional[datetime] = None
mitigated_at: Optional[datetime] = None
resolved_at: Optional[datetime] = None
# People
roles: List[IncidentRole] = field(default_factory=list)
# Timeline
timeline: List[TimelineEvent] = field(default_factory=list)
# Affected services
affected_services: List[str] = field(default_factory=list)
# Links
slack_channel: Optional[str] = None
video_call_link: Optional[str] = None
postmortem_link: Optional[str] = None
# Metadata
tags: List[str] = field(default_factory=list)
customer_impact: Optional[str] = None
@property
def time_to_detect(self) -> Optional[float]:
"""Minutes from actual issue start to detection."""
# This would require knowing when issue actually started
return None
@property
def time_to_engage(self) -> Optional[float]:
"""Minutes from detection to first responder."""
if self.engaged_at:
return (self.engaged_at - self.detected_at).total_seconds() / 60
return None
@property
def time_to_mitigate(self) -> Optional[float]:
"""Minutes from detection to impact mitigation."""
if self.mitigated_at:
return (self.mitigated_at - self.detected_at).total_seconds() / 60
return None
@property
def time_to_resolve(self) -> Optional[float]:
"""Minutes from detection to full resolution."""
if self.resolved_at:
return (self.resolved_at - self.detected_at).total_seconds() / 60
return None
def _fmt_minutes(value: Optional[float]) -> str:
"""Format a duration in minutes; a milestone that never happened shows as n/a."""
return "n/a" if value is None else f"{value:.0f} minutes"
class IncidentManager:
"""
Manages incident lifecycle and coordination.
"""
def __init__(
self,
slack_client,
pagerduty_client,
status_page_client,
database
):
self.slack = slack_client
self.pagerduty = pagerduty_client
self.status_page = status_page_client
self.db = database
async def create_incident(
self,
title: str,
description: str,
severity: IncidentSeverity,
affected_services: List[str],
triggered_by: str
) -> Incident:
"""
Create a new incident and initiate response.
"""
incident = Incident(
id=str(uuid.uuid4()),
title=title,
description=description,
severity=severity,
status=IncidentStatus.DETECTED,
detected_at=datetime.now(timezone.utc),
affected_services=affected_services
)
# Add creation event
incident.timeline.append(TimelineEvent(
timestamp=datetime.now(timezone.utc),
event_type="status_change",
description=f"Incident created: {title}",
user_id=triggered_by
))
# Create incident channel
channel = await self._create_incident_channel(incident)
incident.slack_channel = channel
# Page on-call if SEV1 or SEV2
if severity in [IncidentSeverity.SEV1, IncidentSeverity.SEV2]:
await self._page_on_call(incident)
# Update status page if SEV1
if severity == IncidentSeverity.SEV1:
await self._create_status_page_incident(incident)
# Save to database
await self.db.save_incident(incident)
return incident
async def assign_role(
self,
incident_id: str,
role: str,
user_id: str,
assigned_by: str
):
"""Assign a role to a user."""
incident = await self.db.get_incident(incident_id)
# Remove existing role assignment if any
incident.roles = [r for r in incident.roles if r.role != role]
# Add new assignment
incident.roles.append(IncidentRole(
role=role,
user_id=user_id,
assigned_at=datetime.now(timezone.utc)
))
incident.timeline.append(TimelineEvent(
timestamp=datetime.now(timezone.utc),
event_type="action",
description=f"Assigned {role} to {user_id}",
user_id=assigned_by
))
await self.db.save_incident(incident)
await self._notify_channel(
incident.slack_channel,
f"🎯 {user_id} is now {role}"
)
async def update_status(
self,
incident_id: str,
new_status: IncidentStatus,
message: str,
user_id: str
):
"""Update incident status."""
incident = await self.db.get_incident(incident_id)
old_status = incident.status
incident.status = new_status
# Update timing fields
now = datetime.now(timezone.utc)
if new_status == IncidentStatus.INVESTIGATING and not incident.engaged_at:
incident.engaged_at = now
elif new_status == IncidentStatus.MITIGATED and not incident.mitigated_at:
# TTM ends when impact stops, not when mitigation work starts
incident.mitigated_at = now
elif new_status == IncidentStatus.RESOLVED and not incident.resolved_at:
incident.resolved_at = now
if not incident.mitigated_at:
incident.mitigated_at = now # Resolving also ends the impact
# Add timeline event
incident.timeline.append(TimelineEvent(
timestamp=now,
event_type="status_change",
description=f"Status: {old_status.value} → {new_status.value}. {message}",
user_id=user_id
))
await self.db.save_incident(incident)
# Notify channel
status_emoji = {
IncidentStatus.INVESTIGATING: "🔍",
IncidentStatus.MITIGATING: "🔧",
IncidentStatus.MITIGATED: "🩹",
IncidentStatus.RESOLVED: "✅",
}.get(new_status, "📋")
await self._notify_channel(
incident.slack_channel,
f"{status_emoji} Status update: {new_status.value}\n> {message}"
)
# Update status page
if incident.severity == IncidentSeverity.SEV1:
await self._update_status_page(incident, message)
async def add_note(
self,
incident_id: str,
note: str,
user_id: str
):
"""Add a note to the incident timeline."""
incident = await self.db.get_incident(incident_id)
incident.timeline.append(TimelineEvent(
timestamp=datetime.now(timezone.utc),
event_type="note",
description=note,
user_id=user_id
))
await self.db.save_incident(incident)
await self._notify_channel(
incident.slack_channel,
f"📝 Note from {user_id}: {note}"
)
async def resolve_incident(
self,
incident_id: str,
resolution_summary: str,
user_id: str
):
"""Resolve the incident."""
await self.update_status(
incident_id,
IncidentStatus.RESOLVED,
resolution_summary,
user_id
)
incident = await self.db.get_incident(incident_id)
# Generate summary
summary = f"""
🎉 Incident Resolved
**Duration**: {_fmt_minutes(incident.time_to_resolve)}
**Time to Engage**: {_fmt_minutes(incident.time_to_engage)}
**Time to Mitigate**: {_fmt_minutes(incident.time_to_mitigate)}
**Resolution**: {resolution_summary}
Next steps:
1. Schedule postmortem (within 48 hours)
2. Complete any follow-up actions
3. Update documentation if needed
"""
await self._notify_channel(incident.slack_channel, summary)
# Close status page incident
if incident.severity == IncidentSeverity.SEV1:
await self._close_status_page_incident(incident)
async def generate_postmortem_draft(
self,
incident_id: str
) -> str:
"""Generate a postmortem template from incident data."""
incident = await self.db.get_incident(incident_id)
# Format timeline
timeline_table = "| Time | Event |\n|------|-------|\n"
for event in incident.timeline:
time_str = event.timestamp.strftime("%H:%M UTC")
timeline_table += f"| {time_str} | {event.description} |\n"
template = f"""
# Postmortem: {incident.title}
## Incident Summary
| Field | Value |
|-------|-------|
| **Date** | {incident.detected_at.strftime("%Y-%m-%d")} |
| **Duration** | {_fmt_minutes(incident.time_to_resolve)} |
| **Severity** | SEV{incident.severity.value} |
| **Status** | Draft |
### One-Line Summary
[TODO: One sentence describing what happened and the impact]
### Impact
- **User Impact**: [TODO]
- **Duration**: {_fmt_minutes(incident.time_to_resolve)}
- **Scope**: [TODO]
---
## Timeline (All times in UTC)
{timeline_table}
**Key Metrics**:
- Time to Engage (TTE): {_fmt_minutes(incident.time_to_engage)}
- Time to Mitigate (TTM): {_fmt_minutes(incident.time_to_mitigate)}
- Total Incident Duration: {_fmt_minutes(incident.time_to_resolve)}
---
## Root Cause Analysis
### What Happened
[TODO: Detailed technical explanation]
### Why It Happened (5 Whys)
[TODO: Apply 5 whys technique]
---
## Action Items
| ID | Action | Owner | Priority | Due Date | Status |
|----|--------|-------|----------|----------|--------|
| 1 | [TODO] | [TODO] | P1 | [TODO] | TODO |
---
"""
return templateChapter 6: Communication During Incidents#
6.1 Internal Communication#
Incident channel structure
Create a dedicated Slack channel: #inc-YYYY-MM-DD-short-description
Example: #inc-2024-01-15-payment-errors
Channel topic (set immediately)
"SEV1: Payment errors affecting checkout IC: @alice | Tech: @bob | Comms: @charlie Status: Investigating | Started: 14:42 UTC"
Update cadence
| Severity | Cadence |
|---|---|
| SEV1 | Every 15 minutes minimum |
| SEV2 | Every 30 minutes minimum |
| SEV3 | Every hour or at major milestones |
Update format
- [14:50] Status: Investigating
- Identified correlation with deployment at 14:30
- Error rate: 5% (down from 8%)
- Working on: Rollback decision
- Next update: 15:05
- [14:57] Status: Mitigating
- Rollback initiated at 14:55
- Error rate: 2% (decreasing)
- ETA to normal: 5 minutes
- Next update: 15:12 or when resolved
- [15:15] Status: Resolved
- Rollback complete; error rate back to 0.5% (normal) since 15:00
- Duration: 40 minutes (users affected for 25)
- Postmortem to be scheduled
These updates follow the same incident as the postmortem timeline in §4.2.
6.2 External Communication#
Principles
- Be honest — Don't hide issues. Customers know anyway. Transparency builds trust.
- Be timely — First update within 10 minutes of detection. Updates every 15-30 minutes during incident.
- Be clear — Plain language, not technical jargon. Focus on impact to users.
- Be accountable — Don't blame third parties. Own the customer experience.
Status page update templates
| Stage | Template |
|---|---|
| INVESTIGATING | "We are investigating reports of [issue]. Some customers may experience [impact]. We will provide updates every 15 minutes." |
| IDENTIFIED | "We have identified the cause of [issue] and are working on a fix. [Impact] is ongoing. We expect to resolve this within [timeframe]." |
| MONITORING | "We have implemented a fix and are monitoring the results. [Impact] should be resolving. We will confirm resolution shortly." |
| RESOLVED | "This incident has been resolved. [Brief explanation]. We apologize for any inconvenience and are taking steps to prevent this from happening again." |
What NOT to say
| Don't say | Why |
|---|---|
| "A third-party provider caused this outage" | Customers don't care, you chose the provider |
| "An engineer deployed a bug" | Internal details, sounds like blame |
| "We're not sure what's happening" | Be honest but not helpless |
| Technical jargon: "Database replication lag" | Customers don't understand or care |
Incident tooling and communication
Q1/3 In the
IncidentManager.create_incidentflow, what happens when an incident is created as SEV2?Answer & explanation
Answer: C
Every incident gets a dedicated channel and SEV1/SEV2 page on-call, but only SEV1 automatically creates a status page incident. Assuming every paged incident goes public would put internal-only degradations on the status page.
Q2/3 It is a SEV1 and your next update is due, but you have learned nothing new since the last one. What do you post?
Answer & explanation
Answer: A
SEV1 updates go out at least every 15 minutes even with no progress; silence causes more panic than an honest "still investigating." Guessing a root cause risks misleading stakeholders, and going quiet is exactly the "skipping communication" mistake.
Q3/3 Your checkout outage was caused by your payment provider. Which status page message follows the lesson's principles?
Answer & explanation
Answer: B
Good external messages are plain, impact-focused, timely and accountable: you chose the provider, so you own the customer experience. Blaming a third party, using technical jargon or sounding helpless are all on the "what not to say" list.
Part III: Real-World Application#
Chapter 7: Case Studies#
7.1 How Google Does Incident Response#
From the SRE book and public talks:
Structure
- Incident Commander (IC) — Single point of decision-making
- Operations Lead (Ops) — Works directly on the problem
- Communications Lead (Comms) — Handles stakeholder updates
- Planning Lead (for long incidents) — Manages logistics, handoffs
Key practices
- Clear command structure
- IC makes final decisions
- IC doesn't debug - they coordinate
- IC can delegate decisions explicitly
- Only one IC at a time
- War rooms
- Virtual or physical gathering point
- All responders in one channel
- Reduces communication lag
- Video call for complex incidents
- Role handoffs
- Incidents can last hours or days
- Explicit handoff process
- "I'm handing IC to [name]"
- Handoff briefing: current state, next steps
- Acknowledgment required
- Postmortem culture
- Postmortem for all significant incidents
- Blameless by policy
- Action items tracked to completion
- Postmortems shared widely for learning
- Error budgets affect response
- If error budget exhausted, stricter process
- Feature freeze until reliability improved
- Incident response tied to SLO
Lesson: The structure and roles matter more than individual heroics.
7.2 How Stripe Does Incident Response#
Context: Payments = zero tolerance for errors
Illustrative: these are common practices for high-stakes systems, not a published description of Stripe's internal process.
Key practices
- On-call excellence
- Extensive on-call training
- Shadow shifts before going live
- Regular game days (practice incidents)
- On-call is a respected role
- Runbooks for everything
- Every alert has a runbook
- Runbooks tested regularly
- If no runbook, create one during incident
- Runbooks reviewed in postmortems
- Automated response
- Automatic rollback on error spike
- Automatic scaling on load
- Automatic circuit breaking
- Reduce need for human intervention
- Rapid postmortems
- Postmortem within 48 hours
- Short format for quick turnaround
- Focus on action items
- Follow-up tracked in system
- Learning from near-misses
- Report issues even if no impact
- Analyze near-misses like incidents
- Find systemic issues before they cause outages
- "Near miss → fix it before it's a miss"
Lesson: For high-stakes systems, invest heavily in prevention and preparation.
Chapter 8: Common Mistakes#
Mistake 1: Too many cooks
| Wrong | Right |
|---|---|
| Everyone jumps in to help | Clear roles assigned |
| Multiple people making changes | IC coordinates all changes |
| No coordination | One person makes changes at a time |
| Changes conflict with each other | "I'm changing X" announced before acting |
Mistake 2: Skipping communication
| Wrong | Right |
|---|---|
| Heads down fixing the problem | First status update within 10 minutes |
| Stakeholders don't know what's happening | Regular updates even if no progress |
| Customers learn about outage from Twitter | Status page updated immediately |
| Leadership surprised | Leadership briefed for SEV1 |
Mistake 3: Blame culture
| Wrong | Right |
|---|---|
| "Who deployed this?" | Focus on what, not who |
| "How could you not test this?" | "How did our process allow this?" |
| Public shaming in postmortem | Systemic fixes, not personal criticism |
| People hide mistakes | Safe to report issues |
Mistake 4: No postmortem
| Wrong | Right |
|---|---|
| Incident resolved, move on | Postmortem within 48 hours |
| No analysis of what happened | Root cause analysis |
| Same incident happens again | Action items assigned and tracked |
| No improvement | Learnings shared widely |
Mistake 5: Alert fatigue
| Wrong | Right |
|---|---|
| 50 pages per week | < 5 pages per week (target) |
| Most alerts are false positives | Every alert is actionable |
| On-call ignores alerts | False positives are fixed |
| Real issues missed | On-call trusts alerts |
Real-world practice and anti-patterns
Q1/3 During a SEV1, five engineers each try a different fix at the same time and the error rate keeps swinging. What change helps most?
Answer & explanation
Answer: D
"Too many cooks" makes changes conflict and hides which one helped; the fix is clear roles with the IC coordinating every change. Waiting for the postmortem abandons mitigation, which comes first while users are still affected.
Q2/3 A team gets about 50 pages a week, most of them false positives. What is the real danger?
Answer & explanation
Answer: C
Alert fatigue erodes trust in the pager: when most pages are noise, the real one gets ignored. The remedy is fewer, actionable alerts and fixing false positives, not a cheaper pager.
Q3/3 Following the lesson's Stripe example, why analyze a near-miss that had no customer impact?
Answer & explanation
Answer: A
"Near miss, fix it before it's a miss": near-misses reveal the same systemic gaps as outages, at zero cost to users. Using near-misses to assign responsibility would make people stop reporting them, undoing the blameless culture.
Part IV: Interview Preparation#
Chapter 9: Interview Tips#
9.1 Incident Management Discussion Framework#
When asked "How would you handle an incident?":
- Structure is key — "First, I'd establish clear roles:
- Incident Commander for coordination
- Technical Lead for debugging
- Comms Lead for stakeholders
This prevents chaos and ensures coordination."
- Mitigation over root cause — "My first priority is mitigating user impact. Can we rollback? Can we failover? Root cause investigation comes after we've stopped the bleeding."
- Communication — "I'd set up an incident channel, update stakeholders every 15 minutes, and update the status page within 10 minutes. Silence is worse than 'we're investigating.'"
- Learning — "After resolution, I'd run a blameless postmortem. The goal is to find systemic issues and prevent recurrence, not assign blame. We'd track action items to completion."
- Prevention — "The best incident is one that doesn't happen. I'd invest in monitoring, alerting, runbooks, and regular game days to prepare the team."
9.2 Key Phrases#
On response
"My first question is 'what's the user impact?' That determines severity and urgency. A backend error with no user impact is different from checkout being down."
On roles
"I believe in clear incident command. One person coordinates, others execute. Without this, you get five people all trying to fix the problem differently."
On communication
"I'd update stakeholders every 15 minutes, even if the update is 'still investigating.' Silence causes more panic than honest updates about uncertain situations."
On postmortems
"Postmortems must be blameless. If we blame individuals, people hide mistakes. The question is 'how did our system allow this?' not 'who made the error?'"
On prevention
"Every incident should produce action items that prevent recurrence. If we don't learn, we're just firefighting forever. Track action items like any other work."
On alert design
"Every page should be actionable and important. If an alert fires and the response is 'wait and see,' it shouldn't be a page. Alert fatigue causes missed real issues."
Chapter 10: Practice Problems#
Practice · problem 1
Problem 1: Payment Outage#
Scenario: You're on-call. At 2 AM, you're paged: "Payment success rate dropped from 99% to 85%."
Questions:
- What's your first 5 minutes?
- How do you communicate?
- What's your mitigation strategy?
Click for approach
-
First 5 minutes:
- Acknowledge the page
- Check dashboard for scope and timeline
- Is it all payments or specific type?
- When did it start? Correlate with deployments
- Quick assessment: real issue or false alarm?
-
Communication:
- Create incident channel: #inc-2024-01-15-payment-degradation
- Declare SEV1 (payments are critical)
- Page secondary on-call for support
- Update status page: "Investigating payment processing issues"
- Notify leadership via Slack/text
-
Mitigation:
- If recent deployment: consider rollback
- If external provider: check their status, enable fallback
- If load-related: scale up, enable rate limiting
- Priority: restore payments, investigate later
Practice · problem 2
Problem 2: Postmortem Scenario#
Scenario: An engineer deployed code that caused a 30-minute outage. Leadership wants to know "who did this."
Questions:
- How do you handle the leadership question?
- What should the postmortem focus on?
- What action items might emerge?
Click for approach
-
Handling leadership:
- Redirect from "who" to "how"
- "The individual is less important than understanding how our process allowed this to happen"
- Share that blameless culture leads to better outcomes
- Offer to present postmortem findings
-
Postmortem focus:
- Why did the code have a bug? (Testing gap?)
- Why wasn't it caught in code review? (Reviewer didn't know to look?)
- Why wasn't it caught in staging? (Data difference?)
- Why wasn't rollback faster? (Process gap?)
-
Action items:
- Add automated test for this scenario
- Update code review checklist
- Improve staging data to match production
- Reduce rollback time with automation
- Share learnings with other teams
Chapter 11: Sample Interview Dialogue#
"Walk me through how you'd handle a major production incident."
"Let me walk through the complete lifecycle.
Detection and Triage:"
- Acknowledge within 5 minutes
- Quick assessment: Is this real? What's the scope?
- Declare severity based on user impact
- For SEV1/2: Create incident channel, page backup
"For a SEV1 like 'checkout is down', I'd immediately escalate to a war room model:"
- Slack channel:
#inc-2024-01-15-checkout-down - Video call for complex coordination
- Assign roles:
- Myself as IC (or delegate if I'm debugging)
- Senior engineer as Technical Lead
- Manager or senior as Comms Lead
"How do you decide what to do first?"
"Mitigation over investigation. The priority is stopping user impact.
My decision tree:"
%% title: Mitigation decision tree
flowchart TD
Q1{"1. Was there a<br/>recent deployment?"} -->|"YES"| A1["Can we rollback safely?"]:::ok
Q1 -.->|"NO"| Q2{"2. Is a dependency down?"}
Q2 -->|"YES"| A2["Can we failover or<br/>degrade gracefully?"]:::ok
Q2 -.->|"NO"| Q3{"3. Are we overloaded?"}
Q3 -->|"YES"| A3["Can we scale up<br/>or rate limit?"]:::okI'm looking for the fastest way to restore service, even if it's not the 'correct' fix. We investigate root cause after users are happy.
"What about communication during the incident?"
"Communication is as important as the fix itself.
My communication plan:"
Status page
- First update within 10 minutes
- Template: "We are investigating issues with [X]"
- Update every 15 minutes even if no change
- Include ETA when known
Internal
- Update incident channel every 10-15 minutes
- Format: Status | What we know | What we're doing | Next update
- Escalate to leadership for SEV1
Stakeholders
- Customer support team briefed
- Sales team notified if major customers affected
- Executive summary for leadership
"After it's resolved, then what?"
"The incident isn't really over until we've learned from it.
Post-incident:"
- Within 48 hours: Blameless postmortem
- Timeline reconstruction
- 5 Whys root cause analysis
- What went well / what went poorly
- Action items assigned with owners and dates
- Action items tracked like any other work
- In sprint planning
- Regular check-ins
- Actually get done (not just documented)
- Share learnings
- Post summary in engineering channel
- Update runbooks if needed
- Consider if training needed
"Good comprehensive answer. One last question: how do you prevent incidents?"
"Prevention is the best incident response.
Prevention strategies:"
- Observability: Can't fix what you can't see
- Metrics, logs, traces all correlated
- Dashboards for key services
- Alerting: Right alerts that are actionable
- Every alert has a runbook
- Regular review of alert quality
- Testing: Catch issues before production
- Load testing for capacity
- Chaos engineering for resilience
- Canary deployments for safety
- Game days: Practice incident response
- Simulated incidents quarterly
- Test runbooks and communication
- Culture: Safe to report issues
- Blameless postmortems
- Celebrate near-miss reports
Summary#
Incident lifecycle
Detection → Triage → Response → Mitigation → Resolution → Learning
Key roles
- Incident Commander: Coordinates, doesn't debug
- Technical Lead: Diagnoses and fixes
- Communications Lead: Updates stakeholders
Priorities
- Mitigation first, investigation second
- Communication is not optional
- Restore service, then find root cause
On-call
- Sustainable rotations (≥8 people)
- Actionable alerts only
- Runbooks for every alert
- Proper handoffs between shifts
Postmortems
- Blameless (systems fail, not people)
- Within 48 hours while memory fresh
- 5 Whys for root cause
- Action items tracked to completion
Communication
- First update within 10 minutes
- Regular updates even if no progress
- Plain language, not technical jargon
- Own the customer experience
Key insight
The best incident response is built on preparation: good alerts, tested runbooks, practiced teams. Heroics are a sign of system failure.
Further Reading#
Books:
- Book
"Site Reliability Engineering" by Google (Free online)
- Book
"The Phoenix Project" by Gene Kim
- Book
"Incident Management for Operations" by Rob Schnepp
Resources:
- Docs
PagerDuty Incident Response Guide (free)
- Read
Atlassian Incident Management Handbook
- Book
Google SRE Book - Chapter on Incident Response
Tools:
- Read
PagerDuty: Alerting and on-call management
- Read
Incident.io: Incident management workflows
- Read
Statuspage: External communication
- Read
Jeli: Incident analysis and learning
Week 10 Complete!#
Production Readiness and Operational Excellence
| Day | Topic | Purpose |
|---|---|---|
| Day 1 | SLIs, SLOs, SLAs | Define what "healthy" means |
| Day 2 | Observability | See whether you're healthy |
| Day 3 | Deployment Strategies | Maintain health through change |
| Day 4 | Capacity Planning | Ensure health under load |
| Day 5 | Incident Management | Respond when health fails |
The production engineer's toolkit
- SLOs tell you what healthy means
- Observability tells you if you're healthy
- Deployment strategies keep you healthy through change
- Capacity planning keeps you healthy under load
- Incident management restores health when it fails
Together, these skills let you operate reliable systems that serve users well, even at scale, even under stress.
Next: Week 10 Capstone — Bringing together everything from Weeks 1-10 in a comprehensive system design interview simulation.
You've built the complete toolkit of a senior production engineer. The capstone will test your ability to apply all of it together.
this lesson in shorts · 3
play them all →One idea from this lesson in about a minute each, made for your phone.
Go deeper · reading trail
all reading →Real write-ups from the teams that built these systems. Each opens on the publisher's site in a new tab.
Managing Incidents
Andrew Stribblehill · 2016- What
- The SRE book chapter on running an incident.
- Key idea
- Separate roles (incident commander, operations, communications, planning), keep a live incident document and hand off command explicitly.
- Why here
- The incident response fundamentals of this lesson, with an example of an incident managed badly and then well.
The Atlassian Incident Management Handbook
- What
- Atlassian's public handbook for how its teams detect, respond to and learn from incidents.
- Key idea
- It defines what counts as an incident, sets values for each stage (know before customers do, escalate early), describes the incident manager role and ends in postmortems that assign actions.
- Why here
- A complete process to borrow for this lesson's on-call, runbook and postmortem sections.
Details of the Cloudflare outage on July 2, 2019
John Graham-Cumming · 2019- What
- Cloudflare's detailed postmortem of a global outage.
- Key idea
- A new WAF rule contained a regular expression that backtracked badly and exhausted CPUs on every HTTP-serving core worldwide; the fix list covers testing, rollout and regex engines.
- Why here
- A model postmortem for this lesson: timeline, root cause, why safeguards failed and concrete follow-ups.
New lessons by email
New lessons, side quests and case files as they ship, at most once a week. No spam, unsubscribe anytime.
Discussion · …
Public: everyone reading this lesson sees comments. Be kind and keep it on the material.


