Инженерная практика
Автоматизируй механику, оставь людям суждение
В одной команде PR в среднем набирали по одиннадцать комментариев, и примерно семь из этих одиннадцати были про форматирование, порядок импортов или регистр имён. Двое сеньоров-ревьюеров тратили почти день в неделю, печатая «nit: пробелы» и «здесь нужен camelCase». Потом кто-то добавил Prettier и ESLint как блокирующую CI-проверку, запускающуюся ещё до того, как уйдёт запрос на ревью. За ночь среднее упало до четырёх комментариев, и все четыре были про поведение: off-by-one в пагинации, пропущенный await, потерянный кейс ошибки, вопрос о правиле округления денег. В тщательности ревьюеров ничего не изменилось. Машина просто перестала подсовывать им работу машины.
Граница проходит по разрешимости, а не по важности
Правило не в том, чтобы «автоматизировать неважное». Некоторые автоматические проверки — security-скан, ловящий SQL-инъекцию, проверка типов, ловящая null deref — ловят вещи гораздо важнее большинства человеческих комментариев. Настоящая граница — это разрешимость: можно ли правило вычислить механически со стабильным ответом? Форматирование, порядок импортов, конвенции именования, неиспользуемые переменные, правила линта, ошибки типов, известно-уязвимые зависимости и большой класс паттернов багов — все разрешимы: инструмент каждый раз выдаёт один и тот же вердикт, не требуя контекста. Всё разрешимое — место в CI, запускающемся до того, как человек вообще откроет дифф, и блокирующем мёрж при провале.
Человеку остаётся неразрешимый остаток — ровно то, для чего ревью и существует: тот ли это дизайн? Делает ли он на самом деле то, что задумано в тикете? Поймёт ли его следующий инженер через полгода? Верен ли путь ошибки, точен ли путь денег, цела ли граница авторизации? Стандарт ревью кода Google ставит дизайн первым — «самое важное, что нужно покрыть», — потому что решения по дизайну накапливаются и дорого откатываются, тогда как сбившаяся скобка не стоит ничего, и форматтер чинит её бесплатно. Каждая минута, которую сеньор тратит на разрешимую проверку, украдена у неразрешимых, которые может сделать только он, и это повторяющаяся кража: один и тот же нит, на каждом PR, вечно, пока ты не автоматизируешь его один раз.
| Гейт | Что ловит | Разрешимо? | Когда запускается |
|---|---|---|---|
| Форматтер (Prettier, gofmt) | Раскладка, пробелы, кавычки | Да — авто-фикс | Pre-commit + CI-блок |
| Линтер / проверка типов | Неиспользуемые переменные, null deref, правила линта | Да | CI-блок, до ревью |
| SAST / скан зависимостей | Инъекции, известные CVE, секреты | В основном — нужна настройка | CI-блок, до ревью |
| Человек-ревьюер | Дизайн, замысел, поддерживаемость, корректность | Нет — нужно суждение | После того как гейты зелёные |
Shift left: гейт запускается до человека и должен быть дёшев в соблюдении
Половина ценности — в последовательности. Линтер, запускающийся только после апрува человека, — это театр: нит уже напечатан. Гейт обязан запускаться до запроса на ревью, в идеале ещё и pre-commit на машине автора, чтобы автор сам чинил механические проблемы, а ревьюер получал дифф, где каждая разрешимая проверка уже зелёная. Это та же логика shift-left, что в trunk-based CI: лови дешёвый класс проблем в самой ранней, самой дешёвой точке и освобождай дорогой ресурс (человеческое суждение, end-to-end тестирование) для проблем, которые ловятся только поздно. Эффект подготовки автора из исследования SmartBear усиливает это — чистый, заранее проверенный дифф — это дифф, который автору уже пришлось внимательно осмотреть, что само по себе снижает плотность дефектов.
Но у автоматизации есть отказ, который тихо её уничтожит: ложные срабатывания. Инструмент статического анализа, помечающий десять непроблем на каждую настоящую, приучает инженеров игнорировать его, и тогда он не ловит ничего, потому что все рефлекторно отмахиваются от его вывода — та же динамика усталости от алертов, что у пейджера, кричащего «волк». Программа Google Tricorder сделала это центральным проектным ограничением, держа анализаторы под строгой эффективной частотой ложных срабатываний ниже 10% и давая разработчикам однокликовый канал обратной связи «бесполезно», чтобы убивать шумные проверки. Урок в том, что «включить больше статического анализа» не бесплатно: шумный анализатор хуже, чем никакого, потому что он тратит доверие, которое непросто восстановить. Настраивай на сигнал, делай фикс очевидным и позволяй разработчикам вырезать проверки, не оправдывающие своё место.
Почему это работает
Глубокая причина автоматизировать механику — не время ревьюера, а калибровка модели всей команды о том, что такое ревью. Когда первые PR джуна возвращаются забитыми нитами про пробелы, он усваивает, что ревью — про поверхность, и ревьюит других так же, и культура костенеет вокруг мелочей, пока изъяны дизайна проплывают мимо. Когда всё это берёт на себя машина, и каждый человеческий комментарий — про поведение или дизайн, команда усваивает, что ревью — для того, что важно. Автоматизация не просто экономит внимание; она учит всех, куда внимание должно направляться.
Автоматизация перенаправляет ревью, а не заменяет его
Соблазнительный перебор — заключить, что если инструменты ловят баги, то инструменты могут заменить ревью. Не могут — они сдвигают границу, а не стирают её. У линтера нет понятия о том, решает ли фича реальную проблему пользователя; SAST-инструмент не скажет тебе, что путь ретрая не идемпотентен; AI-ревьюер может суммировать дифф, но всё равно не сертифицирует, что дизайн переживёт требования следующего квартала. Что автоматизация делает — это перенаправляет человека: расчищая разрешимый слой, она концентрирует дефицитное суждение на неразрешимом слое, где живут дорогие дефекты. Команда, которая подключает форматтеры, линтеры, проверки типов и настроенный SAST как блокирующий пред-ревью гейт, не ревьюит меньше — она ревьюит только то, что стоит человека, а это и есть весь смысл практики.
Конечное состояние — многоуровневый пайплайн. Уровень 0 — собственные pre-commit проверки автора и аннотированный дифф. Уровень 1 — блокирующий CI-гейт: форматирование, линт, типы, тесты, скан зависимостей и security, — который PR обязан пройти, прежде чем человека вообще попросят. Уровень 2 — человеческое ревью, теперь тратящее весь свой неразбавленный бюджет на дизайн, замысел, корректность и передачу знаний. Каждый уровень ловит то, что дешевле всего поймать на этом слое, и передаёт несократимый остаток выше. Самый рычажный ход, который большинство команд ещё не сделали до конца, — просто обеспечить, чтобы ни один человек никогда не печатал комментарий, который могла бы сделать машина.
Ваши PR забиты комментариями про стиль и форматирование, а сеньоры жгут часы на нитах. Какой структурный фикс правильный?
Где реальная граница того, что автоматизировать, а что оставить человеку-ревьюеру?
Почему Google в Tricorder держал анализаторы под эффективной частотой ложных срабатываний ниже 10%?
Упорядочьте многоуровневый пайплайн так, чтобы внимание человека видело только неразрешимый слой:
- 1 Автор запускает pre-commit формат/линт локально и аннотирует дифф
- 2 Блокирующий CI-гейт запускает формат, линт, типы, тесты, настроенный SAST — должен быть зелёным
- 3 Запрос на ревью уходит только после того, как пройдёт каждая разрешимая проверка
- 4 Человек тратит весь бюджет на дизайн, замысел, корректность, поддерживаемость
- 5 Шумные проверки, собирающие фидбэк «бесполезно», вырезаются, чтобы защитить доверие
- 01Коллега говорит: «автоматизируй неважные проверки, люди делают важные». Почему «важность» — неверная граница и какая верная?
- 02Каких двух отказов надо избегать, опираясь на автоматические гейты, и как выглядит многоуровневое конечное состояние?
Причина автоматизировать механические части ревью — не просто сэкономить время ревьюера, а поставить нужный класс проблем на нужный слой и научить всю команду, куда направляется внимание. Граница — разрешимость, а не важность: любое правило, которое машина может вычислить со стабильным, не зависящим от контекста ответом — форматирование, порядок импортов, именование, неиспользуемые переменные, линт, типы, известно-уязвимые зависимости и широкая полоса паттернов багов, — место в блокирующем CI-гейте, в идеале продублированном pre-commit, который запускается до запроса на ревью, чтобы автор его расчистил, а ревьюер его никогда не видел. Это оставляет человеку неразрешимый остаток, ради которого ревью и существует: верен ли дизайн, совпадает ли с замыслом, будет ли поддерживаемым, верны ли пути ошибки, денег и авторизации. Последовательность важна — гейт, запускающийся после апрува, это театр, — и важен сигнал: ненастроенный анализатор, кричащий «волк», игнорируют, и он ничего не защищает, поэтому Google в Tricorder держал ложные срабатывания под 10% с однокликовым каналом вырезания. Соблазнительный перебор — думать, что инструменты могут заменить ревью; они лишь перенаправляют его, концентрируя дефицитное суждение там, где прячутся дорогие дефекты. Зрелое конечное состояние многоуровневое — пред-проверки автора, затем блокирующий CI-гейт, затем человеческое ревью только дизайна и корректности, — и самое рычажное правило в том, что ни один человек никогда не должен печатать комментарий, который могла бы сделать машина.