Симптом во время сбоя обычно короткий: один путь перестал завершаться ожидаемым состоянием. Цена поспешного исправления выше, чем кажется. Если сразу увеличить timeout, добавить повтор или переписать соседний модуль, команда получает новый код, но не получает цепочку доказательств. Через месяц похожий симптом возвращается, а в заметке остаётся только фраза «починили». Она не объясняет, что именно наблюдали, почему выбрали это действие и как следующий инженер проверит профилактику.
В декабре 2020 года я бы начал не с большой процедуры и не с роли для каждого человека. Достаточно завести короткую запись разбора и научиться держать в ней пять разных вещей: симптом, наблюдение, гипотезу, действие и проверку. Ниже все значения созданы в памяти Node для учебной фикстуры. Это не журнал, не сетевой вызов, не история реального инцидента и не доказательство работы в боевой среде. Цель уже: не дать видимому симптому превратиться в уверенный, но непроверяемый диагноз.
Сначала останавливаем историю до первой правки
Первое полезное действие — зафиксировать наблюдение до изменения конфигурации или кода. Наблюдение отвечает на вопрос «что именно увидели на границе?». В учебном случае вызов preview-order получил состояние blocked, а затем вход для pricing-adapter оказался без currency. Здесь ещё нет причины. Поле могло исчезнуть в mapper, во входном сценарии или в самом описании контракта. Такая осторожность не замедляет разбор: она не даёт следующему шагу подменить факт удобной версией события.
Временная шкала нужна не для красивого отчёта. Она сохраняет порядок. Сначала есть E1 и E2 — два наблюдения. Потом H1 — гипотеза, которая прямо ссылается на E2. Только после неё A1 — ограниченное действие в учебной ветке, и V1 — проверка этого действия. Если поменять порядок местами, запись станет похожа на объяснение задним числом: решение уже принято, а факт подбирается к нему позже. Для небольшого сервиса или модуля такая дисциплина особенно полезна, потому что один разработчик часто одновременно смотрит код, меняет конфигурацию и пишет комментарий к задаче.
| Элемент | Что в нём фиксируем | Что не пишем вместо него | Следующая граница |
|---|---|---|---|
| Симптом | какой путь дал неожидаемое состояние и какова цена повторения | название предполагаемой причины | выбрать точку наблюдения |
| Наблюдение | значение, место и способ его получения | объяснение, почему значение появилось | сформулировать одну гипотезу |
| Гипотеза | проверяемое предположение со ссылкой на наблюдение | обвинение компонента или человека | поставить малую проверку |
| Действие | что временно изменили и почему это ограничено | заявление, что причина устранена | ожидаемая проверка |
| Профилактика | изменение контракта, теста или документа и его будущая проверка | слово «готово» без критерия | вернуться к очереди изменения |
Цена смешения этих строк практическая. Фраза «mapper сломал заказ» одновременно содержит наблюдение, гипотезу и обвинение, но ни одну часть нельзя проверить. Короткая запись лучше: «E2: после преобразования нет currency; H1: новый mapper не переносит обязательное поле; A1: учебная ветка остаётся на предыдущем mapper; V1: контролируемый вызов снова получает allowed». В ней есть граница, связь и следующий вопрос. Она не обещает, что этого достаточно для любой системы.
Факт должен пережить смену версии гипотезы
Хорошее наблюдение можно перечитать после того, как гипотеза оказалась неверной. Поэтому рядом с ним полезно хранить не пересказ, а маленькое доказательство: вход, ожидаемое значение, фактическое значение и границу. В fixture эта роль у строк evidence. Они намеренно короткие: result.state === "blocked" и mappedInput.currency === undefined. Такой фрагмент не заменяет тест интеграции, но отделяет факт от подписи к нему. Если завтра окажется, что поле убрал не mapper, E2 не надо переписывать. Нужно заменить H1 и поставить новый путь проверки.
// Детерминированная in-memory фикстура из этого revision-модуля.
// Это не журнал, не трафик и не отчёт о боевой системе.
const timeline = [
{ id: 'E1', kind: 'observation', evidence: 'result.state === "blocked"' },
{ id: 'E2', kind: 'observation', evidence: 'mappedInput.currency === undefined' },
{ id: 'H1', kind: 'hypothesis', basedOn: 'E2' },
{ id: 'A1', kind: 'action', basedOn: 'H1' },
{ id: 'V1', kind: 'verification', checksAction: 'A1' },
];
// Каждая ссылка проверяется до публикации revision.
assertIncidentLinks(timeline, decisions, prevention);
Код выше не подключается к HTTP, базе или очереди. Он делает более скромную работу: проверяет, что ссылка из гипотезы ведёт к раннему наблюдению, действие следует за гипотезой, а проверка относится к действию. Это полезный тип fixture для текста о процессе. Когда запись меняют в будущем, модуль не позволит тихо переставить V1 перед A1 или добавить профилактику без критерия. Получается не имитация чужой инфраструктуры, а контракт на порядок рассуждения.
Решение записываем как проверяемую ставку
Действие в разборе не обязано быть окончательным исправлением. Иногда задача действия — остановить дальнейшее ухудшение или вернуть систему к известному варианту, пока причина ещё проверяется. Но у действия должен быть trigger и ожидаемый check. В D1 trigger — H1, а expected check — V1. Благодаря этому нельзя написать «вернули старый mapper, стало лучше» без пояснения, что именно проверялось. Если проверка не проходит, действие не превращается в истину: его отменяют или меняют гипотезу.
const decision = {
id: 'D1',
trigger: 'H1',
action: 'оставить старый mapper в учебной ветке',
expectedCheck: 'V1',
};
// Запрещённый вывод: «mapper виноват».
// Проверяемый вывод: H1 основана на E2 и ожидает V1.
В примере специально не выбираются retry и timeout. Повтор мог бы скрыть обязательное поле, а увеличение времени никак не проверяет контракт pricing-adapter. Это не запрет на такие меры. Это порядок: сначала назвать наблюдаемую границу, затем проверить, относится ли мера к ней. Если новое наблюдение покажет временный отказ зависимости, запись станет другой: появится другая гипотеза, другое действие и другой expected check. Технический разбор ценен именно тем, что допускает изменение решения без подмены уже записанного факта.
Без поиска виноватого — не значит без технической ответственности
Фактологичный разбор не пишет «кто допустил ошибку», потому что такая строка редко помогает следующему изменению. Вместо неё он спрашивает: какая граница не показала обязательное поле; какая проверка отсутствовала; почему временное решение было разумным с доступной информацией; какой контракт снизит шанс повторения. Это не попытка смягчить проблему. Наоборот, техническая запись становится точнее: она обязана назвать компонент, поле, действие и контрольную проверку, но не приписывает намерения или знания людям, которых в fixture вообще нет.
Здесь важно не превратить принцип в набор мягких слов. Если mapper действительно отбрасывает currency, это надо написать как условие кода и закрепить тестом. Если документация не называет поле обязательным, это тоже надо зафиксировать. Если решение было временным, рядом остаётся его граница и срок следующей проверки. «Не искать виноватого» означает не пропускать причинные условия. Оно не означает скрывать неверный контракт или откладывать неудобную профилактику.
Маршрут для следующего разбора
- Записать симптом без диагноза: какой путь дал неожидаемое состояние и что стоит повторение этой ошибки для читателя, пользователя или команды.
- Снять одно или два наблюдения до правки. У каждого указать границу, вход или значение и способ получить его снова.
- Сформулировать одну гипотезу с явной ссылкой на наблюдение. Не писать причину в форме факта, пока нет отдельной проверки.
- Выбрать ограниченное действие. Записать, что оно меняет, чего не меняет и при каких условиях его нужно отменить.
- Назвать expected check: какой контролируемый сценарий или автоматическая проверка подтвердит именно это действие.
- Сформировать профилактику отдельно от обхода. В ней должен быть конкретный тест, контракт или документ и признак будущего выполнения.
- Перед публикацией перечитать временную шкалу сверху вниз: любой вывод должен ссылаться на предыдущее наблюдение, а любое «готово» — на проверку.
Граница учебного примера и источники метода
Эта статья не строит процесс для крупной организации и не обещает, что пять строк заменят коммуникацию, резервные каналы или отдельные меры безопасности. Наша фикстура не запускает реальный запрос, proxy, хранилище, развёртывание или мониторинг. Она не измеряет время восстановления и не назначает числовую норму. Её можно взять как минимальный каркас для локального модуля, а затем проверить на реальном стенде и в правилах конкретной команды.
Источники ниже важны именно как рамка. Глава Google о разборе инцидентов показывает ценность живого состояния и разделения работ; глава о postmortem — ценность записи причин и follow-up без обвинения. NIST описывает более широкий цикл обработки security-инцидентов, поэтому его нельзя механически переносить на любой баг. В учебном материале остаётся общий, проверяемый принцип: собрать наблюдения, ограничить действие, проверить эффект и оставить конкретную профилактику.
Проверяемые источники
- Google SRE Book: Managing Incidents — официальная глава 2017 года о фиксации живого состояния инцидента, разделении работ и восстановлении; это источник принципов, а не описание данного учебного случая
- Google SRE Book: Postmortem Culture — официальное описание postmortem как записи влияния, действий, причин и follow-up; особенно полезно различение системных причин и персонального обвинения
- NIST SP 800-61 Rev. 2: Computer Security Incident Handling Guide — первичный нормативный источник для security-инцидентов; здесь используется только его общий цикл подготовки, обнаружения, анализа, сдерживания и извлечения уроков