Files
progcode/editorial/agent-rewrites/073.json
T
huncode 2d914b543f
Build and deploy / deploy (push) Failing after 15s
Publish rewritten technical article archive
2026-08-02 22:19:34 +03:00

8 lines
18 KiB
JSON

{
"index": 73,
"slug": "editorial-2025-12-field-year-synthesis",
"title": "Как проверить годовой инженерный вывод: контрфакты, цена и граница сравнения",
"excerpt": "Годовой вывод часто превращает событие после изменения в доказательство его пользы. Разбираем, как отделить решение от наблюдения, назвать альтернативу и остановить вывод там, где данных недостаточно.",
"contentHtml": "<p>В годовом отчёте появляется знакомая связка: команда выбрала решение, после него метрика изменилась, значит решение сработало. Через несколько месяцев такой вывод начинают повторять как рецепт. Симптом ошибки прост: в записи есть выбранный путь и хороший результат, но нет отвергнутых вариантов, цены выбора и границы сравнения. Цена ошибки — неверный выбор в следующем проекте. Команда переносит не проверенный механизм, а удачную последовательность событий.</p>\n<p>Тезис статьи: годовой инженерный вывод готов только тогда, когда он различает decision, observation и causal claim. Для этого нужно назвать доступную альтернативу, зафиксировать cost, сформулировать unknown и указать, какие состояния действительно сравнивались. Если хотя бы одного элемента нет, результатом должен быть запрос на исправление, а не уверенный итог.</p>\n<h2>Почему порядок событий не доказывает причину</h2>\n<p>Пусть команда изменила лимит очереди в понедельник, а во вторник снизилась задержка. Запись подтверждает порядок событий. Она не показывает, что произошло бы без изменения. В этот же период могли измениться объём трафика, состав запросов, кэш, версия зависимости или нагрузка на соседний сервис.</p>\n<p>Наблюдение отвечает на вопрос «что увидели». Причинное утверждение отвечает на другой вопрос: «что именно вызвало это изменение». Между ними нужен способ сравнения. Им может быть контрольное окно, сопоставимая группа, повторяемый эксперимент или другая методика, которую команда заранее описала. Если такого способа нет, нужно сохранить unknown, а не заменить его глаголом «улучшило».</p>\n<p>Контрфактический вопрос не требует придумывать альтернативную историю. Он проверяет границу утверждения: какой фактор мог дать тот же результат, какое состояние служит сравнением и что нельзя узнать из текущей записи. Такой вопрос делает текст менее эффектным, но более переносимым.</p>\n<h2>Шесть полей для одной точки года</h2>\n<p><strong>Decision</strong> описывает выбор в конкретный момент. В нём есть действие и контекст: например, «оставили один владелец повторных запросов для внешнего вызова». Фраза «сделали систему надёжнее» уже описывает предполагаемый результат и не подходит.</p>\n<p><strong>Alternatives</strong> перечисляет варианты, доступные тогда же. Нельзя добавлять идеальный вариант, появившийся только после инцидента. Если команда выбирала между локальным повтором и повтором на границе клиента, нужно назвать именно эти варианты и требования, по которым их сравнивали.</p>\n<p><strong>Cost</strong> показывает, чем заплатили за выбор. Это может быть дополнительная задержка, сложность отката, неполное покрытие, расход памяти или ручная операция. Без cost решение выглядит бесплатным и неизбежным.</p>\n<p><strong>Observation</strong> фиксирует наблюдаемый факт: число попыток в учебном сценарии, значение поля, порядок событий или статус проверки. Не называйте его эффектом. Слово «снизило» уже содержит причинный вывод.</p>\n<p><strong>Unknown</strong> называет первый вопрос, на который запись не отвечает. Например: «неизвестно, как повёл бы себя тот же поток без изменения лимита». Unknown не является дефектом текста. Это честная граница знания.</p>\n<p><strong>Comparison boundary</strong> уточняет набор сравнения: два фиксированных состояния, окно времени, тип нагрузки и исключённые факторы. Без границы нельзя понять, насколько широк вывод.</p>\n<figure><img src='/assets/editorial/2025/year-synthesis-2025-counterfactual-review-loop.svg' alt='Цикл проверки годового инженерного вывода: решение, альтернатива, стоимость, наблюдение, неизвестное и граница сравнения.' loading='lazy' /><figcaption>Цикл возвращает запись к пропущенному полю. Если граница сравнения не определена, проверка останавливается.</figcaption></figure>\n<h2>Симптом → причина → проверка → действие</h2>\n<div class='table-scroll'><table><caption>Диагностика слабого годового вывода</caption><thead><tr><th scope='col'>Симптом</th><th scope='col'>Причина</th><th scope='col'>Проверка</th><th scope='col'>Действие</th></tr></thead><tbody><tr><td>После изменения появилась хорошая метрика</td><td>Порядок событий приняли за причинность</td><td>Найти контрольное состояние или явно записать его отсутствие</td><td>Заменить «изменение улучшило» на observation и добавить unknown</td></tr><tr><td>Выбранный путь выглядит единственным</td><td>Альтернативы вырезали при сокращении отчёта</td><td>Восстановить варианты, доступные в момент решения</td><td>Добавить alternatives и критерии выбора</td></tr><tr><td>Решение описано только как успех</td><td>Cost остался в рабочей переписке</td><td>Проверить задержку, сложность, покрытие и откат</td><td>Назвать принятый расход рядом с decision</td></tr><tr><td>Разные команды спорят о результате</td><td>Они сравнивают разные окна или нагрузки</td><td>Сопоставить период, входы, версии и исключения</td><td>Сузить comparison boundary до проверяемого набора</td></tr><tr><td>Reviewer пишет «не хватает контекста»</td><td>Не назван конкретный разрыв</td><td>Проверить шесть полей по одному</td><td>Вернуть один repair request с ожидаемым дополнением</td></tr></tbody></table></div>\n<h2>Учебная карточка и код проверки</h2>\n<p>Ниже учебный пример. Он работает только с фиксированным объектом в памяти. В нём нет настоящих логов, метрик, тикетов, запросов или производственных данных. Код показывает порядок проверки записи, но не доказывает эффект решения.</p>\n<pre><code>type ReviewCard = {\n decision: string;\n alternatives: string[];\n cost: string;\n observation: string;\n unknown: string;\n comparisonBoundary: string;\n};\n\nfunction inspect(card: ReviewCard) {\n const gaps = Object.entries(card)\n .filter(([, value]) =&gt; value.length === 0)\n .map(([field]) =&gt; field);\n\n if (gaps.length &gt; 0) {\n return { status: 'stop-and-repair', gaps };\n }\n\n return {\n status: 'synthetic-review-handoff',\n note: 'Учебная запись не подтверждает production-эффект',\n };\n}\n\nconst card = {\n decision: 'Оставили один владелец retry',\n alternatives: ['retry на клиенте', 'retry на адаптере'],\n cost: 'Дополнительная задержка перед окончательной ошибкой',\n observation: 'В учебном прогоне выполнено не более двух попыток',\n unknown: 'Неизвестно поведение при другой нагрузке',\n comparisonBoundary: 'Фиксированный сценарий и два заданных входа',\n};\n\nconsole.log(inspect(card).status);\n// synthetic-review-handoff</code></pre>\n<p>Положительный статус означает только полноту учебной карточки. Он не означает, что один владелец retry уменьшил задержку или количество ошибок в настоящей системе. Для production понадобятся реальные входы, наблюдаемая телеметрия, план сравнения и владелец проверки.</p>\n<h2>Порядок полевого прохода</h2>\n<ol><li>Выберите одну запись, которая связывает решение с последующим результатом. Не пытайтесь разбирать весь год одной таблицей.</li><li>Перепишите decision как действие в конкретный момент. Уберите слова «улучшили», «оптимизировали» и другие слова результата.</li><li>Восстановите alternatives. Оставьте только варианты, которые реально были доступны при выборе.</li><li>Назовите cost. Запишите дополнительную работу, риск, задержку, неполное покрытие или сложность отката.</li><li>Отделите observation от интерпретации. Добавьте окно, входы, версию и способ измерения, если они известны.</li><li>Сформулируйте один unknown. Если вопросов десять, выберите первый разрыв, который мешает проверить причинность.</li><li>Определите comparison boundary. Укажите, какие два состояния или периода сопоставлены и что исключено.</li><li>Выберите маршрут. При пропущенном поле остановите запись и верните точный repair request. При заполненных полях передайте только synthetic hand-off на человеческое чтение.</li></ol>\n<h2>Отрицательный путь важнее красивого итога</h2>\n<p>Слабая проверка проходит только по заполненной карточке. Надёжная проверка должна остановиться на пустом поле и не превращать запуск без исключения в успех. Например, если unknown отсутствует, функция не должна подставлять «нет неизвестных». Это не знание, а потеря границы.</p>\n<p>Есть и другой отрицательный путь: reviewer не согласен с выбранной альтернативой, хотя все поля заполнены. Это не обязательно ошибка фактов. Сначала нужно проверить traceability записи. Затем можно отдельно обсуждать trade-off. Нельзя маскировать стратегическое несогласие под «неполный контекст» и нельзя исправлять пропуск данных спором о предпочтениях.</p>\n<p>Если comparison boundary невозможно сформулировать, остановите итоговый вывод. Не расширяйте его словами «в целом», «обычно» или «для системы». Широкая формулировка не заменяет отсутствующее сравнение.</p>\n<h2>Ограничения метода</h2>\n<p>Такая карточка не восстанавливает прошлое идеально. Участники могут не помнить все варианты. Метрики могли собираться с другой семантикой. Внешние изменения могли совпасть по времени. Контрфактический вопрос выявляет эти ограничения, но сам по себе не создаёт контрольную группу.</p>\n<p>Полный проход не нужен для каждого мелкого изменения. Он оправдан там, где запись предлагает повторить решение, объясняет заметное изменение или становится основанием для технического стандарта. Для локальной заметки может хватить decision и границы. Чем дороже ошибочный перенос рецепта, тем полнее должна быть карточка.</p>\n<p>Учебный код также ограничен. Он не читает реальные источники, не проверяет качество метрик, не запускает эксперимент и не создаёт production-решение. Все значения в примере заданы вручную. Их нельзя выдавать за результат измерения.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Запись готова к передаче на человеческое чтение, если она содержит конкретное decision, доступные alternatives, явный cost, наблюдаемый observation, один unknown и точную comparison boundary. Для каждого поля можно указать источник или честно отметить, что это фиксированный учебный литерал. Отсутствующее поле возвращает статус stop-and-repair. Ни один абзац не называет причиной то, что запись лишь наблюдала после изменения.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href='https://www.rfc-editor.org/rfc/rfc7282' target='_blank' rel='noopener noreferrer'>RFC 7282: On Consensus and Humming in the IETF</a> — первичный текст о технических возражениях и компромиссах при выборе. Он не задаёт формат годового отчёта и не доказывает причинность.</li><li><a href='https://csrc.nist.gov/pubs/sp/800/61/r3/final' target='_blank' rel='noopener noreferrer'>NIST SP 800-61 Rev. 3</a> — официальный документ о разборе и улучшении процесса реагирования на инциденты. Здесь используется только общий принцип возвращать наблюдения в последующее улучшение; документ не подтверждает учебный код и не заменяет метод сравнения.</li></ul>"
}