Tag: runbooks

  • Designing an Incident Response Runbook That Actually Gets Used Under Pressure

    Designing an Incident Response Runbook That Actually Gets Used Under Pressure

    Most incident response docs die the same death. Someone writes them once, during a calm retrospective, optimizing for completeness instead of usability. Six months later it’s 3 a.m., PagerDuty is screaming, there’s a VP in the Slack channel, and nobody opens the doc, because reading five paragraphs of prose to figure out what to do first is cognitively impossible under stress. I’ve watched competent engineers abandon a 40-page “IR plan” in under 90 seconds and just start guessing. The plan wasn’t wrong. It was unusable.

    A runbook that documents your incident process and a runbook that gets followed when your hands are shaking are two different documents.

    Why prose-heavy runbooks fail

    Decision flow diagram for a credential-leak incident response runbook

    Under acute stress, working memory collapses. You lose the ability to parse nested conditionals, skim for relevance, or hold context across paragraphs. That’s neurology, not a discipline problem. A document that reads like a design doc (“In the event that database replication lag exceeds acceptable thresholds, engineers should first consider…”) demands exactly the kind of sustained reading comprehension that incident stress destroys.

    I keep seeing the same three failure modes.

    No entry point. The doc assumes you already know which section applies. There’s no triage step that gets you from “something’s wrong” to “page 14” in under 10 seconds.

    Decisions buried in narrative. The actual decision (“page the DBA if X, roll back if Y”) sits in sentence four of paragraph three, surrounded by justification nobody needs mid-incident.

    Root-cause bias. The doc walks you toward understanding why before it tells you how to stop the bleeding. In a live incident, causality is a luxury. Containment is not.

    Better prose won’t fix this. Build runbooks as flowcharts and checklists first, and demote prose to footnotes and postmortem material.

    What a usable structure looks like

    Every runbook entry should be scannable top to bottom in the order a responder actually needs it, not the order that’s intellectually tidy.

    1. Trigger conditions: the literal alert names, dashboard thresholds, or symptom descriptions that mean “this runbook applies.” Not “if the database seems slow,” the actual PromQL alert name or Datadog monitor ID. Ambiguity here is the single biggest cause of wasted minutes.

    2. Severity tiers, defined by observable impact, not by vibes:

    SEV1: customer-facing outage or data integrity risk, all hands, exec notified
    SEV2: degraded service, workaround exists, on-call + lead notified
    SEV3: internal-only or single-component failure, on-call handles solo

    Tie the tier directly to escalation and comms obligations below. Don’t make the responder infer what SEV2 implies.

    3. First-5-minutes checklist: literal checkboxes, imperative verbs, zero interpretation required. This is the part people actually execute, so it has to survive being read by someone operating at 60% cognitive capacity.

    4. Escalation path: who, in what order, by what channel, with what SLA for response. Include a fallback if the primary doesn’t ack in N minutes. Name the PagerDuty schedule, the phone number, the backup. “Escalate” can’t be a vague gesture.

    5. Communication templates: pre-written, fill-in-the-blank status updates for internal Slack, status page, and customer-facing comms if applicable. Nobody should be composing prose for stakeholders while also fighting the fire.

    6. Containment and rollback steps, before root-cause investigation. This is the part teams get backwards most often. The runbook should assume the goal is “stop customer impact,” not “understand the bug.” Rollback, failover, feature-flag kill, traffic shedding, whatever gets you back to a known-good state comes before anyone opens a debugger or starts reading logs line by line. Root cause is a postmortem activity, not an incident-response one, unless root cause is the fix (killing a single bad host, say).

    Everything else, architecture diagrams, why the system is built this way, historical context, belongs in a linked reference doc, not the runbook itself. If it’s not actionable in the first 15 minutes, it doesn’t belong above the fold.

    Concrete example: suspected credential leak

    Take a credential or API key suspected to be exposed (committed to a public repo, leaked in a log, found in a pastebin).

    RUNBOOK: Suspected Credential Leak
    
    TRIGGER CONDITIONS
    - GitHub secret-scanning alert fired
    - Credential found in public repo, log aggregator, or third-party report
    - Any employee reports pasting a secret into a public channel/tool
    
    SEVERITY
    SEV1 if: production DB creds, cloud provider root/admin keys, payment
             processor keys
    SEV2 if: scoped service credentials, internal-only API keys
    SEV3 if: already-rotated or non-functional/test credentials
    
    FIRST 5 MINUTES (do these in order, do not skip to investigation)
    [ ] 1. Identify exact credential(s) exposed (name, system, scope)
    [ ] 2. Revoke/rotate the credential at the source NOW
           - AWS: `aws iam update-access-key --access-key-id <id> --status Inactive`
           - DB: rotate password, update secrets manager, do not wait for
             app redeploy to confirm, deactivate first
    [ ] 3. Check access/audit logs for USE of the credential since exposure
           timestamp (not since discovery timestamp)
    [ ] 4. If evidence of use: treat as active compromise, escalate to SEV1,
           page security on-call immediately
    [ ] 5. Post initial status in #incidents using template below
    
    ESCALATION
    - Page: Security on-call (PagerDuty "sec-oncall"), ack SLA 5 min
    - If no ack in 5 min: page Security team lead directly (see contacts doc)
    - SEV1: notify Eng Director + CISO via phone, not Slack
    
    COMMUNICATION TEMPLATE (#incidents channel)
        INCIDENT: Credential exposure - [system/service]
        Severity: [SEV1/2/3]
        Status: [Contained / Investigating / Monitoring]
        Credential rotated: [Y/N, timestamp]
        Evidence of misuse: [Y/N, details]
        Next update: [time]
        IC: [name]
    
    CONTAINMENT (before root-cause)
    [ ] Credential rotated/revoked (confirmed, not assumed)
    [ ] Dependent services redeployed with new credential and confirmed healthy
    [ ] If public repo: force-push history rewrite or purge via GitHub support;
        assume cached copies exist regardless (CDN/scraper mirrors), rotation
        is the real fix, not deletion
    [ ] If evidence of use: lock down affected resource (IP allowlist, disable
        key entirely) even if it breaks a dependent system, customer data
        risk outweighs availability here
    
    ROOT CAUSE (postmortem, after containment confirmed)
    - How did the credential reach a public/exposed location?
    - Was secret-scanning in place on this repo/pipeline? If not, why not?
    - Time from exposure to detection, and detection to rotation

    The excerpt leaves things out on purpose. It doesn’t explain OAuth, doesn’t lecture on secrets-management philosophy, doesn’t walk through how secret scanners work under the hood. All of that lives elsewhere. This section has one job: get a half-asleep on-call engineer from “alert fired” to “credential is dead and we know if it was misused” in under ten minutes, with zero ambiguity about who to call next.

    Keeping it alive

    A runbook this terse goes stale fast if nobody exercises it. I’ve seen teams write a beautiful one and then watch it rot for two years because it never got touched. Two practices help.

    Run it in tabletop exercises quarterly, with someone unfamiliar with the specific system reading it cold. If they get stuck or have to ask what a step means, that’s a bug in the doc, not the reader.

    Update it in the postmortem of every real incident it was used for. If a step was ambiguous or missing during the actual event, fix it that week. Runbooks nobody touches after a real use are already decaying, whether anyone notices or not.

    I’d take an unimpressive document that a tired, stressed, possibly junior engineer can execute correctly without me in the room. Write for that person, not for the auditor who’ll read it later.