Files
progcode/editorial/agent-rewrites/045.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
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"index": 45,
"slug": "editorial-2026-10-practice-code-review-standard",
"title": "Code review без шума: как проверить риск изменения",
"excerpt": "Практический стандарт для code review: сначала назвать границу изменения и цену ошибки, затем запросить нужное доказательство и остановиться, если сильный вывод пока не подтверждён.",
"contentHtml": "<p>В pull request может быть двадцать комментариев, но ни одного вопроса о данных, миграции или отказе. Reviewer исправляет имя переменной и форматирование. В это время изменение меняет nullable-поле, порядок переходов состояния или правило доступа. Симптом виден сразу: обсуждение длинное, а главный риск не назван. Цена ошибки — несовместимый потребитель, повторная операция после timeout или доступ к данным не той роли. Такой дефект часто обнаруживается уже после слияния, когда исправление требует обратной миграции или ручного восстановления.</p>\n<p>Code review снижает риск только тогда, когда связывает границу изменения с проверяемым доказательством. Комментарий должен отвечать на четыре вопроса: что меняется, чем это опасно, какой факт сузит неопределённость и какое действие допустимо сейчас. Если факта не хватает, reviewer должен остановить вывод, а не заполнять пробел догадкой.</p>\n<h2>Тезис: сначала граница, потом замечание</h2>\n<p>Разделите изменение на риск-классы. <em>Contract risk</em> возникает, когда меняется форма данных или ожидание потребителя. <em>Operational risk</em> появляется при изменении timeout, retry, состояния, очереди или наблюдаемости. <em>Security risk</em> затрагивает доверенную сторону, правило входа и последствие злоупотребления. <em>Style-only</em> ограничен читаемостью и не меняет поведения.</p>\n<p>Класс риска не равен severity. Он выбирает первый вопрос. Для изменения контракта нужен schema delta, карта consumers и путь возврата. Для изменения поведения нужны переходы состояния и failure mode. Для границы доступа нужна модель доверия и проверка запрещённого входа. Для локального стиля достаточно короткого объяснения, почему код станет понятнее.</p>\n<figure><img src=\"/assets/editorial/2026/code-review-standard-2026-decision-evidence-matrix.svg\" alt=\"Матрица code review связывает класс риска, нужное доказательство, допустимый вывод и условие остановки\" loading=\"lazy\" /><figcaption>Существующая схема помогает выбрать вопрос к изменению. Она не заменяет запуск тестов и не выдаёт вердикт о конкретном pull request.</figcaption></figure>\n<h2>Механизм: evidence ограничивает силу вывода</h2>\n<p>Evidence — это именованный факт, который другой инженер может проверить в пределах задачи. Ссылка на файл не всегда является evidence. Три изменённых файла показывают объём diff, но не доказывают, что перечислены все потребители. Тест с зелёным статусом показывает проход конкретного сценария, но не объясняет, что произойдёт при повторе после отказа.</p>\n<p>Свяжите каждый факт с вопросом. <code>schemaDelta</code> отвечает, какое поле изменилось. <code>consumerMap</code> показывает, кто читает старую форму. <code>rollbackNote</code> описывает, что происходит при возврате. <code>failureMode</code> задаёт отрицательный путь. Такая связь важнее количества ссылок: один точный артефакт может закрыть вопрос, а десять общих ссылок — нет.</p>\n<p>Reviewer не обязан принимать формулу «это только рефакторинг». Попросите назвать invariant — свойство, которое не должно измениться, — и способ его проверить. Если invariant не назван, scope остаётся гипотезой. Положительный вывод не открывается.</p>\n<h2>Учебный пример: карточка риска</h2>\n<p>Ниже — учебный пример на TypeScript. Он не читает репозиторий и не утверждает результат настоящего review. Функция проверяет только полноту входной карточки. Её задача — не найти дефект автоматически, а не дать написать «можно одобрять», когда отсутствует обязательная граница.</p>\n<pre><code>type Risk = 'contract' | 'operational' | 'security' | 'style-only';\n\ntype ReviewCard = {\n risk: Risk;\n evidence: {\n changeBoundary: string;\n question: string;\n verification: string;\n };\n requestedAction: 'comment' | 'stop' | 'handoff';\n};\n\nfunction assess(card: ReviewCard): string {\n const required = [\n card.evidence.changeBoundary,\n card.evidence.question,\n card.evidence.verification\n ];\n\n if (required.some((item) =&gt; item.trim() === '')) {\n return 'stop-missing-evidence';\n }\n\n if (card.risk === 'contract' &amp;&amp;\n card.requestedAction === 'handoff') {\n return 'stop-contract-needs-consumer-map';\n }\n\n return card.requestedAction === 'stop'\n ? 'stop-review-question'\n : 'review-question-ready';\n}\n\nconst card: ReviewCard = {\n risk: 'contract',\n evidence: {\n changeBoundary: 'discount is now nullable',\n question: 'which consumers handle null?',\n verification: 'trace each consumer and add the compatibility case'\n },\n requestedAction: 'stop'\n};\n\nconsole.log(assess(card));\n// stop-review-question</code></pre>\n<p>В примере статус описывает следующий разговор, а не качество кода. Если reviewer не видит карту потребителей, он возвращает <code>stop-contract-needs-consumer-map</code>. Это отрицательный путь. Он полезнее общего комментария «нужно больше тестов», потому что называет недостающий факт и действие. Если карта полна, это всё равно не доказывает совместимость: нужно проверить перечисленные consumers и их обработку <code>null</code>.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<div class=\"table-scroll\"><table><caption>Диагностика замечаний в code review</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>Вынести риск в отдельный комментарий</td></tr><tr><td>«Все клиенты совместимы» без списка</td><td>Вывод подменил карту потребителей</td><td>Найти владельцев и места чтения старой формы</td><td>Остановить вывод и запросить consumer map</td></tr><tr><td>Тест зелёный, но retry не описан</td><td>Проверен happy path</td><td>Проследить переход после timeout и повторной попытки</td><td>Добавить failure case или оставить stop</td></tr><tr><td>«Это только рефакторинг»</td><td>Не назван invariant</td><td>Сравнить вход, выход и побочные эффекты до и после</td><td>Попросить invariant и способ проверки</td></tr><tr><td>Комментарий звучит как приказ, но не объясняет риск</td><td>Нормативное слово заменило аргумент</td><td>Спросить, какое свойство защищает требование</td><td>Переписать комментарий через факт и действие</td></tr></tbody></table></div>\n<h2>Как писать сильный комментарий</h2>\n<p>Начните с наблюдаемого факта. «Поле <code>discount</code> стало nullable» точнее, чем «изменение опасное». Затем назовите последствие: «клиент, который распаковывает значение без проверки, получит ошибку». После этого укажите проверку: «найдите все consumers старой схемы и покажите обработку <code>null</code>». Завершите действием: «до этой проверки не делаем вывод о совместимости».</p>\n<p>Один комментарий должен вести к одному действию. Не смешивайте обязательный вопрос о контракте с необязательным предложением переименовать функцию. Метка <code>request-contract-evidence</code> говорит о границе данных. Метка <code>style-note</code> говорит о читаемости. Автор может ответить на них разными изменениями и не потеряет важный риск среди косметических правок.</p>\n<p>Для security и эксплуатации требуйте владельца вопроса, если сами не можете проверить границу. Reviewer может заметить, что endpoint принимает роль из тела запроса, но не должен объявлять всю модель доступа безопасной без контекста авторизации. Точный комментарий выглядит так: «Роль приходит из недоверенного входа. Где сервер связывает её с authenticated user? Нужен путь проверки отрицательного случая». Это уже проверяемый вопрос.</p>\n<h2>Порядок действий</h2>\n<ol><li>Опишите симптом и цену ошибки одним предложением.</li><li>Найдите границу изменения: данные, состояние, доступ, наблюдаемость или только стиль.</li><li>Назовите риск-класс и invariant, который должен сохраниться.</li><li>Сформулируйте один вопрос, ответ на который изменит решение.</li><li>Запросите минимальное evidence: schema delta, consumer map, failure mode, access rule или другой конкретный артефакт.</li><li>Проверьте отрицательный путь, а не только успешный сценарий.</li><li>Разделите обязательное исправление, уточняющий вопрос и необязательную style-note.</li><li>Если evidence отсутствует, верните точный stop без предположения о причине.</li><li>Если evidence есть, сделайте только тот вывод, который оно поддерживает; совместимость, approval и выпуск проверяются отдельно.</li></ol>\n<h2>Ограничения стандарта</h2>\n<p>Матрица не заменяет тестирование, threat model, дизайн-документ, миграционный план или наблюдаемость. Она не перечисляет всех возможных рисков и не назначает единственный порядок приоритетов. В маленьком style-only изменении запрос consumer map создаст ритуал без пользы. Поэтому классификация тоже должна опираться на invariant и границу поведения.</p>\n<p>Даже полная карта потребителей не доказывает, что каждый путь проверен. Она показывает область поиска. Результат зависит от статического анализа, динамической маршрутизации, конфигурации и скрытых интеграций. Если список получен неполным способом, так и напишите. Честный stop лучше уверенного «совместимо».</p>\n<p>Стандарт также не решает спор о продуктовой цели. Изменение может быть технически аккуратным, но не соответствовать требованиям продукта или политики безопасности. В таком случае reviewer фиксирует технические факты и передаёт вопрос владельцу решения. Code review не превращает полномочия reviewer в полномочия архитектора или владельца риска.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Review-вопрос готов, если другой инженер может быстро назвать границу изменения, цену ошибки, нужное evidence, отрицательный путь и следующее действие. В тексте нет вывода сильнее, чем подтверждающие факты. Для каждого обязательного замечания указан владелец проверки или понятный способ её выполнить. Косметический комментарий не маскирует контрактный, эксплуатационный или security-риск.</p>\n<p>Проверьте это на одной карточке. Если читатель не может ответить, какой факт переведёт stop в следующий шаг, карточка не готова. Если ответ есть, это ещё не разрешение на слияние. Это только ясная граница между тем, что уже видно, и тем, что нужно проверить.</p>\n<h2>Проверяемые источники</h2><ul><li><a href=\"https://google.github.io/eng-practices/review/reviewer/standard.html\" target=\"_blank\" rel=\"noopener noreferrer\">Google Engineering Practices: The Standard of Code Review</a> — официальный документ о балансе качества кода и продвижения изменений.</li><li><a href=\"https://google.github.io/eng-practices/review/reviewer/looking-for.html\" target=\"_blank\" rel=\"noopener noreferrer\">Google Engineering Practices: What to look for in a code review</a> — официальный перечень областей проверки: дизайн, поведение, сложность, тесты и документация.</li><li><a href=\"https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Docs: About pull request reviews</a> — официальное описание комментариев, approvals и запроса изменений в pull request.</li></ul>"
}