{"index": 38, "slug": "editorial-2026-12-mechanism-portfolio-case", "title": "Инженерный кейс: как проверить причинность, а не приписать результат изменению", "excerpt": "Если после изменения стало лучше, это ещё не доказывает причинность. Разбираем, как отделить событие от наблюдения, проверить контрфакты и остановить кейс, когда данных недостаточно.", "contentHtml": "
После релиза команда видит знакомый симптом: задержка ответа снизилась, ошибок стало меньше, а в отчёте появляется фраза «изменение дало результат». Через неделю показатель снова меняется. Уже непонятно, помог релиз, закончилась нагрузка, изменился состав трафика или перестал отвечать другой компонент. Цена ошибки — неверное решение на следующем шаге. Команда может закрепить бесполезный код, отменить полезный откат или объявить временный эффект доказанным.
Тезис прост: инженерный кейс доказывает не соседство изменения и результата, а цепочку «вход → механизм → наблюдение → сравнение → вывод». Если хотя бы одно звено отсутствует, текст должен понизить силу утверждения или остановиться. Такой кейс остаётся полезным: он показывает, что известно, чего не хватает и какое наблюдение отличит объяснения.
Событие — действие, которое действительно произошло: например, сервис начал отдавать ответ из локального кеша. Наблюдение — запись с измерением: время ответа, число ошибок, трасса запроса или лог. Вывод — утверждение о связи между ними. Эти три слоя нельзя заменять друг другом.
Фраза «после включения кеша p95 снизился» описывает последовательность. Фраза «кеш снизил p95» уже утверждает причинность. Для второй фразы нужно знать входы, окно измерения, контрольное сравнение и альтернативные причины. OpenTelemetry разделяет traces, metrics и logs именно как разные сигналы: путь запроса, измерение во время работы и запись события. Один сигнал не заменяет остальные.
Не начинайте с красивого итога. Запишите симптом и цену ошибки. Затем зафиксируйте, какое изменение проверяется, какой механизм должен сработать и какое наблюдение его подтвердит. Если механизм нельзя описать одним-двумя предложениями, причинную связь пока рано считать рабочей гипотезой.
Рассмотрим учебный пример. Страница каталога долго ждёт ответ от API. Команда добавляет кеш на пять минут. Ожидаемый механизм таков: повторный запрос с тем же ключом читает локальное значение, не обращается к API и завершает работу быстрее. Это проверяемая гипотеза. Она не равна обещанию, что p95 улучшится во всей системе.
Минимальная модель должна назвать ключ кеша, срок жизни, ветку промаха и измеряемое событие. Нужны также условия, при которых сравнение честно. Если до изменения запросы шли в час пик, а после — ночью, число «до/после» ничего не доказывает. Если одновременно изменился размер ответа, источник трафика или лимит API, у результата появились конкурирующие объяснения.
function assessCase(input) {\n const sameWindow = input.before.window === input.after.window;\n const sameTraffic = input.before.trafficClass === input.after.trafficClass;\n const mechanismObserved = input.after.cacheHits > 0 && input.after.apiCalls < input.before.apiCalls;\n const effectObserved = input.after.p95Ms < input.before.p95Ms;\n\n if (!sameWindow || !sameTraffic) {\n return { status: 'stop', reason: 'comparison-is-not-comparable' };\n }\n if (!mechanismObserved || !effectObserved) {\n return { status: 'stop', reason: 'mechanism-or-effect-is-not-observed' };\n }\n return { status: 'hypothesis-supported', confidence: 'bounded' };\n}Код — учебный пример. Он не измеряет реальный сервис и не даёт production-результата. Его задача — показать отрицательный путь. При несовпоставимых окнах функция возвращает stop, а не достраивает причинность. При отсутствии признака механизма она тоже останавливается. Даже положительный статус остаётся ограниченным: он говорит, что гипотеза согласуется с входом, но не исключает все внешние причины.
Контрфактический вопрос звучит так: «Что должно было бы наблюдаться, если изменение не вызвало эффект?» Для кеша это может быть снижение p95 без роста cache hits, если одновременно упала нагрузка на API. Тогда результат совместим с другим объяснением. Второй вопрос: «Что должно измениться, если механизм работает?» Должны появиться cache hits, уменьшиться обращения к API и сохраниться сравнимый класс трафика.
Контрфакт не требует идеального эксперимента. Он требует назвать альтернативу до интерпретации результата. В зависимости от системы это может быть контрольная группа, поэтапное включение, повторное измерение в том же окне или сравнение запросов с одинаковыми ключами. Если ни один вариант недоступен, вывод становится слабее: «наблюдение совпало с гипотезой», а не «изменение вызвало эффект».
| Симптом | Причина | Проверка | Действие |
|---|---|---|---|
| После релиза p95 ниже | Изменилось окно или распределение трафика | Сравнить время, регион, endpoint и класс нагрузки | Понизить вывод и собрать сопоставимое окно |
| Ошибок меньше, cache hits нет | Сработал внешний fallback или изменился upstream | Проверить traces, логи ошибок и вызовы API | Не приписывать эффект кешу |
| Cache hits есть, API calls не снизились | Ключи расходятся или кеш не участвует в ответе | Сопоставить ключ, TTL и ветку чтения | Исправить механизм или остановить кейс |
| До/после различаются сильно | Одновременно изменились несколько факторов | Составить список изменений и найти контроль | Разделить изменения либо назвать результат неоднозначным |
| Наблюдения неполные | Сигнал не собирался в нужном месте | Проверить покрытие метрик, логов и трасс | Описать пробел, не заполнять его предположением |
Сильный материал не скрывает отвергнутую ветку. Для кеша это может быть увеличение TTL, предварительная загрузка или изменение самого API. Назовите вариант и причину отказа только там, где есть запись. Если решения не было, пишите «рассматривался как учебная альтернатива», а не «команда отвергла его на проверке». Правдоподобная деталь без источника превращает пример в ложное свидетельство.
Разделяйте факты и условия применимости. Факт — в наблюдаемом окне было 120 cache hits. Условие — вывод относится только к запросам с тем же ключом и TTL. Ограничение — холодный кеш, ошибки сериализации и инвалидация не проверены. Такая запись переносима: другой инженер видит, какую часть можно повторить, а какую нельзя переносить без новых данных.
Для риска полезно использовать не одно число, а пару «воздействие × вероятность» и явно отмечать неопределённость. NIST SP 800-30 описывает оценку риска как работу с потенциальным событием, его последствиями и вероятностью, а также рекомендует фиксировать допущения и ограничения. Это не готовая формула для любого продукта. Это дисциплина, которая не даёт спрятать неизвестное за словом «результат».
Наблюдаемая корреляция не доказывает причинность, если система менялась сразу в нескольких местах. Даже контрольная группа может быть нерепрезентативной. Sampling может скрыть редкую ошибку. Метрика может быть правильно собрана, но измерять не тот пользовательский путь. Traces показывают маршрут запроса, но не объясняют бизнес-причину сами по себе. Logs фиксируют события, но без контекста их трудно сопоставить с запросом.
Учебный код также не заменяет нагрузочный тест, проверку инвалидации, анализ стоимости хранения и оценку отказа API. Не называйте его production-проверкой. Если данных нет, корректный результат — остановка с конкретным next action: собрать сигнал, выровнять окно, добавить контроль или отказаться от сильного claim. Отрицательный путь — часть механизма, а не признак незавершённости текста.
Кейс готов, когда читатель может ответить на четыре вопроса без доверия к автору: какой симптом наблюдали и чем грозила ошибка; какое изменение и механизм проверяли; какое сравнение отличает гипотезу от альтернативы; что произойдёт, если данных не хватит. Дополнительно у каждого результата должны быть границы окна, набор сигналов, условия применимости и явно названный остаточный риск.
Если хотя бы на один вопрос отвечает только предположение, кейс не должен переходить в формулировку «изменение улучшило систему». Оставьте более узкий вывод: «наблюдение совместимо с гипотезой при таких условиях». Это не ослабляет инженерную работу. Это сохраняет возможность проверить её снова и не превращает временную удачу в правило.