{ "index": 45, "slug": "editorial-2026-10-practice-code-review-standard", "title": "Code review без шума: как проверить риск изменения", "excerpt": "Практический стандарт для code review: сначала назвать границу изменения и цену ошибки, затем запросить нужное доказательство и остановиться, если сильный вывод пока не подтверждён.", "contentHtml": "
В pull request может быть двадцать комментариев, но ни одного вопроса о данных, миграции или отказе. Reviewer исправляет имя переменной и форматирование. В это время изменение меняет nullable-поле, порядок переходов состояния или правило доступа. Симптом виден сразу: обсуждение длинное, а главный риск не назван. Цена ошибки — несовместимый потребитель, повторная операция после timeout или доступ к данным не той роли. Такой дефект часто обнаруживается уже после слияния, когда исправление требует обратной миграции или ручного восстановления.
\nCode review снижает риск только тогда, когда связывает границу изменения с проверяемым доказательством. Комментарий должен отвечать на четыре вопроса: что меняется, чем это опасно, какой факт сузит неопределённость и какое действие допустимо сейчас. Если факта не хватает, reviewer должен остановить вывод, а не заполнять пробел догадкой.
\nРазделите изменение на риск-классы. Contract risk возникает, когда меняется форма данных или ожидание потребителя. Operational risk появляется при изменении timeout, retry, состояния, очереди или наблюдаемости. Security risk затрагивает доверенную сторону, правило входа и последствие злоупотребления. Style-only ограничен читаемостью и не меняет поведения.
\nКласс риска не равен severity. Он выбирает первый вопрос. Для изменения контракта нужен schema delta, карта consumers и путь возврата. Для изменения поведения нужны переходы состояния и failure mode. Для границы доступа нужна модель доверия и проверка запрещённого входа. Для локального стиля достаточно короткого объяснения, почему код станет понятнее.
\nEvidence — это именованный факт, который другой инженер может проверить в пределах задачи. Ссылка на файл не всегда является evidence. Три изменённых файла показывают объём diff, но не доказывают, что перечислены все потребители. Тест с зелёным статусом показывает проход конкретного сценария, но не объясняет, что произойдёт при повторе после отказа.
\nСвяжите каждый факт с вопросом. schemaDelta отвечает, какое поле изменилось. consumerMap показывает, кто читает старую форму. rollbackNote описывает, что происходит при возврате. failureMode задаёт отрицательный путь. Такая связь важнее количества ссылок: один точный артефакт может закрыть вопрос, а десять общих ссылок — нет.
Reviewer не обязан принимать формулу «это только рефакторинг». Попросите назвать invariant — свойство, которое не должно измениться, — и способ его проверить. Если invariant не назван, scope остаётся гипотезой. Положительный вывод не открывается.
\nНиже — учебный пример на TypeScript. Он не читает репозиторий и не утверждает результат настоящего review. Функция проверяет только полноту входной карточки. Её задача — не найти дефект автоматически, а не дать написать «можно одобрять», когда отсутствует обязательная граница.
\ntype 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) => item.trim() === '')) {\n return 'stop-missing-evidence';\n }\n\n if (card.risk === 'contract' &&\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\nВ примере статус описывает следующий разговор, а не качество кода. Если reviewer не видит карту потребителей, он возвращает stop-contract-needs-consumer-map. Это отрицательный путь. Он полезнее общего комментария «нужно больше тестов», потому что называет недостающий факт и действие. Если карта полна, это всё равно не доказывает совместимость: нужно проверить перечисленные consumers и их обработку null.
| Симптом | Причина | Проверка | Действие |
|---|---|---|---|
| Много комментариев о стиле, риск не назван | Все замечания получили один приоритет | Отметить, меняется ли поведение или контракт | Вынести риск в отдельный комментарий |
| «Все клиенты совместимы» без списка | Вывод подменил карту потребителей | Найти владельцев и места чтения старой формы | Остановить вывод и запросить consumer map |
| Тест зелёный, но retry не описан | Проверен happy path | Проследить переход после timeout и повторной попытки | Добавить failure case или оставить stop |
| «Это только рефакторинг» | Не назван invariant | Сравнить вход, выход и побочные эффекты до и после | Попросить invariant и способ проверки |
| Комментарий звучит как приказ, но не объясняет риск | Нормативное слово заменило аргумент | Спросить, какое свойство защищает требование | Переписать комментарий через факт и действие |
Начните с наблюдаемого факта. «Поле discount стало nullable» точнее, чем «изменение опасное». Затем назовите последствие: «клиент, который распаковывает значение без проверки, получит ошибку». После этого укажите проверку: «найдите все consumers старой схемы и покажите обработку null». Завершите действием: «до этой проверки не делаем вывод о совместимости».
Один комментарий должен вести к одному действию. Не смешивайте обязательный вопрос о контракте с необязательным предложением переименовать функцию. Метка request-contract-evidence говорит о границе данных. Метка style-note говорит о читаемости. Автор может ответить на них разными изменениями и не потеряет важный риск среди косметических правок.
Для security и эксплуатации требуйте владельца вопроса, если сами не можете проверить границу. Reviewer может заметить, что endpoint принимает роль из тела запроса, но не должен объявлять всю модель доступа безопасной без контекста авторизации. Точный комментарий выглядит так: «Роль приходит из недоверенного входа. Где сервер связывает её с authenticated user? Нужен путь проверки отрицательного случая». Это уже проверяемый вопрос.
\nМатрица не заменяет тестирование, threat model, дизайн-документ, миграционный план или наблюдаемость. Она не перечисляет всех возможных рисков и не назначает единственный порядок приоритетов. В маленьком style-only изменении запрос consumer map создаст ритуал без пользы. Поэтому классификация тоже должна опираться на invariant и границу поведения.
\nДаже полная карта потребителей не доказывает, что каждый путь проверен. Она показывает область поиска. Результат зависит от статического анализа, динамической маршрутизации, конфигурации и скрытых интеграций. Если список получен неполным способом, так и напишите. Честный stop лучше уверенного «совместимо».
\nСтандарт также не решает спор о продуктовой цели. Изменение может быть технически аккуратным, но не соответствовать требованиям продукта или политики безопасности. В таком случае reviewer фиксирует технические факты и передаёт вопрос владельцу решения. Code review не превращает полномочия reviewer в полномочия архитектора или владельца риска.
\nReview-вопрос готов, если другой инженер может быстро назвать границу изменения, цену ошибки, нужное evidence, отрицательный путь и следующее действие. В тексте нет вывода сильнее, чем подтверждающие факты. Для каждого обязательного замечания указан владелец проверки или понятный способ её выполнить. Косметический комментарий не маскирует контрактный, эксплуатационный или security-риск.
\nПроверьте это на одной карточке. Если читатель не может ответить, какой факт переведёт stop в следующий шаг, карточка не готова. Если ответ есть, это ещё не разрешение на слияние. Это только ясная граница между тем, что уже видно, и тем, что нужно проверить.
\n