Инженерная практика
Code review: что ловит человек, что должна тулза, и почему латентность — настоящая цена
PR на 1800 строк падает в пятницу в 17:00: новый биллинг-флоу, миграция схемы и рефакторинг — всё одним диффом. Два ревьюера оставляют четыре комментария — переименование, пропущенная точка с запятой, «нит: лучше const» — и аппрувят. Три недели спустя миграция дважды списывает деньги с когорты клиентов, потому что путь ретрая не был идемпотентным. Никто не увидел. Дифф был слишком велик, чтобы его реально прочесть, так что ревьюеры отревьюили то, что легко увидеть: мелочь. Настоящий изъян сидел в дизайне, а дизайн — ровно то, что гигантский LGTM-нутый PR и прячет.
Ревью ловит то, чего не может тулза
Дефицитное внимание сеньора-ревьюера должно идти на то, что машина судить не может: правильный ли это дизайн, делает ли он то, что задумывал тикет, поймёт ли это следующий инженер, корректен ли путь ошибки, не открывает ли он дыру в безопасности. Собственный стандарт Google ставит дизайн первым — «самое важное, что нужно покрыть в ревью», — потому что дизайн-решения накапливаются и дорого откатываются позже, тогда как неуместная скобка не стоит ничего.
Всё, что тулза может решить детерминированно, не должно никогда доходить до человека. Форматирование, порядок импортов, конвенции именования, lint-правила, неиспользуемые переменные — гоняй форматтер и линтер в CI и сделай их блокирующими. В тот момент, когда человек печатает «нит: пробелы», ты потратил самый дорогой ресурс ревью (суждение сеньора) на самую дешёвую возможную проблему, и научил автора, что ревью — про поверхность. Инженерная культура Google проговаривает это явно: автоматизируй принуждение стиля, чтобы стиль не доходил до ревью, потом тренируй ревьюеров тратить внимание на корректность, безопасность и архитектуру.
| Забота | Кто должен поймать | Почему |
|---|---|---|
| Форматирование, порядок импортов, пробелы | Форматтер (CI) | Детерминированно; ноль суждения; не должно доходить до человека |
| Неиспользуемые переменные, очевидные баги, lint-правила | Линтер / статанализ (CI) | Проверяемо машиной; блокирующий гейт до старта ревью |
| Правильный ли это дизайн? | Человек-ревьюер | Нужен контекст, замысел, знание системы — ни одна тулза не рассудит |
| Совпадает ли с замыслом тикета? | Человек-ревьюер | Корректный код, решающий не ту задачу, всё равно шипит баг |
| Поддерживаемость, именование-ради-смысла | Человек-ревьюер | «Поймёт ли это следующий разработчик?» — вопрос суждения |
Латентность — цена, которую никто не меряет
Число, что значит больше всего, — не количество дефектов, а время оборота. PR — это заблокированная работа: автор не может смержить, часто не может чисто начать следующее, и каждый час простоя — это час чьего-то замороженного прогресса. Данные суровы. Анализ примерно 8 миллионов pull request’ов нашёл, что элитные команды закрывают полный цикл ревью меньше чем за 26 часов, тогда как отстающие дают PR простоять неделю до того, как ревью вообще начнётся. Самый большой кусок этого времени — не ревью, а первичный подхват, разрыв до того, как кто-то взглянет. Распределённые команды платят ещё 8–16 часов за раунд просто на разрыв часовых поясов.
У этого простоя есть цена. Тот же анализ оценил стоимость ожидания на бутылочном горлышке ревью примерно в 5,8 часа на разработчика в неделю — около $238K/год замороженного времени для команды из десяти человек, и это только ожидание, не само ревью. Вот почему DORA трактует lead time как ключевую метрику здоровья: медленное ревью напрямую деградирует пропускную способность, и команда, оптимизирующая чисто «тщательное ревью» и игнорящая латентность, меняет малый выигрыш по дефектам на большой проигрыш по потоку.
Почему это работает
Самый быстрый фикс латентности ревью почти никогда не «ревьюить быстрее» — это «делать PR меньше». PR на 50 строк подхватывают за минуты, потому что в него дёшево переключиться; PR на 1500 строк простаивает, потому что ни у кого нет свободного часа. Маленькие PR бьют по обеим проблемам разом: быстрее подхват и выше детект дефектов. Латентность и качество обычно — один рычаг, а не трейдофф.
Экономика маленьких PR: детект дефектов падает с обрыва
Исследование SmartBear в Cisco — 2500 ревью на 3,2 миллиона строк кода, крупнейшее в своём роде — нашло, что сладкая точка — 200–400 строк кода на ревью. Внутри этой полосы, при ревью, растянутом не больше чем на 60–90 минут, ты получаешь около 70%+ выхода дефектов. Перевали за 400 строк, и детект резко падает; человек перестаёт читать и начинает скользить. Темп тоже важен: ревьюеры, идущие медленнее ~400 LOC/час, были выше среднего в нахождении дефектов, но за ~500 LOC/час плотность дефектов оказалась ниже среднего в 87% случаев.
Это механизм за штампом не глядя. Ревьюер перед диффом на 1800 строк не имеет бюджета удержать всё это в голове, так что делает единственное, что влезает: комментит локально видимое (имя, нит по стилю) и аппрувит. Гигантский PR не получает больше придирчивости за то, что большой — он получает меньше, потому что внимание не масштабируется с размером, а когнитивный предел ревьюера фиксирован. Большие диффы — это где прячутся реальные изъяны дизайна ровно потому, что это диффы, которые никто не может прочесть целиком.
| Размер PR | Что реально происходит | Детект дефектов |
|---|---|---|
LOC < 200 | Читается целиком, быстрый подхват, реальное обсуждение дизайна | Высокий |
200–400 LOC | Сладкая точка: 60–90 мин, полное внимание | 70–90% выхода |
400–800 LOC | Внимание истончается; начинается скольжение | Падает |
LOC > 800 | Штамп: ниты + LGTM, дизайн не прочитан | Рушится |
Трейдофф сеньора: тщательность против пропускной, обмен знанием против привратничества
Каждое ревью — ставка на то, куда потратить фиксированный бюджет. Выкрути тщательность на максимум — и затормозишь поток всей команды; оптимизируй чисто под пропускную — и дефекты протекут. Ход сеньора — тратить глубину там, где риск: дизайн, миграция данных, граница auth, путь денег, — и пропускать низкорисковые диффы быстро. Стандарт Google кодифицирует это нарочито гуманным правилом: аппрувь, как только изменение определённо улучшает общее здоровье кода, даже если оно не идеально. «Не идеально» — не повод блокировать; «делает кодбазу хуже» — повод.
Вторая ось культурная: ревью как обмен знанием против ревью как привратничества. Сделанное хорошо, ревью распространяет контекст — автор узнаёт систему, ревьюер узнаёт изменение, и bus factor растёт. Сделанное как статусная игра, оно становится бикешеддингом (закон тривиальности Паркинсона: люди бесконечно спорят о цвете велосарая, потому что это единственная часть, которую они понимают) и привратничеством, где ревьюер блокирует, чтобы утвердить контроль, а не улучшить код. Признак — тред из двенадцати комментариев об именовании на PR, чей настоящий изъян дизайна уехал нетронутым. Ревью, придирающееся к мелочи и пропускающее порочный дизайн, вывернуло наизнанку всю свою цель.
Коллега открывает PR на 1400 строк, сбивший в кучу фичу, рефакторинг и миграцию. Он блокирует его следующую задачу уже два дня. Как ревьюер, каков ход сеньора?
Ревьюер оставляет шесть комментариев о порядке импортов и пробелах в переменных, потом аппрувит. В чём корневая проблема?
По исследованию SmartBear/Cisco, что происходит с детектом дефектов, когда одно ревью растёт за ~400 LOC?
Расставь здоровый пайплайн ревью так, чтобы внимание человека падало туда, где оно ценнее всего:
- 1 Форматтер + линтер гоняются в CI и блокируют — стиль/формат не доходит до человека
- 2 Держи PR маленьким (цель ~200–400 LOC), чтобы читался за один присест
- 3 Подхватывай быстро — первичная латентность — крупнейшая часть оборота
- 4 Трать глубину ревьюера на рискованнейшие части: дизайн, миграция, auth, путь денег
- 5 Аппрувь, как только определённо улучшает здоровье кода — «не идеально» не блок
- 01Коллега говорит: «тщательное ревью значит ловить всё, так что большие ревью должны ловить больше багов». Объясни, почему данные говорят обратное.
- 02Почему латентность ревью, не число дефектов, часто метрика, за которой сеньор следит первой — и каков самый дешёвый способ её улучшить?
Code review окупается на том, что тулза судить не может — дизайн, замысел, поддерживаемость, корректные пути ошибки и денег, — так что выталкивай всё детерминированное (формат, lint, стиль) в блокирующий CI-гейт и никогда не трать на него человека. Цена, которую никто не меряет, — латентность: PR в ревью это заблокированная работа, элитные команды закрывают цикл меньше чем за 26 часов, тогда как отстающие дают ему простоять неделю, и одно ожидание сжигает ~5,8 часа на разработчика в неделю. Детект дефектов пикует на 200–400 LOC и рушится за ~400, и поэтому гигантские PR получают штамп ниты-и-LGTM, пока их настоящие изъяны дизайна уезжают нетронутыми. Баланс сеньора — тратить глубину там, где живёт риск, аппрувить, как только изменение определённо улучшает здоровье кода, а не гнаться за идеалом, и трактовать ревью как обмен знанием, а не статусную игру привратника. Меньшие PR — единственный ход, что улучшает качество и поток одновременно.