{ "index": 253, "slug": "editorial-2020-12-field-incident-review", "title": "Разбор инцидента: как не принять обход за исправление", "excerpt": "Запрос возвращает blocked, потому что обязательное поле теряется на границе mapper. Разбираем, как отделить факт от гипотезы, проверить обратимый обход и оставить защиту от повторения.", "contentHtml": "
Запрос preview-order возвращает blocked, хотя вход выглядит допустимым. После преобразования объекта пропадает обязательное поле currency. Команда возвращает прежний mapper, получает allowed и закрывает задачу. Цена ошибки проявится позже: новый mapper снова попадёт в путь, поле снова исчезнет, а старый обход уже будут считать исправлением.
Восстановление сервиса и устранение причины — разные события. Временное действие должно иметь условие запуска, ожидаемый результат и путь отмены. Отдельно нужно записать тест или контракт, который не даст ошибке вернуться. Иначе отчёт сохраняет уверенность, но не сохраняет способ проверки.
\nРассмотрим только переход от mapper к границе pricing-adapter. Это учебная fixture в памяти. Она не обращается к сети, базе, очереди или реальному API. Вход содержит сумму и не содержит валюту. Новый mapper возвращает объект без currency. Затем preview-order отвечает blocked. Мы проверяем форму рассуждения, а не заявляем production-результат.
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Наблюдение описывает то, что можно увидеть. «После mapper поле отсутствует» — наблюдение. «Новый mapper не переносит поле» — гипотеза. Она связывает два факта, но остаётся изменяемой. Если поле исчезло раньше, гипотеза не выдержит проверки, а факты останутся полезными.
\nДействие должно менять одну понятную переменную. В примере это возврат к прежнему mapper на ограниченной ветке. Проверка должна измерять именно это действие: fixture получает тот же вход, возвращает allowed и сохраняет currency. Такая проверка не доказывает исправность всех заказов. Она подтверждает только заданный сценарий.
Профилактика отвечает на другой вопрос: что поймает повторение? Здесь нужен контрактный тест, который отклоняет результат mapper без обязательного поля. Пока тест не написан и не прошёл, профилактика остаётся предложением. Статус planned честнее слова «готово», если изменение ещё не появилось в коде.
| Симптом | Причина | Проверка | Действие |
|---|---|---|---|
preview-order вернул blocked | Нарушен контракт обязательного поля или сработала другая ветка отказа | Сохранить ответ и проверить вход на границе mapper | Не менять retry и timeout до локализации причины |
После mapper нет currency | Новый mapper мог отбросить поле | Сравнить вход, результат нового mapper и результат прежнего | Сформулировать узкую гипотезу H1 |
Прежний mapper даёт allowed | Обход возвращает известный контракт, но причина не устранена | Повторить fixture на том же входе и проверить сохранение поля | Оставить обход ограниченным и записать условие отмены |
| Проверка обхода не прошла | Гипотеза неполна или отказ вызван другой границей | Собрать новое наблюдение до следующего изменения | Отменить вывод и проверить альтернативную ветку |
| Ошибка возвращается после изменения mapper | Нет автоматической защиты контракта | Запустить тест на обязательное поле в точке передачи | Добавить контрактный тест и связать его с готовностью |
Увеличение timeout скрывает медленный ответ, но не возвращает пропавшее поле. Дополнительный retry повторяет тот же неверный объект и может умножить побочный эффект. Одновременная правка mapper, retry и timeout стирает причинную связь: если результат изменится, станет непонятно, какая правка помогла.
\nЭто не запрет на retry или timeout. Они уместны, когда наблюдение указывает на временную сетевую ошибку или ограничение времени ответа. Но тогда у решения должны быть собственный trigger и собственная проверка. Инструмент выбирают по наблюдаемому механизму, а не по привычке.
\nПредположим, возврат к прежнему mapper не дал allowed. Это не повод назвать прежний код неисправным. Новый факт говорит лишь о том, что выбранный обход не подтвердил гипотезу. Поле могло отсутствовать уже во входе. Отказ мог зависеть от другой обязательной величины. Вторая проверка должна отличить эти варианты.
Хороший разбор не прячет отрицательный результат. Он сохраняет его рядом с условием, при котором проверка должна была пройти. Так следующий инженер не повторит тот же обход вслепую. Формулировка «A1 не подтвердил H1 на входе E1» полезнее, чем «фикс не сработал»: первая фраза указывает границу нового исследования.
\nblocked на переходе к pricing-adapter.currency.Fixture не измеряет доступность, нагрузку, время восстановления, права доступа или поведение внешней зависимости. Она не заменяет журнал, мониторинг, резервирование и процедуру отката. Asset на схеме объясняет последовательность, но не является доказательством результата. Учебный код ограничен одним объектом и одним контрактом.
\nНельзя объявлять production-инцидент исправленным по одному успешному примеру. В реальной системе нужно подтвердить границу на фактическом запросе, проверить безопасный rollout и посмотреть на отрицательные случаи. Нельзя также превращать разбор в поиск виноватого: смена автора mapper не объясняет, почему контракт оказался без защиты.
\nРабота готова, когда выполнены четыре условия. Симптом воспроизводится на известном входе. Действие меняет только заявленную границу и проходит свою проверку. Отрицательный путь записан и приводит к новой гипотезе, а не к повторению того же обхода. Наконец, автоматическая проверка падает на результате mapper без currency и проходит на корректном результате.
Такой критерий не обещает, что система больше никогда не откажет. Он делает утверждение уже: конкретный контракт виден, действие проверено, а известный способ регрессии получает защиту.
\n