lenec ru

← все посты

Постмортем без блейма: шаблон, который работает

19K

Сервис вышел из строя в три ночи. Хорошие команды разбираются, что случилось, и через пару дней пишут постмортем — документ, который объясняет инцидент и закрывает выводы. Плохие команды ищут виноватого, и постмортем превращается в обвинительный акт «вот разработчик X выкатил баг». Через полгода в команде боятся писать постмортемы, потому что попадание под раздачу гарантировано.

Постмортем без обвинений — это формат, который пришёл из Google SRE и распространился в индустрии. Цель — выяснить причины и предотвратить повторение, а не наказать. Звучит просто, но в реальности команды всё равно скатываются в blame, потому что не знают, как структурировать документ, чтобы он не оказался обвинительным даже непреднамеренно. Разберу шаблон постмортема, что в нём важно, и какие фразы превращают «без обвинений» в обратное.

Чем blameless отличается от обычного

Blameless постмортем не означает, что нельзя называть имена, или нельзя говорить «здесь было сделано не так». Это означает, что фокус — на системе, а не на людях.

Сравним:

Было (с обвинением):

Алексей выкатил миграцию без тестирования на staging, что привело к падению базы. Ему следовало быть внимательнее.

Стало (без обвинений):

Миграция была применена без прогона на staging. У команды не было автоматической проверки, требующей staging-теста перед production-релизом. Добавим эту проверку в pipeline.

В первом варианте — конкретный человек и оценка его действий. Во втором — описание процесса, его пробела, и действие для устранения. Имя Алексея может быть в timeline, но не в выводе «кто виноват».

Это не про мягкость к ошибкам. Это про то, что человеческие ошибки — это симптом, а не причина. Если процесс позволяет одному человеку уронить прод нажатием кнопки, дело не в человеке.

Шаблон постмортема

Структура, которая работает:

# Postmortem: <short incident description>

## Summary

One paragraph: what happened, when, impact, how it was resolved.

## Impact

Quantified user/business impact.
- N users affected.
- Service unavailable for X minutes.
- Revenue impact: estimated $Y.

## Timeline

Detailed chronology with timestamps in UTC.

- 14:32 — Deploy of v2.4.1 starts.
- 14:35 — First error in monitoring.
- 14:38 — On-call paged.
- 14:45 — Investigation starts.
- 15:02 — Rollback initiated.
- 15:08 — Service restored.

## Root cause

Technical explanation of what went wrong, in the system.
Not "who did what".

## Detection

How was the incident detected?
- Detected at 14:35 by error rate alert.
- Or: detected at 14:50 after user complaints in support.

What was the gap between failure and detection?

## Response

What was done during the incident.
- Initial triage steps.
- Decision to rollback (and why, why not).
- Communication during the incident.

## Resolution

What action restored service.

## Action items

Concrete tasks with owners and deadlines.
- [Owner] Add staging gate to deploy pipeline. Due: YYYY-MM-DD.
- [Owner] Improve alert sensitivity for db latency. Due: YYYY-MM-DD.

## Lessons learned

What we know now that we didn't before.
What surprised us.

## Supporting data

Links to dashboards, logs, traces, related tickets.

Десять секций, каждая на конкретный вопрос. Постмортем без timeline или action items — это не постмортем, это сообщение в чате.

Что положить в Summary

Summary читают все. Менеджер, открывший постмортем, должен из неё понять масштаб и суть. Один абзац, без деталей.

Шаблон:

15 марта 2026, с 14:35 до 15:08 UTC, сервис нотификаций был недоступен для всех пользователей из-за ошибки в миграции БД. Затронуто примерно 12000 активных сессий. Сервис восстановлен после rollback миграции и пересборки read-replica.

Конкретные числа, даты, длительность. Без эмоций и без «к сожалению, мы столкнулись с непредвиденной ситуацией».

Timeline: что включать

Timeline — это голые факты. Что происходило в системе и что делала команда. Без интерпретаций.

Хорошие записи:

  • 14:32 — Deploy of v2.4.1 starts.
  • 14:35 — Error rate jumps from 0.1% to 8%.
  • 14:38 — PagerDuty alerts on-call.
  • 14:42 — On-call begins investigation.
  • 15:02 — Rollback initiated.

Плохие записи:

  • 14:32 — Кто-то решил выкатить миграцию (это интерпретация).
  • 14:38 — Сработал алерт, к сожалению, никто не отреагировал быстро (оценка).
  • 14:50 — Поняли, что всё плохо (эмоция).

Имя on-call можно упомянуть в формате «on-call (name)» или просто «on-call» — для timeline это не важно. Важно, какое действие было сделано в какое время.

Root cause: про систему

Самая частая ошибка — root cause описывается как действие человека.

Было:

Root cause: Разработчик добавил миграцию, которая блокирует таблицу users на 5 минут.

Стало:

Root cause: Миграция, добавленная в этой версии, выполняла ALTER TABLE с блокировкой на чтение. Pipeline не проверяет блокирующие миграции, и они проходят на production без специального плана.

Описание системы и пробела в процессах. Действия конкретного человека — в timeline, как факт. В root cause — что в системе позволило этому действию обрушить сервис.

