{ "index": 150, "slug": "editorial-2023-11-practice-postmortem", "title": "Postmortem, который помогает исправить систему", "excerpt": "Как отделить наблюдаемые факты от поздних объяснений, связать решение с доступной информацией и превратить профилактику в проверяемое действие.", "contentHtml": "
После сбоя команда обычно помнит две вещи: какой сигнал сработал и кто последним менял систему. На встрече эти детали быстро превращаются в объяснение: «ошибка произошла из-за этого изменения». Такой вывод может быть неверным. Он смешивает факт, решение и гипотезу о причине.
\nЦена ошибки высока. Команда тратит время на защиту вокруг случайного признака, а настоящий механизм остаётся без проверки. Следующий дежурный получает документ с обвинением и общим советом «быть внимательнее». Похожий сбой повторяется, но его уже труднее разобрать: нужные логи истекли, контекст решения забыт, а исправление объявили готовым без измерения.
\nРабочая схема проста: сначала записать симптом и его цену, затем собрать факты с источниками, отдельно описать решение в моменте и только после этого сформулировать небольшую профилактическую проверку. Postmortem не должен угадывать причину по одной строке лога. Он должен показывать, что известно, чего не известно и какое действие уменьшит неопределённость.
\nФакт отвечает на вопрос «что было зафиксировано и где это видно?». Это узкое утверждение с временем и ссылкой на разрешённый артефакт: лог, trace, метрику, версию конфигурации или запись мониторинга. Фраза «в 10:03 запросы к маршруту получили 502» может быть фактом, если рядом есть запрос к источнику и задано окно времени.
\nРешение отвечает на вопрос «что команда сделала, имея такую информацию?». В него входят время, действие и список фактов, доступных до действия. Поздний trace или результат расследования нельзя добавлять в этот список задним числом. Он помогает понять выбор, но не доказывает, что выбор был правильным или ошибочным.
\nПрофилактическая проверка отвечает на вопрос «что мы проверим, чтобы уменьшить риск повторения?». В ней нужны гипотеза, минимальный метод, бинарный критерий и обратимый путь. До прогона это намерение проверить, а не доказательство того, что новый alert, лимит или тест уже защитил пользователей.
\n| Тип | Вопрос | Минимальные поля | Нельзя выводить |
|---|---|---|---|
| Факт | Что зафиксировано? | время, наблюдение, ссылка на источник | виновника и root cause |
| Решение | Что выбрали тогда? | время, действие, доступные fact ID | оценку задним числом |
| Проверка | Что проверим дальше? | гипотеза, метод, критерий, rollback | реальный эффект до измерения |
| Действие | Кто доведёт работу? | владелец роли, срок, ссылка на проверку | обещание устранить все риски |
Представим учебный инцидент в HTTP-сервисе. После релиза доля ответов 502 выросла. Дежурный откатил конфигурацию таймаута. Через несколько минут доля ошибок снизилась. Этого недостаточно, чтобы написать «новый таймаут был причиной». За это время могли исчезнуть входной всплеск, зависший upstream или другая ошибка маршрутизации.
\nСначала запишите наблюдения:
\nconst facts = [\n { id: 'f-1', at: '10:03', text: 'gateway reported 502 for /checkout', source: 'metric:gateway_5xx' },\n { id: 'f-2', at: '10:04', text: 'timeout config was version 17', source: 'config:checkout@17' },\n { id: 'f-3', at: '10:05', text: 'on-call restored version 16', source: 'change:rollback-482' },\n];\n\nconst decision = {\n at: '10:05',\n action: 'restore checkout config to version 16',\n availableFactIds: ['f-1', 'f-2'],\n};\n\nconst check = {\n hypothesis: 'the timeout change contributes to the 502 path',\n method: 'replay the same request class with versions 16 and 17',\n criterion: 'both outcomes and upstream status are captured',\n rollback: 'keep version 16 and stop the replay if error rate rises',\n};\nПример учебный. Он не читает настоящие метрики, не запускает rollback и не доказывает связь между таймаутом и 502. Его польза в форме: у решения видны только два доступных факта, а проверка имеет отдельный критерий и остановку. Если команда позже найдёт новый trace, его добавят в факты и пересмотрят гипотезу, но не перепишут историю доступной информации.
\nНужна и отрицательная ветка. Если в карточке факта появилось поле rootCause, система или ревью должны остановить запись. Если решение ссылается на факт, который возник позже, его нельзя считать контекстом решения. Если проверка говорит «сбой больше не повторится», но не называет вход, окно и измерение, это обещание, а не критерий.
| Симптом | Вероятная причина смешения | Проверка | Действие |
|---|---|---|---|
| В черновике есть имя инженера, но нет источников | оценка человека заменяет анализ условий | найти timestamp и evidence для каждой фразы | убрать имя из объяснения, добавить владельца следующего действия |
| «Релиз вызвал ошибку» написано как факт | гипотеза попала в timeline | сравнить время релиза, симптома и альтернативные изменения | пометить связь как непроверенную и сформулировать эксперимент |
| Action item звучит как «добавить мониторинг» | нет сценария и порога срабатывания | назвать вход, сигнал, окно и ожидаемое значение | сделать критерий бинарным и указать обратное действие |
| После отката написано «проблема решена» | снижение симптома приняли за доказательство причины | сопоставить ошибку с upstream, версиями и временем | описать откат как mitigation, а причину оставить открытой |
| Документ нельзя проверить через неделю | в нём остались воспоминания без артефактов | проверить каждое утверждение по ссылке и сроку хранения | сохранить минимальный разрешённый evidence или отметить пробел |
Временная граница нужна не для бюрократии. Она защищает от hindsight bias: после сбоя команда видит больше, чем видела в момент действия. Поэтому в записи решения храните не весь итоговый материал, а именно набор сведений, который мог повлиять на выбор. Если действие необратимо, отдельно запишите владельца точки возврата и сигнал остановки.
\nТакая схема не заменяет расследование распределённой системы. Один trace может не показать потерю сообщения. Откат может убрать симптом, но оставить повреждённые данные. Метрика может считать только успешные запросы и скрывать ошибки до входа в сервис. Поэтому границы источников и неполные данные нужно писать прямо.
\nBlameless не означает «никто ни за что не отвечает». Документ должен содержать владельца действия, срок и критерий завершения. Он также должен фиксировать небезопасное изменение, нарушенный контроль или отсутствие доступа к сигналу, если это подтверждено. Не следует приписывать человеку мотив или использовать его имя как техническую причину.
\nУчебный код выше не подключается к сети, CI, логам, alert-системе или production runtime. Его нельзя выдавать за результат прогона. В реальной системе доступ к incident data, приватность, retention и право публикации требуют отдельной проверки. Если источник недоступен, честная запись — «не проверено», а не правдоподобная реконструкция.
\nPostmortem готов к разбору, когда другой инженер может пройти его без устного пересказа:
\nПроверка готовности не утверждает, что система стала надёжнее. Она утверждает более узкую вещь: документ сохраняет границу знания и задаёт следующий эксперимент, который можно проверить. После прогона обновите запись фактическим результатом, источником и новой оценкой риска.
\n