Files
progcode/editorial/agent-rewrites/045.json
T

8 lines
20 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>Хорошее review не обязано находить все дефекты и не обещает идеальный код. Его задача уже выполнена, если оно связывает наблюдаемый факт с границей изменения, проверкой и допустимым действием. Если факта не хватает, reviewer ограничивает вывод: не пишет «совместимо» или «безопасно», а называет, какой вопрос ещё требуется закрыть.</p>\n<h2>Что именно проверяет review</h2>\n<p>Начните с цели изменения, а не с первой строки diff. Что должен получить пользователь, потребитель API или оператор? Затем проверьте дизайн, поведение, сложность, тесты, имена, комментарии, стиль и документацию. Такой порядок совпадает с областями, перечисленными в руководстве Google Engineering Practices, но он не является универсальной политикой: команда может добавить свои требования к миграциям, данным или доступам.</p>\n<p>Разделите обязательное и необязательное. Нарушенная граница контракта — причина для точного вопроса или остановки. Неудачное имя, которое не меняет смысл и не противоречит style guide, — отдельная рекомендация. Когда оба типа замечаний лежат в одном списке без меток, автор тратит внимание на косметику, а дорогой риск выглядит равным запятой.</p>\n<h2>Сначала назовите границу изменения</h2>\n<p>У любого diff есть граница, через которую меняется ожидание другой части системы. Для контракта это форма JSON, тип поля, код ошибки или порядок вызовов. Для поведения во времени — состояния, timeout, retry и побочный эффект. Для доступа — доверенная сторона, входное правило и запрещённый результат. Для style-only изменения граница остаётся локальной: меняется читаемость, но не наблюдаемое поведение.</p>\n<p>Класс риска выбирает следующий вопрос. При nullable-поле сравните старую и новую форму, найдите потребителей и опишите переход. При retry покажите состояние до сбоя, событие, состояние после него и результат повтора. При проверке роли отделите значение из запроса от решения, кто имеет право его передать. Фраза «это только рефакторинг» не закрывает проверку: попросите назвать invariant — свойство, которое должно остаться прежним, — и способ его проверить.</p>\n<figure><img src=\"/assets/editorial/2026/frontend-backend-boundary-2026-review-evidence-loop.svg\" alt=\"Последовательность проверки границ code review: intent, request, response и render input; расхождение ведёт к проверке соответствующего контракта\" loading=\"lazy\" /><figcaption>Схема помогает идти по соседним границам: намерение, запрос, ответ и вход рендера. Она не заменяет чтение diff и не доказывает корректность конкретного изменения.</figcaption></figure>\n<h2>Evidence ограничивает силу вывода</h2>\n<p>Evidence — именованный факт, который другой инженер может проверить в рамках задачи. Ссылка на файл показывает место, но не обязательно показывает всех потребителей. Зелёный тест подтверждает один сценарий, но не объясняет повтор после timeout. Три изменённых файла показывают объём diff, но не доказывают, что откат возможен.</p>\n<p>Привяжите каждый риск к минимальному набору доказательств. Для контракта это <code>schemaDelta</code>, <code>consumerMap</code> и <code>rollbackNote</code>. Для поведения — <code>stateTransition</code>, <code>failureMode</code> и граница наблюдения. Для доступа — <code>trustBoundary</code>, правило входа и последствие нарушения. Не просите «проверить всё»: такой запрос нельзя завершить и нельзя воспроизвести.</p>\n<p>Отделяйте факт от вывода. «Поле стало nullable» — факт. «Все клиенты готовы» — вывод, для которого нужна карта клиентов и проверка их обработки <code>null</code>. «Тест прошёл» — факт о запуске. «Повтор безопасен» — более сильное утверждение, которое требует отрицательного сценария. Если набор неполон, корректный результат — stop с конкретным missing evidence.</p>\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>Поле ответа стало nullable</td><td>Контракт</td><td>Сравнить формы и перечислить потребителей старого типа</td><td>Запросить карту потребителей и обработку <code>null</code></td></tr><tr><td>После timeout операция повторяется</td><td>Поведение во времени</td><td>Проследить state до сбоя, повтор и побочный эффект</td><td>Оставить вопрос до проверки идемпотентности</td></tr><tr><td>Роль приходит из тела запроса</td><td>Граница доверия</td><td>Найти серверную связь роли с authenticated user</td><td>Передать узкий вопрос владельцу доступа</td></tr><tr><td>В обсуждении только форматирование</td><td>Риск вытеснен стилем</td><td>Проверить цель, вход, выход и invariant</td><td>Разделить обязательный риск и style-note</td></tr><tr><td>Тест зелёный, но проверен только happy path</td><td>Ложное покрытие</td><td>Сломать условие и убедиться, что тест падает</td><td>Добавить отрицательный сценарий или ограничить вывод</td></tr></tbody></table></div>\n<h2>Воспроизводимый пример: карточка review</h2>\n<p>Ниже — небольшой TypeScript-валидатор. Он не читает репозиторий, не запускает CI и не оценивает production. Функция проверяет только полноту карточки: если для контрактного изменения нет карты потребителей, она возвращает stop. Это полезный механизм для шаблона комментария, но не автоматическое разрешение слияния.</p>\n<pre><code>type Risk = 'contract' | 'operational' | 'security' | 'style-only';\n\ntype ReviewCard = {\n risk: Risk;\n boundary: string;\n question: string;\n evidence: string[];\n action: 'comment' | 'stop' | 'escalate';\n};\n\nfunction assess(card: ReviewCard) {\n const missing = [\n ['boundary', card.boundary],\n ['question', card.question],\n ['evidence', card.evidence.join(', ')]\n ].filter(([, value]) =&gt; value.trim() === '')\n .map(([name]) =&gt; name);\n\n if (missing.length &gt; 0) {\n return { status: 'stop-missing-input', missing };\n }\n\n if (card.risk === 'contract' &amp;&amp;\n !card.evidence.includes('consumerMap')) {\n return { status: 'stop-missing-consumer-map', missing: ['consumerMap'] };\n }\n\n return { status: card.action + '-question-ready', missing: [] };\n}\n\nconsole.log(assess({\n risk: 'contract',\n boundary: 'discount: number -&gt; number | null',\n question: 'which consumers handle null?',\n evidence: ['schemaDelta'],\n action: 'stop'\n}));\n// { status: 'stop-missing-consumer-map', missing: ['consumerMap'] }</code></pre>\n<p>Проверяемое свойство примера узкое: при отсутствии <code>consumerMap</code> функция не сообщает о совместимости. Если добавить карту, результат станет <code>stop-question-ready</code>, потому что в вызове выбрано действие <code>stop</code>. Даже тогда карточка не доказывает, что каждый потребитель действительно обработан. Она лишь делает следующий вопрос явным.</p>\n<h2>Как читать diff по шагам</h2>\n<ol><li>Сформулируйте наблюдаемый симптом и цену ошибки: что изменится для пользователя, потребителя или оператора.</li><li>Найдите границу: данные, состояние, доверие, наблюдаемость или только локальный стиль.</li><li>Сравните старое и новое поведение. Для контракта запишите schema delta, для retry — state transition, для доступа — trust boundary.</li><li>Назовите invariant и отрицательный путь. Примеры: старый клиент не падает, повтор не создаёт дубль, запрещённый вход получает отказ.</li><li>Соберите минимальное evidence и укажите, какой вопрос закрывает каждая позиция.</li><li>Пройдите связанные файлы и тесты в контексте, а не только изменённые строки. Если область проверки неполна, напишите это прямо.</li><li>Сформулируйте одно действие: исправить, показать evidence, уточнить у владельца или остановить вывод.</li><li>После ответа автора проверьте именно заявленное свойство. Зелёный CI не отменяет ручную проверку контракта, границы доступа или поведения при повторе.</li></ol>\n<h2>Как писать комментарий, который помогает</h2>\n<p>Начните с факта: «Поле <code>discount</code> стало nullable». Затем назовите последствие: «старый потребитель может распаковать значение без проверки». Дайте проверку: «покажите список потребителей и тест обработки <code>null</code>». Завершите границей вывода: «до этого нельзя утверждать совместимость». В таком комментарии есть наблюдение, причина запроса и следующее действие.</p>\n<p>Один комментарий — одно решение. Обязательное замечание о контракте не прячьте среди предложения переименовать функцию. Для локального стиля используйте явную метку вроде <code>Nit</code> или «необязательно», если это соответствует правилам вашей системы review. В руководстве Google такая маркировка отделяет пожелание от требования; это снижает риск, что автор примет личное предпочтение за блокирующее условие.</p>\n<p>Если вопрос требует другой компетенции, передайте его владельцу границы, но не приписывайте ему диагноз. Для безопасности это может быть вопрос о модели доверия, для эксплуатации — о повторе побочного эффекта, для контракта — о совместимости потребителей. Передача должна содержать факты, точный вопрос и известное ограничение. Approval, merge и выпуск — отдельные решения, а не следствие одной заполненной карточки.</p>\n<h2>Ограничения применимости</h2>\n<p>Эта схема не заменяет дизайн-документ, threat model, контрактные тесты, нагрузочную проверку, аудит доступа или план миграции. Она не доказывает отсутствие уязвимости и не считает severity. Её функция скромнее: не позволить выводу стать сильнее доступных фактов.</p>\n<p>Для style-only изменения карта потребителей создаст лишнюю процедуру, если граница поведения действительно доказанно не меняется. Для финансовой операции потребуются дополнительные строки об идемпотентности, аудите и сверке. Для публичного API важны версия, период совместимости и план удаления старой формы. Для персональных данных добавьте права, срок хранения и путь удаления. Расширяйте матрицу только теми условиями, которые принадлежат конкретному риску.</p>\n<p>Есть и предел статического review. Скрытая динамическая маршрутизация, конфигурация, внешняя интеграция и race condition могут находиться за пределами доступного diff. В этом случае reviewer фиксирует область, которую проверил, и остаточный вопрос. Честный stop полезнее уверенного «безопасно», если подтверждающего эксперимента ещё нет.</p>\n<h2>Критерий готовности</h2>\n<p>Review-вопрос готов, когда другой инженер без догадок видит симптом, границу, цену ошибки, нужное evidence, отрицательный путь и следующее действие. Для обязательного замечания понятен владелец проверки. Для style-note ясно, что она не блокирует поведение. Для положительного результата указано, что именно проверено и какое утверждение всё ещё запрещено.</p>\n<p>Перед отправкой комментария перечитайте его как короткую карточку: факт → риск → доказательство → действие → ограничение. Если в ней осталось «выглядит хорошо», «всё совместимо» или «тесты зелёные» без конкретного свойства, вернитесь к границе изменения. Это не формальность: так обсуждение остаётся воспроизводимым после смены автора и reviewer.</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> — официальный материал о балансе качества кодовой базы и продвижения изменений; он не является политикой каждой команды и не подтверждает конкретный результат review.</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/how-tos/review-pull-requests\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub Docs: Review pull requests</a> — официальная документация о просмотре commits, file changes и diff, а также о feedback, approval и request changes; интерфейс GitHub не задаёт универсальный процесс review.</li></ul>"
}