On-Call Practices and Runbook Design Questions
Running a sustainable on-call function: rotation design, production-readiness handoffs, and authoring runbooks that let responders act quickly. Covers runbook automation, on-call culture, escalation-ready documentation, and readiness reviews before a service takes production traffic. The operational-preparedness discipline that makes incidents survivable.
You're asked to do a root-cause analysis for an incident where the telemetry is sparse. How would you reconstruct the timeline, and how would you turn the gaps you find into a prioritized plan for what to instrument next?
Sample Answer
With sparse telemetry, reconstruct the timeline from whatever correlatable evidence exists (logs, deploy history, database audit fields, even client-side error reports), be explicit about the gaps rather than papering over them with guesses, and then rank the missing instrumentation by how much it would have shortened this specific investigation, not by a generic wishlist.
Reconstructing the timeline
- Inventory every source that exists, even partial ones: application logs, load-balancer or edge logs, deployment/CI history, database
updated_ator audit-table entries, orchestrator events, and client-side error or crash reports. Sparse telemetry usually means no distributed tracing, not zero data. - Normalize and correlate by whatever join key is available. Without trace IDs, correlate by IP plus user-agent plus a tight time window, or by user ID if logs carry it. This gives an approximate rather than exact join, so timestamp uncertainty needs to be carried forward explicitly ("between 14:02 and 14:06" rather than a false-precision single timestamp).
- Use deployment and config-change history as anchor points. Even with no application telemetry, knowing exactly when a deploy or config change happened gives a hard boundary the rest of the timeline can be built around.
- Mark every inferred gap as a gap, not a guess. If there's a 20-minute window with zero data between the last known-good log line and the first symptom report, write "unknown: 20-minute gap" in the timeline rather than smoothing over it with an assumption; the gap itself is the signal for what to instrument next.
Turning gaps into a prioritized instrumentation plan
Priority order, ranked by how much each would have cut investigation time for the incident just reconstructed, not by comprehensiveness:
- Request-scoped correlation ID, propagated from the edge through every downstream service call and into logs. This directly closes the "approximate join" problem above and is almost always the highest-leverage single addition.
- Structured logging on the exact boundary where the gap occurred (if the gap was inside a background job, instrument that job's start/end/error events first, not an unrelated part of the system).
- A minimal health/heartbeat signal for anything that failed silently (a background job that stopped running with no error, a queue consumer that stalled): a periodic "I am alive and processed N items" emission would have converted a silent gap into a detectable one.
- Everything else (full distributed tracing, high-cardinality dashboards, synthetic monitoring) is real but lower priority than closing the specific gap that made this RCA (root cause analysis) hard.
Worked example
A checkout failure is reported by users around 14:15, but the last application log line referencing checkout is at 13:52, and no logs mention it again until 14:31 when a deploy rollback log appears. The 13:52 to 14:31 window is the gap. Cross-referencing the CDN access log shows checkout page loads continuing normally through 14:20 with a rising 502 rate starting at roughly 14:08, narrowed to that window because the CDN's log granularity is per-minute. The deploy history shows a deploy landed at 13:50, two minutes before the last known-good application log. That's enough to build a bounded timeline: deploy at 13:50, unknown internal state for 18 minutes, first externally visible failure around 14:08, rollback at 14:31. The instrumentation gap this surfaces is specific: the checkout service was emitting no logs at all during a partial-failure state, meaning either logging itself failed or the failure mode (hung requests, not crashed ones) doesn't hit any log statement. The top-priority fix is a heartbeat or request-timeout log on that exact code path, not a generic "add more logging everywhere" recommendation.
Trade-offs and pitfalls
The biggest pitfall is presenting a reconstructed timeline with the same confidence as one built from complete telemetry; a bounded, gap-labeled timeline is honest and still useful, a smoothed-over one invites a wrong root cause. Instrumentation has real costs (ingest volume, storage, and potential PII exposure in high-cardinality logs), so the priority list above should stop at the point where the next item's cost clearly exceeds the diagnostic value for the failure modes actually observed, rather than instrumenting everything a textbook would recommend. A related wrong turn is chasing full distributed tracing as the first fix when the actual gap was a silently-hanging job with zero heartbeat: tracing helps correlate across services, but it doesn't help if the service in question emitted nothing at all during the failure.
One of your critical alerts fires constantly but turns out to be right only a fraction of the time. How would you redesign it so it's trustworthy again, and how would you decide which alerts generally deserve to page a human first?
Sample Answer
Direct answer
A chronically low-precision page is a design bug in the alert, not a discipline problem for the responder: fix it by classifying WHY each false positive happened (transient blip versus a signal that's sustained but wrong for a different reason) and attacking each class with a targeted change, then re-measure in shadow mode before it pages anyone again. Separately, decide what generally deserves to page a human with an explicit bar: actionable (there's something to do right now), urgent (waiting meaningfully worsens the outcome), and real (it correlates with actual impact, not an internal metric that moves for benign reasons).
Structured elaboration
- Classify the false positives, don't guess. Pull a sample of historical firings and tag each one: true incident, transient blip (self-resolved with no action), or sustained-but-non-actionable (stayed unhealthy but for a known, benign reason). Compute today's precision from that sample before changing anything.
- Attack each class differently. Transient blips get a sustain/consecutive-sample requirement, since by definition they don't last. Sustained-but-wrong firings need a better underlying signal, typically moving from a cause-based check (an internal metric that spikes for reasons unrelated to customer impact) to a symptom-based one (customer-facing error rate or SLO burn).
- Re-measure before re-enabling paging. Run the changed alert in shadow/log-only mode against real traffic for at least a full weekly cycle, and compare precision and recall against the original, not just precision alone, since a change that also silently drops true positives isn't actually a win.
- Set the general page-or-not bar (actionable, urgent, real) so the next alert someone proposes gets evaluated against a fixed standard instead of being added because it seemed prudent at the time.
Worked example
Say the alert fired 100 times last month, and by the question's premise it's "right only a fraction of the time": assume 20 of those 100 firings were real incidents and 80 were false positives.
precision=TP+FPTP=20+8020=0.20Re-tagging the 80 false positives by how long they stayed unhealthy: suppose 65 of them self-resolved within a single evaluation cycle with no human action (transient blips), and 15 stayed unhealthy for several minutes but turned out to be a known, benign condition, for example a scheduled batch job that always spikes this particular metric (sustained-but-wrong).
Adding a "must stay unhealthy for 3 consecutive evaluation samples" requirement removes the 65 transient blips entirely, since none of them lasted past a single sample, while every one of the 20 true incidents is unaffected, because a real incident is sustained by nature and was never at risk of being filtered by a sustain window. Precision after this one change:
precisionnew=20+1520=3520≈0.571That's a jump from 20% to roughly 57% precision from a single, low-risk change, with the true-positive count completely untouched. The remaining 15 sustained-but-wrong firings need a second, more targeted fix, such as excluding the known benign batch-job window or moving to a symptom-based check, since a sustain window alone can't distinguish "sustained and wrong" from "sustained and real." That second change should also be shadow-tested before it's trusted to page.
Trade-offs and pitfalls
- Pitfall: a blanket sustain window can blind you to a genuinely fast, severe failure (a hard crash) that needs to page in well under the sustain threshold. The fix should be tiered: a fast, low-latency path for total-outage-class signals, and a sustain-gated path for degradation-class signals, not one delay applied to every alert on the service.
- Pitfall: re-measuring precision only in the days immediately after a change is vulnerable to regression to the mean; hold the shadow test open long enough to capture the conditions that caused the original noise (a full weekly cycle, including whatever batch job or traffic pattern triggered the sustained-but-wrong case).
- Trade-off: symptom-based alerting (SLO burn rate, customer-facing error rate) is more precise about whether something actually matters, but it's slower to point at a root cause than a cause-based internal metric. Good practice pages on the symptom and links a runbook that walks through cause-based diagnostics from there, rather than choosing one approach exclusively.
How would you communicate about an ongoing outage differently to your own engineering team versus to non-technical stakeholders or customers? What changes, and what stays the same?
Sample Answer
Direct answer
What changes between internal and external incident communication is technical depth and certainty: engineering updates include specifics, hypotheses, exact systems and logs, mitigation steps, because that audience can act on them, while external updates stick to confirmed customer-visible impact and next-update timing, because speculation shared with customers erodes trust if it turns out wrong. What stays the same is cadence discipline and honesty: both audiences get updates on a predictable schedule, and neither gets a false ETA.
Structured elaboration
| Internal (engineering) | External (customers, stakeholders) | |
|---|---|---|
| Content | Hypotheses, specific systems and logs, exact mitigation steps, owners | Confirmed customer-visible impact, current status, general next steps |
| Language | Technical shorthand is fine | Plain language, no internal system names or jargon |
| Certainty | Unconfirmed working theories can be shared, labeled as such | Only confirmed facts; no speculation presented as cause |
| Cadence | Every 15 to 30 minutes while active | Every 30 to 60 minutes, or on a material status change |
| Who approves | Incident commander, informally | Incident commander plus communications or product sign-off |
| Tone | Direct, action-oriented | Calm, factual, acknowledges impact without over-promising |
Tailoring further by audience
Enterprise customers with contractual SLAs often get a more detailed, sometimes one-to-one update from their account team in addition to the public status page, while free-tier customers rely on the public page alone. The underlying facts should be identical; only the delivery channel and level of individual attention differ. Escalation also has its own trigger: open a live bridge call and involve leadership once the outage crosses a defined severity or duration threshold, such as a sustained top-severity incident running longer than the org's own SLA commitment, not just because internal chat is busy.
What never changes regardless of audience
No blame is assigned, internally or externally, while the incident is still active; naming a cause before it's confirmed just gets walked back later. No ETA gets promised that isn't actually known; "investigating" is an honest status, a fabricated timeline is not.
Worked example
Internal update at the 15-minute mark: "checkout-service 5xx rate is elevated since 14:02 UTC, correlates with a config deploy at 13:58; on-call is rolling back now, expect confirmation in a few minutes; questions in the incident channel." External status page at the 30-minute mark, built only from the confirmed facts in that internal update: "We're investigating an issue causing checkout errors for a subset of users. Our team has identified a likely cause and is applying a fix. Next update in 30 minutes." The external version omits the specific deploy detail, since it isn't customer-actionable and would need walking back if the rollback doesn't fully resolve things, but keeps the same cadence commitment as the internal update.
Trade-offs and pitfalls
- Copy-pasting the internal technical update into the external channel either leaks unnecessary detail or reads as more alarming than the plain-language version would.
- More frequent external updates build trust but risk announcing something not yet confirmed if the situation is still moving fast; anchor the cadence to confirmed milestones, not the clock alone.
- Skipping communications or product sign-off on external messages risks a technically accurate but poorly worded update going out while everyone is heads-down on the actual fix.
What's the difference between MTTD, MTTA, and MTTR? Given a short incident timeline, how would you calculate each, and what's a common mistake people make when interpreting these numbers?
Sample Answer
MTTD is how long a problem existed before anything noticed it. MTTA is how long a human took to acknowledge the alert once it fired. MTTR is how long it took to fully resolve once someone was working it. The most common mistake is reporting a single blended average and treating it as typical, when one long outage in the set is doing all the work.
Definitions
| Metric | Starts at | Ends at | What it measures |
|---|---|---|---|
| MTTD | Failure begins | Alert fires / someone notices | How good detection is |
| MTTA | Alert fires | Human acknowledges | How well paging and routing work |
| MTTR | Acknowledgment | Service fully restored | How fast the response process fixes it, once someone owns it |
(Some teams instead measure MTTR from detection to resolve rather than ack to resolve; either is defensible, but the convention has to be fixed and stated, because mixing them across teams silently changes what the number means.)
Worked example: one incident timeline
| Event | Time |
|---|---|
| Failure begins | 14:00:00 |
| Alert fires (detection) | 14:06:00 |
| Engineer acknowledges | 14:11:00 |
| Service restored | 14:47:00 |
Worked example: averaging across three incidents, and where it goes wrong
| Incident | MTTD | MTTA | MTTR |
|---|---|---|---|
| 1 | 6 | 5 | 36 |
| 2 | 2 | 3 | 20 |
| 3 | 15 | 8 | 54 |
The mean of 36.7 minutes is being pulled up almost entirely by incident 3's 54-minute outlier: the mean sits above two of the three data points (20 and 36), with only the outlier itself larger. The median of {20, 36, 54} is 36, the middle value itself rather than a value inflated by the outlier, so it is a better single-number stand-in for the typical incident than the mean. Reporting mean MTTR alone, without the incident count or a percentile, makes a single bad incident look like the typical case.
Trade-offs and pitfalls
Comparing MTTR across teams that use different start-point conventions is comparing two different metrics wearing the same name; agree on the convention org-wide before benchmarking teams against each other. A dropping mean MTTR can hide a rising incident count: if you're resolving more small incidents faster while one rare severe incident still takes hours, the mean improves and the tail risk hasn't moved at all. Improving MTTD without improving MTTA or MTTR just means you find out about the same slow response faster; treat the three as stages of one pipeline, not independent wins to report separately.
Mid-incident during a Sev1, you discover the runbook you're following has outdated commands that don't work on the current cluster configuration. What do you do to keep the response moving, and how do you make sure the runbook gets fixed afterward?
Sample Answer
Direct answer
Keep the incident moving without trusting the stale command: switch to safe, read-only discovery to re-derive the actual current state instead of assuming the runbook's exact syntax still matches reality, get a second responder to sanity-check any ad-hoc workaround before running it, and narrate every command and its outcome in the incident channel as you go. Afterward, treat the correction as a first-class, owned follow-up: file it with the incident as evidence and require the same review the runbook normally gets, rather than merging a fix drafted under adrenaline with no second pair of eyes.
Structured elaboration
Keeping the response moving
- Don't keep retrying the stale command hoping it starts working; that burns clock on a Sev1.
- Fall back to read-only discovery to find what actually changed: list the current resource names or config instead of assuming the runbook's exact prior values, and check whether a known change (a migration, a rename, a tool upgrade) explains the mismatch.
- If a workaround command is genuinely needed, treat it like an experiment: scope it to the smallest possible blast radius (a single pod, host, or canary) if at all possible, and have a second responder review it before running anything destructive.
- Narrate in the incident channel as you go: exact command, who ran it, what happened. This live log is what makes the eventual runbook fix accurate instead of a reconstruction from memory the next morning.
Getting the runbook actually fixed afterward
- File the correction as its own owned action item, not "someone should update this."
- Attach evidence: the incident timeline entries showing what actually worked, captured live rather than recalled later.
- Route the fix through the same review the runbook would normally require. A fix drafted under incident pressure is exactly the kind of change that benefits from a second reviewer, not an exception to needing one.
- If the drift has a systemic cause, such as no process tying documentation updates to the change that invalidated it, raise that separately as its own postmortem action item, not just a one-off doc patch.
Worked example
kubectl rollout restart deployment/checkout -n prod fails with "deployment not found." The responder falls back to read-only discovery: kubectl get deploy -A | grep checkout shows the deployment now lives in namespace checkout-prod, after a namespace-per-service migration weeks earlier that never touched the runbook. The corrected command runs, the service recovers, and both the failed and working commands are logged with timestamps in the incident doc as they happen. The resulting follow-up action item reads: "update runbook RB-042's namespace reference and add a namespace-lookup step instead of a hardcoded name; owner: platform team; verified by a peer review plus a sandboxed dry-run before merge."
Trade-offs and pitfalls
- Fixing the runbook file directly, mid-incident, with no review is a common shortcut; the correct fix ships as a follow-up change through the normal review gate, informed by what was learned live.
- Treating the ad-hoc working command as tribal knowledge instead of writing it down immediately is how the same staleness reappears at the next incident; capture it in the channel the moment it works, not after the retro.
- Verifying a corrected command in a sandbox before trusting it live is safer, but a Sev1 usually doesn't have that time; this is really an argument for building runbooks with idempotent, safe-to-retry commands and dynamic lookups in the first place, so on-call isn't forced into that trade-off during the incident.
Unlock Full Question Bank
Get access to all On-Call Practices and Runbook Design interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.