Удобный приём — «5 whys». Спрашиваем «почему» пять раз подряд:

  1. Почему сервис упал? Потому что миграция блокировала таблицу.
  2. Почему миграция блокировала? Она использовала ALTER TABLE без CONCURRENTLY.
  3. Почему её пропустили в прод? В CI нет проверки на блокирующие миграции.
  4. Почему нет проверки? Команда раньше не сталкивалась с проблемой.
  5. Почему не сталкивалась? База была меньше, блокировки незаметны.

На пятом «почему» обычно становится понятно, какой системный пробел нужно закрыть.

Action items: конкретные задачи

«Action items: будем внимательнее в будущем» — это не action items.

Хорошие action items:

  • Добавить linter в CI, который ловит ALTER TABLE без CONCURRENTLY. Owner: @platform-team. Due: 2026-04-01.
  • Создать runbook «миграции на больших таблицах». Owner: @data-platform. Due: 2026-04-15.
  • Снизить порог алерта error_rate с 5% до 2% для notification-service. Owner: @sre. Due: 2026-03-25.

У каждого — конкретное действие, владелец, дедлайн. Без owner — задача не сделается. Без deadline — будет висеть полгода.

Action items должны быть реалистичными по объёму. «Переписать всю систему миграций» — это не action item, это эпик. В постмортеме — что можно сделать в обозримый срок.

Lessons learned: то, что было неожиданно

Эта секция часто заполняется как «было плохо, в следующий раз сделаем лучше». Это пустой текст.

Что должно быть:

  • Знания, которых не было до инцидента: «Не знали, что ALTER TABLE без CONCURRENTLY блокирует чтение, не только запись».
  • Ошибочные предположения: «Считали, что 5 минут блокировки — приемлемо, по факту это 5 минут полной недоступности».
  • Обратная сторона: «Mitigation сработал быстрее, чем ожидали — rollback занял 6 минут вместо предполагаемых 15».

Эта секция — о росте знаний команды, не о том, что «надо быть внимательнее».

Тон формулировок

Маленькие лингвистические детали меняют постмортем из blameless в blame:

Заменяй:

  • «Алексей не проверил миграцию на staging» → «Миграция не была проверена на staging».
  • «Кто-то поменял конфиг» → «Конфиг был изменён в коммите abc123».
  • «Команда не отреагировала вовремя» → «Реакция on-call заняла 8 минут после алерта».
  • «Дежурный должен был это заметить» → «Алерт сработал на metric X, но не отразился на dashboard, к которому привязан on-call».

Пассивный залог в постмортеме — это нормально, потому что фокус на действии, не на действующем лице. Это та редкая ситуация, когда стилистика «активного залога» уступает blameless-цели.

Кто пишет

Лучше всего — тот, кто был в инциденте. Обычно on-call, который дежурил. Но это не должен быть тот, кто «выкатил неудачный коммит» — он будет либо защищаться, либо чрезмерно самобичеваться.

Идеальная схема: on-call пишет черновик в течение 24-48 часов после инцидента (пока память свежая), команда (включая того, кто причастен к ошибке) проводит ревью и дополняет, инженер-менеджер или SRE финализируют.

Время сильно влияет: постмортем, написанный через две недели, теряет детали и точность. Через пару дней — самое то.

Постмортем-ревью

Документ написан. Дальше — ревью командой. Это не для красоты, а для:

  • Поделиться знанием с теми, кто не был в инциденте.
  • Уточнить детали timeline — кто-то мог помнить лучше.
  • Договориться об action items: они реальные, или это wishlist?
  • Проверить, не скатился ли документ в blame.

Ревью обычно идёт в формате 30-минутной встречи или асинхронно через комментарии в документе. Главный вопрос на ревью: «если мы запустим этот процесс снова, могло ли это случиться опять?». Если ответ «да» — action items недостаточны.

Где живут постмортемы

В репозитории документации команды или в отдельной системе. Главное — доступно для всей команды и инженерной организации.

postmortems/
├── 2026-03-15-notifications-down.md
├── 2026-04-02-deploy-pipeline-stuck.md
└── ...

Имя файла — дата и краткое описание. Через год команда листает «что у нас было в марте» и находит. Без даты — невозможно искать.

Confluence-страницы как место для постмортемов работают хуже: устаревают, теряются, не индексируются. Markdown в репозитории живёт долго.

Чек-лист хорошего постмортема

  • Summary в один абзац с ключевыми числами.
  • Quantified impact: пользователи, время, бизнес.
  • Timeline в UTC, голые факты, без интерпретаций.
  • Root cause описывает системный пробел, не действие человека.
  • Detection и Response — как быстро сработала реакция.
  • Action items с owner и deadline.
  • Lessons learned — реальные знания, не «будем внимательнее».
  • Тон формулировок безличный или о системе, не о людях.
  • Документ написан в течение 48 часов после инцидента.
  • Прошёл ревью командой.

Постмортем без обвинений — это инструмент команды, а не отчёт менеджменту. Если в команде он становится привычкой, через год накапливается база знаний, в которой видны паттерны: «мы три раза падали из-за миграций, нужен системный фикс». Если постмортемы превращаются в blame, через год их перестают писать совсем — потому что попадание под раздачу не стоит того.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.