Что анализатор способен проверить вместо человека и где он врёт: линтеры, типы, quality gates. Встраивание в workflow — в курсе для команды
Каждое замечание, которое человек пишет второй раз, — это ненастроенный инструмент. Задача автоматизации не заменить ревьюера, а освободить его внимание для того, что машина не умеет.
Вы научитесь определять, какие замечания подлежат автоматизации, а какие нет; выбирать инструмент по классу проблем, которые он ловит; настраивать проверки так, чтобы их не отключили через месяц; и оценивать, где статический анализ создаёт больше шума, чем пользы.
Как встроить всё это в командный workflow — правила назначения, обязательные проверки, борьба с шумом от ботов — в теме Инструменты и интеграции курса для команды. Здесь речь о том, что инструменты способны проверить.
Правило простое: автоматизируется всё, что формулируется как правило без исключений. Всё остальное — работа человека.
| Автоматизируется полностью | Требует человека |
|---|---|
| Форматирование, кавычки, длина строки, импорты | Понятно ли имя |
| Неиспользуемые переменные, недостижимый код | Верно ли решена задача |
| Несоответствие типов | Правильный ли тип выбран |
Известные уязвимые конструкции (yaml.load, eval) | Ошибка в логике авторизации |
| Уязвимости в зависимостях по базе CVE | Нужна ли эта зависимость |
| Секреты по шаблону | Секрет, не похожий на секрет |
| Покрытие изменённых строк | Доказывает ли тест что-нибудь |
| Ломающие изменения схемы API | Изменение смысла при той же схеме |
Практический критерий для ревьюера: если вы пишете одно и то же замечание третий раз, заведите правило. Если правило невозможно сформулировать без «но иногда» — не заводите, оно будет создавать шум.
Проверьте себя. Выпишите три самых частых своих замечания за последний месяц. Сколько из них можно закрыть правилом?
Частая ошибка. Автоматизировать то, где часто бывают исключения. Правило с массовыми подавлениями хуже отсутствия правила: оно приучает игнорировать предупреждения.
Инструменты выстраиваются в порядке возрастания стоимости обнаружения. Чем раньше ловится проблема, тем дешевле.
Форматтер. Не проверяет, а приводит к виду. Ключевое свойство — отсутствие настроек, о которых можно спорить. ruff format, black, prettier. Как только формат задан инструментом, весь класс споров исчезает из ревью.
Линтер. Проверяет правила и находит подозрительные конструкции. ruff, eslint. Современные линтеры покрывают то, что раньше требовало нескольких инструментов, включая порядок импортов и часть проверок безопасности.
Типы. mypy, pyright, tsc в строгом режиме. Из всех инструментов типизация даёт самый высокий возврат: она ловит целый класс ошибок, который иначе находится только тестами или в проде. Optional, не проверенный на None, — самая частая находка.
Анализ безопасности. bandit, semgrep, CodeQL. Находят известные опасные конструкции. Важно понимать границу: они ловят yaml.load и subprocess(shell=True), но не ловят отсутствующую проверку прав — потому что для этого надо знать, какая проверка должна быть.
Зависимости. pip-audit, npm audit, Dependabot. Единственный класс, где автоматизация практически полна: сверка версий с базой уязвимостей человеку недоступна.
Секреты. gitleaks, detect-secrets. Обязательно и в pre-commit, и в CI: локальный хук можно обойти.
Тесты и покрытие изменённых строк. diff-cover вместо абсолютного порога — по причинам, разобранным в теме Метрики качества кода.
Проверьте себя. Какой из перечисленных слоёв отсутствует в вашем проекте? Что вы из-за этого проверяете руками?
Частая ошибка. Ставить только линтер и считать автоматизацию сделанной. Типизация ловит другой класс ошибок и обычно даёт больше.
Одна и та же проверка в разных местах даёт разный эффект.
Редактор. Мгновенно, до коммита. Самое дешёвое место, но не гарантия: настройки у всех разные.
pre-commit. Быстрые проверки на изменённых файлах: формат, линтер, секреты. Дольше двух-трёх секунд — начнут обходить через --no-verify. Поэтому тесты и типизацию всего проекта сюда не кладут.
CI на PR. Полный набор. Единственное место, которое нельзя обойти, поэтому обязательные проверки живут здесь.
Плановый запуск. То, что не привязано к изменениям: аудит зависимостей (уязвимость публикуется без вашего участия), полное сканирование, мутационные тесты.
Практическое замечание: одна и та же проверка должна работать одинаково локально и в CI. Ситуация «локально зелено, в CI красно» из-за разных версий инструмента съедает больше времени, чем сама проверка экономит. Версии инструментов фиксируются так же, как версии библиотек.
Проверьте себя. Сколько времени идёт ваш pre-commit? Если больше пяти секунд — посмотрите, кто в команде им пользуется.
Частая ошибка. Дублировать все проверки в pre-commit и CI. Локально — быстрое, в CI — полное.
Автоматический комментарий в PR ценен, пока его читают. Дальше начинается обратный эффект: сорок комментариев от линтера означают, что человеческие замечания никто не найдёт.
Что работает:
reviewdog и встроенные аннотации CI показывают проблему в контексте.Что не работает: бот, дублирующий то, что уже подсвечено в редакторе; бот, комментирующий каждый PR одним и тем же напоминанием; проверка с высокой долей ложных срабатываний.
Проверьте себя. Посмотрите последние десять PR: сколько комментариев ботов и сколько людей? Соотношение больше трёх к одному — проблема.
Частая ошибка. Оценивать бота по числу найденного. Оценивать надо по доле исправленного: если девять из десяти замечаний закрывают как неактуальные, бот вредит.
Ни один инструмент не ответит на вопросы, которые определяют ценность ревью: та ли задача решена; выдержит ли решение рост; является ли это изменение ломающим для клиентов; правильна ли проверка прав; что произойдёт при повторном вызове; понятен ли код через год.
Из этого следует практический вывод, важный для ревьюера: зелёный CI не является причиной для approve. Он причина не тратить внимание на то, что уже проверено. Ровно так и стоит формулировать ценность автоматизации — она не сокращает время ревью, а меняет то, на что это время уходит.
Проверьте себя. Возьмите последний найденный вами настоящий баг. Мог ли его поймать инструмент?
Частая ошибка. Считать зелёный CI признаком качества PR. Он признак отсутствия определённого класса дефектов.
Новый участник команды приносит конфигурацию проверок. Намерение хорошее, реализация даст обратный эффект.
+# .pre-commit-config.yaml
+repos:
+ - repo: https://github.com/psf/black
+ rev: stable
+ hooks:
+ - id: black
+ - repo: https://github.com/pycqa/isort
+ hooks:
+ - id: isort
+ - repo: https://github.com/pycqa/flake8
+ hooks:
+ - id: flake8
+ - repo: https://github.com/pycqa/pylint
+ hooks:
+ - id: pylint
+ args: [--disable=all, --enable=all]
+ - repo: https://github.com/pre-commit/mirrors-mypy
+ hooks:
+ - id: mypy
+ args: [--strict]
+ pass_filenames: false
+ - repo: local
+ hooks:
+ - id: pytest
+ entry: pytest
+ language: system
+ pass_filenames: false
+
+# .github/workflows/quality.yml
+ - run: black --check .
+ - run: isort --check .
+ - run: flake8 .
+ - run: pylint app/
+ - run: mypy --strict app/
+ - run: bandit -r app/
+ - run: pytestЧто видит ревьюер. Проблемы четырёх разных видов.
rev: stable и отсутствие rev у остальных: версия не зафиксирована. Инструмент обновится сам, и в один день CI станет красным на коммите, который ничего не менял. Отладка такого — потерянный день.
black + isort + flake8 + pylint: четыре инструмента с пересекающимися и местами противоречащими правилами. black и flake8 конфликтуют по длине строки и переносам, если не согласованы явно. pylint с --enable=all включает проверки вроде «слишком много аргументов», «слишком короткое имя», «отсутствует docstring» — это тысячи замечаний на существующей кодовой базе. Всё это заменяется одним ruff, который покрывает и линтер, и порядок импортов, и часть правил pylint, и работает в сотни раз быстрее.
mypy --strict с pass_filenames: false и pytest в pre-commit: обе проверки идут по всему проекту на каждый коммит. Это минуты ожидания, и результат предсказуем — команда начнёт коммитить с --no-verify, то есть все хуки, включая быстрые и полезные, перестанут работать.
mypy --strict на существующем непроаннотированном проекте выдаст тысячи ошибок сразу. Ни один PR не пройдёт.
И чего нет: сканера секретов, аудита зависимостей, покрытия по изменённым строкам. То есть отсутствуют ровно те проверки, которые невозможно заменить человеком, зато в избытке те, что дублируют друг друга.
Комментарии в PR:
.pre-commit-config.yaml:23· blockerpytestиmypyпо всему проекту в pre-commit — это минуты на каждый коммит. Через неделю все будут коммитить с--no-verify, и мы потеряем в том числе быстрые проверки. В pre-commit только то, что укладывается в 2–3 секунды на изменённых файлах; тесты и типы — в CI.
.pre-commit-config.yaml:3· blocker Версии не зафиксированы (rev: stable, а у остальныхrevвообще нет). Инструмент обновится сам, и CI покраснеет на коммите, который ничего не менял. Нужны точные версии, обновляемые осознанно черезpre-commit autoupdate.
.github/workflows/quality.yml:5· blockermypy --strictна проекте без аннотаций выдаст тысячи ошибок, и ни один PR не пройдёт. Практичный путь: включить нестрого на весь проект, строго — на новые модули через[[tool.mypy.overrides]], и расширять список по мере аннотирования.
.pre-commit-config.yaml:11· majorpylint --enable=allвключает в том числе «нет docstring» и «слишком много аргументов» — это тысячи замечаний на текущем коде. И вместе сflake8иisortон даёт три пересекающихся набора правил, местами конфликтующих сblack. Предлагаю заменить всю тройку наruff+ruff format: одно правило конфигурации, никаких конфликтов, время работы — доли секунды.
.github/workflows/quality.yml:1· major Нет двух самых полезных проверок: сканера секретов (gitleaks) и аудита зависимостей (pip-audit). Это ровно тот класс, где человек бесполезен, — секрет глазами в диффе на 500 строк не заметишь, а CVE в транзитивной зависимости не узнаешь никак.
.github/workflows/quality.yml:7· minor Покрытие не проверяется вообще. Предлагаюdiff-coverпо изменённым строкам — про абсолютные пороги мы уже обсуждали в другом PR.
.pre-commit-config.yaml:1· question Как это будет работать у тех, у кого уже настроен редактор с другим форматтером? Стоит договориться и зафиксировать настройки редактора в репозитории, иначе получим войну переформатирований.
Чем закончилось.
# .pre-commit-config.yaml — быстрое, на изменённых файлах
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.2
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/gitleaks/gitleaks
rev: v8.28.0
hooks:
- id: gitleaks# CI на PR — полное
- run: ruff check . && ruff format --check .
- run: mypy app/ # строго — только для новых модулей
- run: bandit -r app/ -ll # только средняя и высокая критичность
- run: pip-audit
- run: pytest --cov=app --cov-report=xml
- run: diff-cover coverage.xml --fail-under=80Pre-commit укладывается в полторы секунды, поэтому его не обходят. ruff заменил четыре инструмента. Строгая типизация включается по модулям — список расширяется по мере аннотирования, и каждое расширение видно в PR.
Итог через месяц: комментариев о форматировании и импортах в ревью не стало вообще, зато нашлись два секрета в истории (отозваны) и одна уязвимость в транзитивной зависимости. Ровно то распределение труда, которое нужно: машина занялась тем, что человек не видит, человек — тем, что машина не понимает.
ГРАНИЦА автоматизировано то, что формулируется правилом без исключений
ДУБЛИ инструменты не пересекаются и не конфликтуют между собой
ВЕРСИИ зафиксированы; локально и в CI одинаковые
СКОРОСТЬ pre-commit укладывается в пару секунд на изменённых файлах
ВНЕДРЕНИЕ новые строгие правила — для нового кода, а не для всей истории
ОБЯЗАТЕЛЬНОЕ секреты и аудит зависимостей есть — их человек не заменит
ПОКРЫТИЕ считается по изменённым строкам
ШУМ бот комментирует только изменённые строки, с ограничением количества
ПОДАВЛЕНИЯ массовые `noqa` и `type: ignore` — признак неудачного правила
ПОНИМАНИЕ зелёный CI не является причиной для approveЧто делать с числами, которые выдают эти инструменты, — Метрики качества кода. Как встроить проверки в командный процесс без сопротивления — Инструменты и интеграции.
Ключевая мысль: автоматизация не сокращает время ревью — она меняет то, на что оно уходит. Если после её внедрения ревью стало быстрее, но не глубже, что-то сделано не так.