Постмортемы: почему без обвинения и как довести до правок
21:04. Инцидент закрыт, SLI вернулся в норму, в канале написали «спасибо всем». Дежурный идёт спать. Через три недели то же самое повторяется в 03:40, и на этот раз чинят на восемь минут дольше, потому что человек другой.
Между этими двумя абзацами должна была стоять процедура, которая превращает одно событие в изменение системы. Она называется постмортемом, и с ней происходит одна из двух неприятностей. Либо её нет вовсе — «и так всё понятно». Либо она есть, документы аккуратно пишутся, складываются в вики, а частота инцидентов не меняется — потому что документ не является продуктом постмортема. Продуктом является выкаченное изменение.
Эта глава — про механику, а не про этикет. Мы разберём, почему требование «без обвинения» — утверждение о потоке информации, а не о доброте; почему «коренной причины» в распределённой системе обычно не существует; как считать ущерб инцидента в единицах бюджета ошибок, а не в минутах; и — главное, потому что здесь ломается почти у всех — какие механизмы доводят пункты разбора до продакшна, а какие только имитируют работу.
Предполагается, что вы уже прочли про реакцию на инцидент — роли, связь и первые действия. Постмортем начинается там, где та глава заканчивается: пользователям снова хорошо, и теперь надо понять, что случилось на самом деле.
Что такое постмортем и что не является постмортемом
Постмортем — это процедура, которая берёт на вход инцидент, а на выходе даёт список изменений системы с владельцами и сроками.
Из определения сразу выпадает несколько вещей, которые постмортемом называют, но которыми он не является.
Не отчёт наверх. Документ, оптимизированный под чтение начальством, пишется другими словами: в нём меньше подробностей о том, где команда путалась, и больше формулировок «ситуация была взята под контроль». Ровно эти подробности и являются данными.
Не поиск причины ради причины. Понимание, не превратившееся в изменение, испаряется вместе с людьми, которые уволятся через полтора года.
Не суд. Смешение разбора с оценкой людей ломает разбор — ниже мы покажем, как именно, и это не риторическая фигура.
Не пересказ таймлайна. Таймлайн — сырьё. Разбор — это то, что вы с ним делаете.
Английский термин прижился, но неудачен: «вскрытие» предполагает труп. Часть сообщества (Джон Оллспоу и проект Learning from Incidents) использует «post-incident review» или «debriefing» — разбор после инцидента. По-русски «разбор» точнее, и дальше я использую оба слова как синонимы.
Когда его писать: порог, а не привычка
Постмортем стоит денег — ниже посчитаем, около 12–14 человекочасов на средний инцидент без учёта самих правок. Значит, писать их на каждое отклонение нельзя: практика обесценится ровно тем же механизмом, каким шумные правила обесценивают алерты. Нужен явный порог, записанный до того, как случится инцидент.
Рабочий набор триггеров, при любом из которых разбор обязателен:
| Триггер | Почему именно он |
|---|---|
| Сожжено больше 10 % месячного бюджета ошибок за одно событие | Порог считается арифметически, а не на глаз: при SLO 99,9 % это 4,3 минуты полного отказа |
| Любая потеря или порча пользовательских данных | Даже на одну запись: класс отказа стоит дороже, чем его единичное проявление |
| Потребовалось ручное вмешательство в данные в проде | Значит, автоматика не справилась и знание о починке живёт в голове одного человека |
| Пейджер сработал ночью | Ночной вызов — самая дорогая единица работы в команде, см. дежурство |
| Повтор класса отказа, уже разобранного ранее | Первый разбор не сработал, и это отдельный предмет для разбора |
| Инцидент затронул внешние обязательства (SLA, регуляторика) | Здесь у документа появляется второй читатель |
| Любой участник попросил | Особенно ценно: обычно так регистрируются near miss |
Последняя строка — не вежливость. Это единственный канал, по которому в систему попадают инциденты, которых не было: случаи, когда чуть не сломали, но поймали. Про них ниже отдельно.
Механизм безобвинительности
Требование «постмортем без обвинения» чаще всего подаётся как этическая норма — и в таком виде разваливается при первом же возражении: «а если человек действительно ошибся?». Он действительно ошибся. Вопрос не в этом. Вопрос в том, что происходит с потоком данных, если разбор является местом, где за ошибки прилетает.
Модель: почему обвинение сокращает находки нелинейно
Единственный источник данных о том, что происходило внутри инцидента, — участники. В логах нет того, какую команду человек набрал и стёр, не выполнив; какой дашборд он открыл первым; почему решил, что дело в базе; какая строчка рунбука сбила с толку; что в этот момент писали в соседнем чате. Всё это существует только в памяти людей и попадает в разбор только добровольно.
Огрубим до модели. Пусть в инциденте есть $n$ существенных деталей, каждая известна одному участнику, и каждая раскрывается с вероятностью $p$ — вероятностью, что человек сочтёт рассказ безопасным. Способствующий фактор обычно не виден из одной детали: он собирается из $k$ деталей, между которыми надо провести связь. Тогда ожидаемая доля обнаруженных факторов приблизительно
$$P \approx p^{k},$$
где $P$ — вероятность, что фактор будет обнаружен.
Подставим. При $k = 2$: откровенность $p = 0{,}9$ даёт $0{,}81$, а $p = 0{,}6$ — уже $0{,}36$. Снижение откровенности в полтора раза срезало находки более чем вдвое. При $k = 3$: $0{,}73$ против $0{,}22$ — падение в три с лишним раза.
Модель груба (детали не независимы, $k$ разный у разных факторов), но её вывод устойчив и от параметров не зависит: потери находок нелинейны по глубине связи. Мелкие факторы, видимые из одной детали, вы найдёте и в обвинительном режиме. Именно глубокие — те, ради которых разбор и затевался, — исчезнут первыми.
Дальше контур замыкается: меньше найденных факторов → поверхностные правки («добавили пункт в чеклист код-ревью») → тот же класс отказа повторяется → давление на команду растёт → откровенность падает ещё. Это усиливающая петля обратной связи в чистом виде, устройство таких контуров разобрано в петлях обратной связи, а их коварная особенность — большая задержка между причиной и проявлением — в задержках. Полгода после того, как разбор превратился в разнос, метрики выглядят нормально: инциденты ещё не успели вернуться.
Near miss — данные, которые исчезают первыми
Близкие промахи — самый дешёвый источник данных о надёжности, какой у вас есть. Никто не пострадал, бюджет не сожжён, а информация о слабом месте та же самая. Их всегда кратно больше, чем инцидентов.
И они исчезают первыми, потому что у них нет обязательности: инцидент нельзя не заметить, а про «я чуть не выполнил это на проде» можно просто промолчать. Практический признак, что обвинительный контур уже работает: в разборах перестали появляться near miss. Их не стало меньше — о них перестали рассказывать. Это ранний индикатор, он срабатывает за месяцы до того, как испортится статистика инцидентов.
Что делает работающая процедура: ASRS
Что описанный механизм существует, известно не из опыта IT. После катастрофы TWA 514 в 1974 году (экипаж снизился до высоты, разрешённой в другой точке маршрута; выяснилось, что похожий случай в другой авиакомпании уже был, но информация никуда не пошла) в США в 1976 году запустили Aviation Safety Reporting System — https://asrs.arc.nasa.gov/.
В её конструкции два элемента, и оба переносятся дословно:
- Отчёты принимает не тот, кто наказывает. Систему ведёт NASA — нейтральная сторона, а не FAA, у которого полномочия отзывать лицензии. Идентифицирующие сведения удаляются при приёме.
- Подача отчёта даёт ограниченный иммунитет. При непреднамеренном нарушении своевременно поданный отчёт снимает штрафные санкции.
Результат — десятки тысяч добровольных отчётов в год от людей, которым за это ничего не платят. Пилот сообщает о собственной ошибке, потому что сообщить безопаснее, чем промолчать.
Перенос в инженерную команду: тот, кто ведёт разбор, не должен быть тем, кто ставит оценки за перформанс. Если разбор ведёт руководитель, принимающий решения о повышениях, никакая формулировка «у нас без обвинения» этого не компенсирует — люди реагируют на структуру, а не на декларацию. Взгляд на инциденты со стороны руководителя — дежурства и инциденты; здесь важно только структурное следствие: роли разводят.
Послезнание не отключается усилием воли
Барух Фишхофф в 1975 году показал (Hindsight ≠ foresight), что люди, знающие исход, систематически переоценивают его предсказуемость — и, что важнее для нас, не могут отключить это знание, даже когда их прямо просят. Инструкция «оценивайте так, как если бы вы не знали результата» эффекта почти не даёт.
Отсюда практический вывод: призыв «не судите задним числом» не работает, нужен процедурный приём. Приём такой — сначала восстановить, что было видно на экранах в каждый момент, и только потом обсуждать решения. Не «почему он не посмотрел на выкатки», а «какие панели были открыты, что на них было, откуда он должен был узнать про выкатки».
Разница между левой и правой частями этой картинки и есть весь предмет спора. В 20:33 у дежурного четыре факта, и гипотеза «медленный платёжный шлюз» — лучшее объяснение именно этих четырёх. Факт «в 20:14 выкачен конфиг» существовал, но жил на другом дашборде, ссылки на который не было ни в рунбуке, ни в алерте. Фраза «надо было сразу посмотреть выкатки» произносится из правой части картинки, где известны все девять фактов, и в качестве вывода бесполезна: она не порождает ни одного изменения. Формулировка «связь выкаток и SLI не была доступна из алерта» порождает ровно одно, конкретное и проверяемое.
Тест подстановки
Самый практичный критерий, отделяющий системную проблему от индивидуальной, — substitution test: подставьте на место участника другого инженера той же квалификации, с теми же данными на экране, в тот же час ночи, с тем же давлением. Сделал бы он то же самое?
Если ответ «скорее да» — вы смотрите на свойство системы, и обсуждать конкретного человека бессмысленно. Если «нет, большинство поступило бы иначе» — стоит понять, чего этому человеку не хватало: контекста, доступа, опыта, — и это тоже свойство системы (онбординга, документации, распределения знаний), просто другой её части.
Тест сформулирован в расследовательской практике авиации, у Сидни Деккера он развёрнут в принцип локальной рациональности: действия человека были осмысленны в его контексте с его данными, иначе он бы их не совершил. Задача разбора — восстановить этот контекст, а не оценить действия из своего.
Граница: безобвинительность не равна безнаказанности
Это место, где практика чаще всего вырождается в лозунг, поэтому границу надо назвать прямо.
Дэвид Маркс и Сидни Деккер (концепция just culture) разделяют три вещи, и разные вещи требуют разной реакции:
| Что произошло | Пример | Реакция |
|---|---|---|
| Человеческая ошибка — непреднамеренное действие | Выполнил команду в консоли прода, думая, что это стейдж | Утешить человека, менять систему: разные цвета консолей, подтверждение по имени кластера |
| Рискованное поведение — сознательное отклонение, риск которого не осознаётся | Выкатывает мимо канарейки, потому что «так делают все и всегда работало» | Убрать стимулы к отклонению: почему обходной путь быстрее правильного? Сделать правильный путь удобнее |
| Безрассудство — сознательное игнорирование понятого риска | Отключил проверки, зная и понимая, чем это грозит, ради срока | Дисциплинарный разговор — но не в разборе и не с фасилитатором |
Средняя строка — самая частая и самая интересная. Диана Вон, разбирая катастрофу «Челленджера», назвала это нормализацией отклонения: отклонение, которое несколько раз прошло без последствий, постепенно становится новым стандартом, и в момент отказа никто не считает себя нарушителем. Механика — тот же дрейф, что описан в архетипах системного мышления. Лечится не выговором, а вопросом «почему безопасный путь оказался дороже небезопасного» — и ответ почти всегда обнаруживает инженерный дефект: медленный CI, неудобный инструмент, обязательное согласование, которое ждёт три часа.
Итог границы: безобвинительность не означает отсутствия последствий за намеренное нарушение. Она означает, что разбор инцидента — не то место, где эти вопросы решаются, потому что совмещение двух функций надёжно убивает первую. Если оба разговора всё равно нужны, они происходят в разное время, с разными людьми, и это проговаривается вслух.
Коренной причины не существует
Формулировка «root cause analysis» подразумевает, что причина одна и её можно найти. В распределённой системе это почти никогда не так, и Ричард Кук в How Complex Systems Fail формулирует резко: сложные системы работают в режиме постоянного присутствия скрытых дефектов, и катастрофа требует совпадения нескольких — по отдельности каждый безобиден.
Почему «пять почему» ломается
Метод даёт линейную цепь и три предсказуемых дефекта: на каждом шаге «почему» ровно одно (хотя причин обычно несколько), цепь останавливается там, где остановившему удобно, и результат зависит от того, кто ведёт.
Возьмём реальный по устройству инцидент. Разбор в стиле «пяти почему»:
- Почему упал checkout? — Исчерпан пул соединений к БД.
- Почему исчерпан? — Новый конфиг снизил
max_pool_size. - Почему конфиг снизил? — Инженер выкатил значение из общего шаблона.
- Почему выкатил? — Не проверил влияние на checkout.
- Почему не проверил? — Был невнимателен.
Пятый шаг — тупик: из него не следует ни одной правки, кроме «быть внимательнее». Причём цепь формально безупречна, каждое «почему» логично. Дефект в форме, а не в добросовестности.
Дерево способствующих факторов
Замена — не цепь, а граф, разложенный по трём ветвям: почему сломалось, почему долго не видели, почему долго чинили. Это ровно те три слагаемых, из которых складывается ущерб, и каждая ветвь даёт свой класс правок.
37 мин · 62 % отказов checkout"] INC --> B1["Почему сломалось"] INC --> B2["Почему 9 минут не видели"] INC --> B3["Почему 28 минут чинили"] B1 --> A1["Лимит пула попал в общий шаблон
конфига три недели назад"] B1 --> A2["Конфиг выкатывается тем же путём,
что код, но без канареечной фазы"] B1 --> A3["Нагрузочный стенд работает на 1/10
трафика — исчерпание не воспроизводится"] B2 --> C1["Метрика занятости пула не экспортируется
(есть только размер)"] B2 --> C2["Окно алерта по скорости сжигания
даёт 9 минут при этой тяжести"] B3 --> D1["Дашборд выкаток и дашборд SLI —
разные, ссылок друг на друга нет"] B3 --> D2["Рунбук ссылался на дашборд,
удалённый в марте"] B3 --> D3["Гипотезу про шлюз нечем было
быстро опровергнуть"] A1 -.-> A2 C1 -.-> D3
Восемь факторов вместо одного «невнимателен». Каждый формулируется как свойство системы, каждый порождает проверяемую правку, ни один не требует называть фамилию. Пунктирные связи — важная часть: фактор A1 стал опасен именно потому, что действовал A2, а отсутствие метрики (C1) прямо продлило жизнь неверной гипотезе (D3). Техника рисования таких графов — причинно-следственные диаграммы.
Заметьте: ни один фактор не является «корневым». Уберите любой из A1–A3 — инцидента не будет. Это и есть практический смысл отказа от единственной причины: у вас есть выбор, какой фактор дешевле убрать, и выбор этот инженерный, а не следственный.
Влияние считают в бюджете, а не в минутах
«Инцидент длился 37 минут» — не измерение ущерба. Оно не отвечает ни на один вопрос, который вам предстоит решать: важнее ли эта правка, чем фича; надо ли замораживать релизы; какой из трёх инцидентов квартала чинить первым.
Единица, в которой ущерб сравним и складывается, — доля сожжённого бюджета ошибок.
Наивный расчёт и почему он занижает
Исходные данные. SLO checkout: 99,9 % успешных запросов за 30 дней. Средний трафик 40 rps. Инцидент: 37 минут, доля неуспешных запросов 62 %, время вечернее — 70 rps.
Месячный бюджет в событиях. Всего запросов за окно:
$$40 \cdot 30 \cdot 24 \cdot 3600 = 103\,680\,000.$$
Бюджет — $0{,}1\,%$ от них: $103\,680\,000 \cdot 0{,}001 = 103\,680$ «плохих» запросов.
Наивная оценка по времени. 37 минут при 62 % отказов «эквивалентны» $37 \cdot 0{,}62 = 22{,}9$ минуты полного отказа. Месячный бюджет по времени — 43,2 минуты. Значит, $22{,}9 / 43{,}2 = 53\,%$ бюджета.
Честная оценка по событиям. Плохих запросов за инцидент:
$$37 \cdot 60 \cdot 70 \cdot 0{,}62 = 96\,348.$$
Доля бюджета: $96\,348 / 103\,680 = 92{,}9\,%$.
Разница почти вдвое. Причём коэффициент не случаен: $92{,}9 / 53{,}1 = 1{,}75$ — ровно отношение трафика в момент инцидента к среднему ($70/40 = 1{,}75$). Время-взвешенный расчёт занижает ущерб ровно во столько раз, во сколько трафик в момент отказа превышал средний, а инциденты, что неудобно, любят случаться в пик — потому что пик и есть та нагрузка, на которой система ломается.
Правило, которое стоит записать: если SLI событийный, ущерб считают событиями. Разница между двумя определениями бюджета разобрана в главе про бюджет ошибок.
Что это меняет в решении
Остаток бюджета: $103\,680 - 96\,348 = 7\,332$ запроса на оставшиеся 27 дней месяца. При 40 rps это $7\,332 / 40 = 183$ секунды — три минуты полного отказа до конца окна.
Из формулировки «инцидент длился 37 минут» не следует ничего. Из формулировки «до конца месяца у нас осталось три минуты» следует всё: релизы checkout замораживаются, кроме правок надёжности; выкатки идут через канарейку с автооткатом; правки из этого разбора получают приоритет выше очередной фичи не потому, что кто-то так решил, а потому что бюджета нет. Это и есть работающая связь разбора с приоритетами — единственная, которая не держится на авторитете конкретного человека.
Агрегат за квартал: что чинить на самом деле
Один разбор отвечает на вопрос «что случилось». На вопрос «куда вкладывать инженерное время» отвечает только агрегат по классу отказа.
| Инцидент | Длит. | Класс отказа | Сожжено месячного бюджета |
|---|---|---|---|
| CHK-2026-01 | 12 мин | Исчерпание пула соединений | 20,3 % |
| CHK-2026-02 | 8 мин | Ошибка конфигурации при выкатке | 14,0 % |
| CHK-2026-03 | 37 мин | Исчерпание пула соединений | 92,9 % |
| CHK-2026-04 | 19 мин | Отказ внешнего платёжного шлюза | 23,1 % |
| CHK-2026-05 | 6 мин | Ошибка конфигурации при выкатке | 8,7 % |
| CHK-2026-06 | 14 мин | Исчерпание пула соединений | 25,1 % |
Сумма по классам за квартал: исчерпание пула — 138,4 %, внешний шлюз — 23,1 %, конфигурация — 22,7 %.
Ни один отдельный разбор этого не показывает. Самый громкий инцидент (CHK-2026-03) и правда из первого класса, но два других того же класса выглядели «мелкими» и в отдельности не оправдывали серьёзной работы. Вместе они означают, что за квартал один класс отказа съел почти полтора месячных бюджета целиком, — и именно он должен стоять первым в плане, а не самый свежий.
Отсюда правило: классификацию отказа записывают в постмортем как обязательное машиночитаемое поле, из фиксированного словаря. Иначе агрегат не построить, а без агрегата разборы остаются набором отдельных историй.
from dataclasses import dataclass
from collections import defaultdict
WINDOW_S = 30 * 24 * 3600 # окно SLO — 30 дней
@dataclass
class Incident:
id: str
failure_class: str # из фиксированного словаря, иначе агрегат не сойдётся
duration_s: int # длительность деградации по SLI, не по чату
rps_during: float # трафик В МОМЕНТ инцидента, а не средний за месяц
bad_fraction: float # доля запросов, плохих по определению SLI
def bad_events(inc: Incident) -> float:
"""Сколько событий бюджета съел инцидент."""
return inc.duration_s * inc.rps_during * inc.bad_fraction
def month_budget(rps_avg: float, slo: float) -> float:
"""Месячный бюджет в событиях: весь трафик, умноженный на долю потерь."""
return rps_avg * WINDOW_S * (1.0 - slo)
def burned_share(inc: Incident, rps_avg: float, slo: float) -> float:
return bad_events(inc) / month_budget(rps_avg, slo)
def by_class(incidents, rps_avg: float, slo: float) -> dict[str, float]:
"""Агрегат за период. O(n) по времени, O(k) по памяти (k — число классов)."""
acc: dict[str, float] = defaultdict(float)
for inc in incidents:
acc[inc.failure_class] += burned_share(inc, rps_avg, slo)
return dict(sorted(acc.items(), key=lambda kv: -kv[1]))
quarter = [
Incident("CHK-2026-01", "db-pool-exhaustion", 12 * 60, 65, 0.45),
Incident("CHK-2026-02", "bad-config-rollout", 8 * 60, 55, 0.55),
Incident("CHK-2026-03", "db-pool-exhaustion", 37 * 60, 70, 0.62),
Incident("CHK-2026-04", "upstream-gateway", 19 * 60, 60, 0.35),
Incident("CHK-2026-05", "bad-config-rollout", 6 * 60, 50, 0.50),
Incident("CHK-2026-06", "db-pool-exhaustion", 14 * 60, 62, 0.50),
]
for cls, share in by_class(quarter, rps_avg=40, slo=0.999).items():
print(f"{cls:24s} {share:6.1%} месячного бюджета за квартал")
# db-pool-exhaustion 138.4% месячного бюджета за квартал
# upstream-gateway 23.1% месячного бюджета за квартал
# bad-config-rollout 22.7% месячного бюджета за квартал
Сырые числа для инцидента берутся из той же системы, что и SLO, — считать «на глазок» по чату нельзя. Для Prometheus:
# Плохие события за окно инцидента.
# Запрос выполняют, установив время оценки на момент окончания инцидента,
# иначе increase() посчитает не тот отрезок.
sum(increase(http_requests_total{job="checkout", code=~"5.."}[37m]))
# Месячный бюджет в событиях: весь трафик за окно SLO, умноженный на (1 - SLO).
sum(increase(http_requests_total{job="checkout"}[30d])) * 0.001
Разложение времени: где именно потеряны минуты
Общая длительность — сумма слагаемых с разной природой и разной стоимостью улучшения. Разложите её, прежде чем решать, что чинить.
Складываем: $9 + 4 + 6 + 13 + 5 = 37$. Теперь видно распределение:
- 13 минут (35 %) — ложная гипотеза. Самое дорогое слагаемое, и самое неочевидное: в чате в это время шла интенсивная работа, ощущение продуктивности было полным.
- 9 минут (24 %) — обнаружение.
- 5 минут (14 %) — само смягчение. То есть починка заняла меньше седьмой части инцидента.
Теперь прикинем эффект правок в минутах, а не в ощущениях:
| Правка | Что сокращает | Оценка выигрыша |
|---|---|---|
| Идеальное обнаружение (алерт мгновенно) | TTD с 9 до ~1 мин | −8 мин (−22 %) |
| Ссылка «последние выкатки» прямо в алерте | Проверка гипотезы за 2 мин вместо 13 | −11 мин (−30 %) |
| Метрика занятости пула на дашборде SLI | Гипотеза не возникает вовсе | −11 мин, пересекается с предыдущей |
| Канарейка с автооткатом для конфигов | Инцидент не доходит до 100 % трафика | −30 мин и более |
| Пункт в рунбуке «проверить выкатки» | Зависит от того, откроют ли рунбук | оценке не поддаётся |
Отсюда два вывода, которые постоянно упускают. Первый: улучшать обнаружение обычно дешевле, чем диагностику, но потолок у него ниже — быстрее нуля не станет, а 28 минут после обнаружения останутся. Второй: правка «добавить алерт» покупает только время обнаружения и не меняет вероятность повтора. Это законная покупка, если понимать, что именно куплено. Беда начинается, когда алерт становится ответом по умолчанию: постмортемы — крупнейший генератор алертового шума в любой команде, потому что после каждого разбора кто-то предлагает «а давайте на это тоже поставим алерт», и через год пейджер звонит по сорока правилам, из которых половина ни разу никого никуда не привела. Механика обесценивания канала посчитана в главе про алерты.
Анатомия документа
Форма имеет значение ровно постольку, поскольку заставляет задать вопросы, которые иначе не задаются. Каждая секция ниже существует потому, что без неё что-то теряется.
Машиночитаемая шапка — чтобы агрегат из предыдущего раздела вообще был возможен:
id: CHK-2026-03
title: Отказ checkout из-за исчерпания пула соединений
status: reviewed # draft | reviewed | actions-closed
severity: sev2
failure_class: db-pool-exhaustion # из словаря, не свободный текст
service: checkout
started_at: 2026-03-17T20:14:00+03:00
detected_at: 2026-03-17T20:23:00+03:00
mitigated_at: 2026-03-17T20:51:00+03:00
ttd_s: 540
tta_s: 240
ttm_s: 1680
budget_burned_pct: 92.9
facilitator: r.ivanova # не участник инцидента
participants: [a.petrov, s.kim, m.orlov]
related: [CHK-2026-01, CHK-2026-06] # тот же класс — повод для агрегата
Дальше — тело документа:
| Секция | Зачем она нужна | Типичное вырождение |
|---|---|---|
| Резюме, 5 строк | Единственное, что прочитают все | Пересказ таймлайна вместо сути |
| Влияние | В единицах пользователя И в доле бюджета | «Сервис был недоступен» без чисел |
| Таймлайн | Сырьё для всего остального | Смешение фактов и интерпретаций |
| Обнаружение | TTD/TTA отдельно от TTM — разные правки | Одна цифра «длительность» |
| Способствующие факторы | Граф, а не цепь | Один пункт, названный «причиной» |
| Что сработало | Защиты, которые надо сохранить при рефакторинге | Пропускают как «нескромное» |
| Где повезло | Самая ценная секция, см. ниже | Отсутствует |
| Правки | Владелец-человек, дата, тикет, уровень силы | «Команда рассмотрит возможность» |
| Что осталось непонятным | Честный список открытых вопросов | Прячут, чтобы выглядеть компетентно |
Про таймлайн одно жёсткое правило: факт и интерпретация помечаются по-разному. «20:33 — открыт дашборд платёжного шлюза, latency p99 равна 1,4 с» — факт. «20:33 — решили, что виноват шлюз» — тоже факт (решение было принято). «20:33 — ошибочно решили, что виноват шлюз» — интерпретация, которая протаскивает послезнание внутрь сырых данных. Слово «ошибочно» в таймлайне запрещено: оно ставится потом, в разделе выводов, и не про людей, а про то, чего не хватило.
«Где нам повезло» — секция из практики Google, которую почти все выбрасывают, и зря. Она регистрирует условия, при отсутствии которых было бы существенно хуже: «повезло, что это случилось в 20:14, а не в 03:14 — дежурный был за компьютером»; «повезло, что вторая реплика не перезапускалась в этот момент»; «повезло, что откат конфига оказался возможен — на прошлой неделе его чуть не сделали необратимым». Каждый такой пункт — бесплатно полученный near miss. По содержанию это близкие промахи, которые вы иначе никогда бы не зарегистрировали, потому что формально ничего не произошло.
Ревью: кто ведёт и какими словами
Разбор — встреча, и у неё есть конструкция, от которой зависит результат.
Кто ведёт. Фасилитатор — не участник инцидента и не тот, кто оценивает людей. В маленькой команде идеала не будет; минимально приемлемо — не тот, кто чинил лично. Роль фасилитатора описана в открытом Debriefing Facilitation Guide от Etsy — это лучший практический текст по теме, и он на 30 страниц, а не на 300.
Когда. В течение 3–5 рабочих дней. Раньше — люди не выспались и злы; позже — детали таймлайна уже реконструируются, а не вспоминаются, и качество данных падает необратимо.
Как. Таймлайн зачитывается вслух по шагам, и на каждом шаге фасилитатор спрашивает не «почему вы так сделали», а «что вы видели в этот момент». Разница не косметическая: первый вопрос — грамматически обвинение, на него отвечают оправданием («ну, я подумал, что…»), и оправдание не содержит данных. Второй вопрос запрашивает состояние экрана, и ответ на него — данные.
| Так спрашивать нельзя | Так — можно | Что порождает вторая формулировка |
|---|---|---|
| «Почему ты не проверил выкатки?» | «Откуда в тот момент можно было узнать про выкатки?» | Правка: ссылка на выкатки в шаблоне алерта |
| «Почему ты выкатил это в пятницу?» | «Что в процессе делало пятничную выкатку нормальной?» | Обнаруживается, что окно выкаток нигде не записано |
| «Кто одобрил этот конфиг?» | «Что проверяется при ревью конфигов, а что нет?» | Правка: валидация лимитов в CI |
| «Почему так долго не эскалировали?» | «Какой признак должен был сказать, что пора эскалировать?» | Правка: явный порог эскалации в рунбуке |
| «Это же очевидно было» | «Что делало гипотезу про шлюз убедительной?» | Обнаруживается отсутствие метрики пула |
Правая колонка — не смягчённые версии левой. Это другие вопросы, и отвечают на них другим: не про человека, а про устройство системы.
Как довести до правок
Здесь программа постмортемов ломается чаще всего. Документы пишутся, пункты заводятся, и половина не делается — не по злому умыслу, а по совершенно понятной причине: у фичи есть заказчик, который придёт и спросит, а у правки надёжности заказчика нет. При равном приоритете выигрывает та работа, о которой кто-то спросит. Значит, механизм нужно строить так, чтобы спрашивали.
Сила правки
Прежде чем говорить о доведении, надо уметь отличать сильную правку от слабой — иначе доведёте до конца список слабых.
Рамка заимствована из промышленной безопасности (NIOSH Hierarchy of Controls) и переносится в софт без изменений. Ключевое различие проходит по линии обнаружения: всё, что ниже неё, не меняет вероятность повтора — меняется только время, за которое вы про повтор узнаете. Ниже линии находятся все правки вида «добавить алерт», «дописать рунбук», «провести обучение». Они нужны, но если весь список правок состоит из них, вероятность следующего инцидента ровно та же, что была до разбора.
Практический критерий силы: сформулируйте, что физически должно произойти, чтобы отказ повторился. Для правки «валидация лимитов пула в CI» — кто-то должен обойти CI. Для правки «пункт в рунбуке» — достаточно, чтобы человек не открыл рунбук в три часа ночи.
Приоритизация
Квадрант «Ловушка» — про дорогие правки, которые лишь ощущаются серьёзными: полное переписывание, миграция на другой стек, «наведём порядок в конфигурации». Их предлагают на эмоциях сразу после инцидента, и они съедают квартал, не убирая ни одного из восьми факторов.
Жизненный цикл правки
Два перехода делают эту схему рабочей. «Просрочена → Отклонена» узаконивает отказ: пункт, который команда полгода переносит, всё равно не будет сделан, и честнее закрыть его с обоснованием, чем держать в списке ради статистики. Список правок, где нет отклонённых, — это не дисциплинированная команда, а команда, которая перестала смотреть в список.
«Выкачена → Проверена отказом» — переход, который почти всегда пропускают. Правка, о которой известно только то, что она выкачена, — гипотеза. Убедиться, что она работает, можно единственным способом: воспроизвести условия отказа. Это прямой мост к учениям и chaos-экспериментам: у game day появляется бесплатная и осмысленная программа — проверять правки прошлого квартала.
Шесть механизмов, которые действительно работают
- Бюджет ошибок как источник приоритета. Пока бюджет цел, правки конкурируют с фичами на общих основаниях. Когда сожжён — работа по надёжности становится обязательной по заранее записанному правилу, а не по результату спора. Это единственный механизм, который не держится на авторитете: он был согласован до инцидента, когда все были спокойны.
- Владелец — человек, срок — дата. «Команда платформы, Q3» не является ни владельцем, ни сроком. Владелец — тот, кого можно спросить в лицо; дата — та, которая наступает.
- Правки живут в общем бэклоге. Отдельный «reliability backlog» — кладбище: у него нет ритуала планирования, и в него никто не смотрит. Тот же трекер, тот же спринт, тот же обзор.
- Явная квота ёмкости. Без неё правки проигрывают всегда — не из-за приоритетов, а из-за асимметрии: у фичи есть дата и заказчик, у правки нет ни того, ни другого. Ниже посчитаем, сколько это в процентах; типичная честная цифра — около 20 % ёмкости на надёжность и техдолг суммарно, механика торга описана в техдолге.
- Регулярный обзор просроченного. Раз в две недели, 15 минут, список пунктов старше срока. Не для порицания — для перевода в «отклонена» или назначения новой даты. Без этого ритуала список гниёт молча.
- Уровень силы как обязательное поле. Если у каждой правки проставлен уровень из иерархии выше, то отчёт «за квартал 14 правок, из них 11 административных» становится виден сам собой — и это самый неприятный и самый полезный слайд, который вы можете показать.
Метрики программы разборов
Метрика «сколько постмортемов написано» бесполезна и вредна: её оптимизируют написанием постмортемов.
| Метрика | Ориентир | Что ловит |
|---|---|---|
| Доля правок, закрытых в срок | > 70 % | Работает ли механизм доведения |
| Медианный возраст открытых правок | < 45 дней | Тихое гниение списка |
| Доля правок уровня «защита» и выше | > 40 % | Не выродились ли в алерты и рунбуки |
| Доля инцидентов уже разобранного класса | падает от квартала к кварталу | Главная. Если не падает — программа не работает |
| Время от инцидента до опубликованного разбора | < 5 рабочих дней | Качество данных в таймлайне |
| Доля разборов, содержащих секцию «где повезло» | растёт | Косвенный индикатор откровенности |
Последняя строка — прокси на то, что важно, но не измеряется прямо. Секцию «где повезло» пишут только там, где не боятся признать, что держались на удаче.
Пять девяток и постмортемы
Ещё одно место, где красивое число из презентации сталкивается с арифметикой. При SLO 99,999 % месячный бюджет — 26 секунд. Инцидент, укладывающийся в такой бюджет, длится меньше, чем требуется человеку, чтобы прочитать заголовок алерта и открыть ноутбук.
Из этого следует не «постмортемы не нужны», а смена их предмета. При 99,9 % разбор изучает действия людей во время инцидента: что видели, что решили, где потеряли минуты. При 99,99 % и выше человек в контур уже не помещается физически, и предметом разбора становится поведение автоматики: почему размыкатель не сработал, почему автооткат не сработал, почему переключение на резерв заняло 40 секунд вместо трёх. Таймлайн собирается из логов, а не из памяти участников, и вопрос «что вы видели в 20:33» теряет смысл, потому что в 20:33 никто ничего не видел.
Практический вывод: если вам обещают пять девяток, спросите, кто и на каком оборудовании будет реагировать за 26 секунд в месяц — и во что обойдётся резервирование, при котором отказ вообще не доходит до пользователя. Обычно после честного подсчёта цель опускается до 99,9 % там, где это правда нужно, и до 99 % на всём остальном. Три независимых аргумента против пяти девяток разобраны в бюджете ошибок.
Цена
Разбор — не бесплатная добродетель. Считаем на том же инциденте.
Человекочасы одного постмортема:
| Работа | Часы |
|---|---|
| Сбор таймлайна из логов, чатов и метрик (фасилитатор) | 3 |
| Интервью с тремя участниками по 30 минут | 1,5 |
| Написание документа | 2,5 |
| Ревью: 6 человек × 1 час | 6 |
| Доработка после ревью и заведение тикетов | 1 |
| Итого документ | 14 человекочасов ≈ 1,75 человекодня |
| Правки: 4 пункта, медиана 6 часов | 24 |
| Итого с правками | 38 человекочасов ≈ 4,75 человекодня |
При двух разборах в месяц это ≈ 9,5 человекодня. В команде из восьми инженеров месячная ёмкость — примерно $8 \cdot 21 = 168$ человекодней, значит, программа разборов забирает $9{,}5 / 168 \approx 5{,}7\,%$ ёмкости.
Эти 5,7 % будут потрачены в любом случае — вопрос лишь в том, запланированы они или взяты по факту из фич, порождая ежемесячный конфликт с продуктом. Отсюда пункт 4 из списка механизмов: квоту проще согласовать, когда есть цифра.
Цена для людей. Постмортем обычно пишет тот, кого ночью разбудили. Ночной вызов уже стоил ему следующего дня; требование написать документ поверх — двойной штраф, и именно так практика становится наказанием, каким формально не является. Рабочие противовесы: таймлайн собирает фасилитатор, а не участник; участник даёт получасовое интервью; после ночного инцидента человек не пишет документ в тот же день. Полная арифметика износа дежурства — в главе про дежурство.
Цена разборов без правок. Это отдельный и недооценённый вид ущерба. Команда, которая четвёртый раз подряд разбирает один и тот же класс отказа и четвёртый раз видит, что правки не сделаны, приходит к очень рациональному выводу: писать их бессмысленно. После этого качество документов падает быстрее, чем если бы разборов не было вовсе, — потому что теперь есть ритуал, изображающий работу. Не начинайте программу разборов, если у вас нет механизма доведения правок. Лучше три инцидента в год, разобранных до выкаченных изменений, чем двадцать документов в вики.
Избыточность стоит денег. Многие сильные правки — это резервирование: вторая реплика, второй регион, пулер перед базой, канареечный контур. Это постоянные расходы, а окупаются они предотвращёнными инцидентами, которых не будет видно. Здесь разбор помогает единственным доступным способом: даёт цифру ущерба в бюджете, которую можно сопоставить со стоимостью резерва. Сопоставление денег и надёжности — облачные расходы и компромиссы.
Где инженерное упирается в организационное
Три места, и все три не решаются шаблоном документа. Называть их надо прямо, а не пытаться починить процессом.
Первое. Если приоритеты команды определяет не команда, а квоты на надёжность нет, правки не будут сделаны независимо от качества разборов. Что здесь может инженер: не спорить о важности, а предъявлять числа — «класс db-pool-exhaustion сжёг 138 % месячного бюджета за квартал; правка стоит 24 человекочаса». Спор о ценностях выигрывает тот, у кого больше полномочий; спор о числах — тот, у кого есть числа.
Второе. Если разбор ведёт человек, от которого зависит перформанс-ревью участников, безобвинительность недостижима никакими формулировками. Роли разводятся или практика вырождается. Это структурное решение, и принимается оно не инженером.
Третье. Если инцидент затрагивает внешние обязательства, у документа появляется второй читатель — юрист или клиент. Тексты для этих двух читателей несовместимы: внутренний должен содержать всё, включая «мы полчаса копали не там», внешний — факты и меры. Единственное работающее решение — два документа, где внешний пишется на основе внутреннего, а не вместо него. Попытка написать один, годный для обоих, всегда даёт бесполезный внутренний.
Что переносится от Google, а что нет
Практика пришла в индустрию не из Google. Она сложилась в отраслях, где цена отказа измеряется в жизнях, и в IT её принесли из авиации и медицины, а Google лишь дал широко прочитанное описание.
Из книг Google (https://sre.google/sre-book/postmortem-culture/ и https://sre.google/workbook/postmortem-culture/) переносится почти без потерь:
- сам шаблон документа и пример разбора (https://sre.google/sre-book/example-postmortem/);
- секции «что сработало» и «где повезло»;
- разделение фактов и интерпретаций в таймлайне;
- связь правок с бюджетом ошибок;
- принцип «фасилитатор не участник».
Не переносится:
- Отдельная роль и обученный пул фасилитаторов. В команде из восьми человек фасилитатором будет коллега, который вчера чинил соседний сервис. Это хуже идеала и вполне работает.
- Ритуалы масштаба: внутренние публикации на десятки тысяч читателей, «постмортем месяца», комитеты по разборам. В маленькой организации это выглядит пародией и умирает за два квартала.
- Тяжесть формата. Документ на 12 страниц оправдан, когда его прочитают двести человек. При аудитории в восемь достаточно одной страницы и получаса встречи. Формат надо резать агрессивно: секции «влияние в бюджете», «факторы», «правки» обязательны, остальное — по обстоятельствам.
- И главное — статистика. У Google один класс отказа встречается сотни раз в год и набирает выборку, на которой видны закономерности. У вас он встретится дважды, и отличить закономерность от совпадения вы не сможете. Следствие прямое: для маленькой команды основная ценность разбора смещается со статистики на передачу знания между людьми. Половина пользы возникает в момент, когда трое коллег впервые узнают, как на самом деле устроен пул соединений. Это не побочный эффект — при малом числе инцидентов это основной эффект, и формат стоит затачивать под него: меньше документа, больше совместного разговора у одного экрана.
Чужие публичные разборы при этом становятся ценнее собственных, потому что дают ту выборку, которой у вас нет. Читайте: коллекцию danluu/post-mortems, разборы Cloudflare (например, отказ 2 июля 2019 года из-за одного регулярного выражения), отчёт AWS по S3 2017 года и отчёты VOID, где собраны публичные инциденты и разобрана несостоятельность поверхностных метрик вроде MTTR.
GitLab, 2017: разбор, который спас пять бэкапов
Лучшая известная иллюстрация тезиса этой главы — инцидент GitLab 31 января 2017 года.
Короткая версия: борясь с последствиями нагрузки и сбоя репликации, инженер выполнил удаление каталога данных не на той машине — на основной вместо вторичной. Потеряно около шести часов пользовательских данных.
Обвинительный разбор здесь заканчивается на первом шаге: человек выполнил разрушительную команду не там, где собирался. Вывод — «быть внимательнее», возможно, с оргвыводами. Формально всё верно.
Разбор, который они провели и опубликовали, пошёл дальше и обнаружил вещь на порядок важнее: из пяти предусмотренных механизмов резервного копирования не работал ни один. Регулярные дампы молча падали из-за несовпадения версий инструмента, уведомления об этом не доходили, снапшоты диска для этого сервера не были включены, выгрузка в объектное хранилище оказалась пустой. Восстановились в итоге из случайного снапшота стейджинга, сделанного за шесть часов до инцидента.
Обратите внимание на структуру вывода. Ошибочная команда была триггером, а не причиной. Причиной потери данных было то, что вся система резервирования не проверялась и молча деградировала месяцами. Обвинительный разбор остановился бы на человеке — и оставил бы пять сломанных бэкапов в проде, а следующий отказ (в котором никто не ошибся бы командой, просто умер бы диск) закончился бы точно так же.
Это не аргумент о доброте. Это иллюстрация к арифметике из начала главы: обвинительный разбор находит триггер, потому что триггер виден из одной детали. Латентные условия собираются из нескольких и требуют откровенности, которой в обвинительном режиме нет.
Отдельно стоит заметить, что GitLab вёл разбор публично, в открытом документе, во время самого инцидента. Это крайность, повторять её не обязательно. Но она хорошо показывает, что откровенность и профессиональная репутация — не противоположности: этот постмортем сделал для их инженерной репутации больше, чем любой пресс-релиз.
Типичные ошибки
- Разбор ведёт тот, кто чинил. Он неизбежно рассказывает уже сложившуюся у себя версию, и альтернативные гипотезы не проверяются.
- Единственная «коренная причина» в шапке документа. Как только она названа, поиск остальных факторов прекращается — работает эффект остановки на первом достаточном объяснении.
- Слово «ошибочно» в таймлайне. Протаскивает послезнание внутрь сырых данных и делает таймлайн непригодным для повторного анализа.
- Правки вида «быть внимательнее», «усилить контроль», «провести обучение». Административный уровень, вероятность повтора не меняется. Допустимы как дополнение, недопустимы как весь список.
- Владелец — команда, срок — квартал. Гарантированный способ не сделать ничего.
- Постмортем на каждое отклонение. Практика обесценивается тем же механизмом, что и шумные алерты, — а тратит на порядок больше времени.
- Все правки — «добавить алерт». Через год пейджер звонит по сорока правилам, из которых половина ни разу не привела ни к какому действию, см. алерты.
- Влияние в минутах, а не в бюджете. Инциденты становятся несравнимыми, агрегат по классам не строится, приоритет назначается по громкости.
- Отсутствие агрегата по классам. Три «мелких» инцидента одного класса перевешивают один громкий другого — и остаются незамеченными.
- Документ пишется, ревью не проводится. Основная ценность возникает в разговоре: в документе есть версия автора, в разговоре — версии всех участников, и расхождения между ними самое интересное.
- Правки не проверяются отказом. Выкаченная правка — гипотеза до тех пор, пока условия отказа не воспроизведены.
- Один документ для внутреннего и внешнего читателя. Всегда даёт бесполезный внутренний.
Мини-итог
- Продукт постмортема — выкаченное изменение системы, а не документ. Всё остальное — сырьё.
- Безобвинительность — утверждение о потоке данных: находки падают примерно как $p^{k}$, где $p$ — откровенность, $k$ — число деталей, из которых собирается фактор. Глубокие факторы, ради которых разбор и затевался, исчезают первыми. Ранний индикатор поломки — near miss перестали появляться в разборах.
- Механизм лечится структурой, а не декларацией: тот, кто ведёт разбор, не тот, кто ставит оценки. Проверено ASRS с 1976 года.
- Граница честная: безобвинительность не отменяет последствий за намеренное нарушение — она означает, что разбор не то место, где эти вопросы решаются.
- Послезнание не отключается по просьбе (Фишхофф, 1975). Работает только процедура: сначала восстановить, что было на экранах, потом обсуждать решения. Критерий — тест подстановки.
- Коренной причины нет, есть граф факторов по трём ветвям: почему сломалось, почему не видели, почему чинили. Восемь устранимых факторов вместо одного «был невнимателен».
- Ущерб считают в бюджете и по событиям: 37 минут при 62 % отказов и трафике 70 rps дали 96 348 плохих запросов из 103 680 месячного бюджета — 92,9 %, а не 53 %, как даёт время-взвешенная оценка. Занижение ровно в 1,75 раза — во столько трафик превышал средний.
- Приоритеты назначает агрегат по классам за квартал, а не отдельный разбор: три инцидента одного класса дали 138 % месячного бюджета, из них два выглядели «мелкими».
- Сила правки: устранение > автоматическая защита > ограждение процесса > обнаружение > административное. Всё ниже линии обнаружения вероятность повтора не меняет.
- Доводят до правок шесть механизмов: бюджет как источник приоритета, владелец-человек с датой, общий бэклог, явная квота ёмкости, регулярный обзор просроченного, обязательное поле уровня силы.
- Цена честная: ≈ 14 человекочасов на документ, ≈ 38 с правками, ≈ 5,7 % ёмкости команды из восьми человек при двух разборах в месяц. Разборы без правок вреднее их отсутствия.
- От Google берите шаблон, «где повезло» и связь с бюджетом; не берите комитеты, публикации на десятки тысяч и надежду на статистику. При малом числе инцидентов главная ценность разбора — передача знания между людьми.
Источники
- Google. Site Reliability Engineering, ch. 15: Postmortem Culture и пример постмортема.
- Google. The Site Reliability Workbook, ch. 10: Postmortem Culture: Learning from Failure — шаблоны, метрики зрелости, разбор антипаттернов.
- John Allspaw. Blameless PostMortems and a Just Culture, Etsy, 2012 — текст, с которого практика пришла в веб-инженерию.
- Etsy. Debriefing Facilitation Guide — практическое руководство фасилитатора, лучший короткий текст по теме.
- Richard Cook. How Complex Systems Fail — 18 тезисов, читается за 15 минут, перечитывается годами.
- Sidney Dekker. The Field Guide to Understanding «Human Error», 3rd ed., CRC Press, 2014 — локальная рациональность, «второй рассказ», тест подстановки.
- Sidney Dekker. Just Culture: Balancing Safety and Accountability, 2nd ed., 2012 — где проходит граница между ошибкой и безрассудством.
- James Reason. Human error: models and management, BMJ 320, 2000 — латентные условия и модель швейцарского сыра.
- Baruch Fischhoff. Hindsight ≠ foresight, JEP: HPP 1(3), 1975 — послезнание как измеренный эффект.
- Diane Vaughan. The Challenger Launch Decision, University of Chicago Press, 1996 — нормализация отклонения.
- Nancy Leveson. Engineering a Safer World, MIT Press, 2011 — STAMP, системный взгляд на безопасность вместо цепочек причин.
- NASA. Aviation Safety Reporting System — работающий пример добровольной отчётности с иммунитетом.
- PagerDuty. Postmortem documentation — открытый процесс, шаблоны и распределение ролей.
- VOID: Verica Open Incident Database — публичные разборы и данные против метрик вроде MTTR.
- danluu/post-mortems — крупнейшая коллекция публичных разборов.
- GitLab. Postmortem of database outage of January 31, 2017.
- Cloudflare. Details of the Cloudflare outage on July 2, 2019.
- Amazon. Summary of the Amazon S3 Service Disruption in the Northern Virginia Region, 2017.
- NIOSH. Hierarchy of Controls — рамка силы защитных мер, из которой заимствована классификация правок.
Что дальше
Разбор даёт список правок, и заметная их часть звучит одинаково: «у нас не было запаса». Пул соединений, воркеры, диск, пропускная способность — инциденты насыщения составляют львиную долю разборов в любой растущей системе, и лечатся они не после, а до: измерением, тестами и осознанно оплаченным запасом.