{ "index": 253, "slug": "editorial-2020-12-field-incident-review", "title": "Разбор инцидента: как не принять обход за исправление", "excerpt": "Запрос возвращает blocked, потому что обязательное поле теряется на границе mapper. Разбираем, как отделить факт от гипотезы, проверить обратимый обход и оставить защиту от повторения.", "contentHtml": "

Запрос preview-order возвращает blocked, хотя вход выглядит допустимым. После преобразования объекта пропадает обязательное поле currency. Команда возвращает прежний mapper, получает allowed и закрывает задачу. Цена ошибки проявится позже: новый mapper снова попадёт в путь, поле снова исчезнет, а старый обход уже будут считать исправлением.

\n

Восстановление сервиса и устранение причины — разные события. Временное действие должно иметь условие запуска, ожидаемый результат и путь отмены. Отдельно нужно записать тест или контракт, который не даст ошибке вернуться. Иначе отчёт сохраняет уверенность, но не сохраняет способ проверки.

\n

Граница примера

\n

Рассмотрим только переход от mapper к границе pricing-adapter. Это учебная fixture в памяти. Она не обращается к сети, базе, очереди или реальному API. Вход содержит сумму и не содержит валюту. Новый mapper возвращает объект без currency. Затем preview-order отвечает blocked. Мы проверяем форму рассуждения, а не заявляем production-результат.

\n
const input = { amount: 1000, itemId: 'demo-1' };\\n\\nconst mapped = newMapper(input);\\nif (!mapped.currency) {\\n  return { status: 'blocked', reason: 'currency_missing' };\\n}\\n\\nconst fallback = oldMapper(input);\\nreturn { status: 'allowed', currency: fallback.currency };
\n

Код намеренно короткий. Он показывает место, где исчезает значение. Он не доказывает, что именно mapper стал единственной причиной отказа. Для такого вывода нужны наблюдения до и после границы, а также проверка альтернативных причин.

\n

Механизм: факт, гипотеза, действие, проверка

\n

Наблюдение описывает то, что можно увидеть. «После mapper поле отсутствует» — наблюдение. «Новый mapper не переносит поле» — гипотеза. Она связывает два факта, но остаётся изменяемой. Если поле исчезло раньше, гипотеза не выдержит проверки, а факты останутся полезными.

\n

Действие должно менять одну понятную переменную. В примере это возврат к прежнему mapper на ограниченной ветке. Проверка должна измерять именно это действие: fixture получает тот же вход, возвращает allowed и сохраняет currency. Такая проверка не доказывает исправность всех заказов. Она подтверждает только заданный сценарий.

\n

Профилактика отвечает на другой вопрос: что поймает повторение? Здесь нужен контрактный тест, который отклоняет результат mapper без обязательного поля. Пока тест не написан и не прошёл, профилактика остаётся предложением. Статус planned честнее слова «готово», если изменение ещё не появилось в коде.

\n
Как читать сигнал и выбирать следующий шаг
СимптомПричинаПроверкаДействие
preview-order вернул blockedНарушен контракт обязательного поля или сработала другая ветка отказаСохранить ответ и проверить вход на границе mapperНе менять retry и timeout до локализации причины
После mapper нет currencyНовый mapper мог отбросить полеСравнить вход, результат нового mapper и результат прежнегоСформулировать узкую гипотезу H1
Прежний mapper даёт allowedОбход возвращает известный контракт, но причина не устраненаПовторить fixture на том же входе и проверить сохранение поляОставить обход ограниченным и записать условие отмены
Проверка обхода не прошлаГипотеза неполна или отказ вызван другой границейСобрать новое наблюдение до следующего измененияОтменить вывод и проверить альтернативную ветку
Ошибка возвращается после изменения mapperНет автоматической защиты контрактаЗапустить тест на обязательное поле в точке передачиДобавить контрактный тест и связать его с готовностью
\n

Почему нельзя лечить все симптомы сразу

\n

Увеличение timeout скрывает медленный ответ, но не возвращает пропавшее поле. Дополнительный retry повторяет тот же неверный объект и может умножить побочный эффект. Одновременная правка mapper, retry и timeout стирает причинную связь: если результат изменится, станет непонятно, какая правка помогла.

\n

Это не запрет на retry или timeout. Они уместны, когда наблюдение указывает на временную сетевую ошибку или ограничение времени ответа. Но тогда у решения должны быть собственный trigger и собственная проверка. Инструмент выбирают по наблюдаемому механизму, а не по привычке.

\n
Цикл разбора инцидента: наблюдение, ограниченный обход, проверка и профилактика
Ограниченный обход снижает влияние сейчас. Контрактная проверка снижает риск повторения позже.
\n

Отрицательный путь

\n

Предположим, возврат к прежнему mapper не дал allowed. Это не повод назвать прежний код неисправным. Новый факт говорит лишь о том, что выбранный обход не подтвердил гипотезу. Поле могло отсутствовать уже во входе. Отказ мог зависеть от другой обязательной величины. Вторая проверка должна отличить эти варианты.

\n

Хороший разбор не прячет отрицательный результат. Он сохраняет его рядом с условием, при котором проверка должна была пройти. Так следующий инженер не повторит тот же обход вслепую. Формулировка «A1 не подтвердил H1 на входе E1» полезнее, чем «фикс не сработал»: первая фраза указывает границу нового исследования.

\n

Порядок действий

\n
  1. Назвать один симптом и одну границу. В примере это blocked на переходе к pricing-adapter.
  2. Собрать факты до изменения: вход, результат mapper, ответ и сохранённые поля.
  3. Разделить факт и гипотезу. Не записывать возможную причину как доказанное наблюдение.
  4. Выбрать одно обратимое действие. Указать trigger, ожидаемый результат и условие отмены.
  5. Повторить тот же учебный сценарий или безопасный контролируемый запрос.
  6. Проверить результат только в пределах входа и среды. Не переносить его на неизвестные пути.
  7. Добавить профилактику с отдельным критерием: тест должен падать на результате mapper без currency.
  8. Закрыть работу только после проверки действия и профилактики. Если проверка отрицательна, начать новый цикл с наблюдения.
\n

Ограничения

\n

Fixture не измеряет доступность, нагрузку, время восстановления, права доступа или поведение внешней зависимости. Она не заменяет журнал, мониторинг, резервирование и процедуру отката. Asset на схеме объясняет последовательность, но не является доказательством результата. Учебный код ограничен одним объектом и одним контрактом.

\n

Нельзя объявлять production-инцидент исправленным по одному успешному примеру. В реальной системе нужно подтвердить границу на фактическом запросе, проверить безопасный rollout и посмотреть на отрицательные случаи. Нельзя также превращать разбор в поиск виноватого: смена автора mapper не объясняет, почему контракт оказался без защиты.

\n

Критерий готовности

\n

Работа готова, когда выполнены четыре условия. Симптом воспроизводится на известном входе. Действие меняет только заявленную границу и проходит свою проверку. Отрицательный путь записан и приводит к новой гипотезе, а не к повторению того же обхода. Наконец, автоматическая проверка падает на результате mapper без currency и проходит на корректном результате.

\n

Такой критерий не обещает, что система больше никогда не откажет. Он делает утверждение уже: конкретный контракт виден, действие проверено, а известный способ регрессии получает защиту.

\n

Проверяемые источники

" }