Или как мы построили машины, которые говорят людям делать работу, которую должны делать машины.
Нам нужно поговорить о неудобной истине, которая у всех инженерных команд лежит на самом виду. Мы годами строили сложные CI/CD‑пайплайны, ботов для ревью кода и инструменты анализа на базе ИИ, а потом настраивали их так, чтобы они отправляли сообщения в Slack и просили людей выполнить ручную работу.
Вдумайтесь на секунду. Мы автоматизировали просьбу. Не выполнение.
Как однажды заметил теоретик менеджмента Питер Друкер: «Нет ничего более бесполезного, чем эффективно делать то, чего вообще не следует делать». Мы стали удивительно хороши в создании систем, которые эффективно напоминают людям делать что‑то неэффективно.
Недавно я наткнулся на этот паттерн, когда разбирал обсуждение настройки бота для ревью кода. Кто‑то хотел, чтобы бот напоминал разработчикам повышать номер версии в merge request. Звучит разумно: управление версиями важно. Но вот в чем проблема: бот оставлял бы комментарий «пожалуйста, повысьте версию», а человеку нужно было бы это прочитать, принять к сведению и вручную отредактировать файл. Единственный «вклад» автоматизации — это ворчание.
Когда я предложил действительно автоматизировать само повышение версии — чтобы CI вычислял подходящую версию и применял ее, — ответ оказался очень показателен: «Мне проще написать промпт для ИИ на обычном английском языке, чем разбираться с GitLab YAML».
Вот оно. Путь наименьшего сопротивления привел нас к системе напоминаний, хотя нужна была система автоматизации. Мы взяли сложную часть — настроить бота, который анализирует код и пишет комментарии, — и использовали ее, чтобы избежать простой части: увеличить число в файле.
Таксономия псевдоавтоматизации
Покопавшись в конфигурациях десятков репозиториев, я обнаружил этот антипаттерн буквально везде. Он проявляется в нескольких разных вариантах, и каждый следующий коварнее предыдущего.
Файловая напоминалка
Вы точно такое видели. Бот комментирует ваш merge request:
«Пожалуйста, обновите
./openapi.jsonпри добавлении новых полей».
Или:
«Убедитесь, что вы обновили оба файла:
opa/prod/data.yaml, иopa/non-prod/data.yaml».
Или мой личный фаворит:
«Убедитесь, что это согласовано с таблицей
external_loyalty_transaction_enumsв базе данных».
Давайте подумаем, что здесь происходит. Мы построили автоматизацию, достаточно сложную, чтобы определить: изменение может потребовать обновлений в другом месте. Она умеет парсить код, понимать контекст и решать, что, возможно, нужна синхронизация. А затем, в момент максимальной полезности, она… просит человека пойти и сделать это вручную.
У баскетбольного тренера Джона Вудена была фраза: «Не путайте активность с достижением». Мы построили системы с огромной активностью — они анализируют диффы, публикуют комментарии, создают треды, — но не достигают ничего, кроме добавления очередного пункта в чей‑то мысленный список дел.
Как лучше: если ваша система умеет определить, что файл A изменился и файл B нужно обновить, значит, ваша система может обновить файл B. OpenAPI‑спецификации можно генерировать из аннотаций в коде. Конфигурационные файлы можно генерировать из шаблонов и рендерить.
Документацию по схеме базы данных можно извлекать автоматически. Если настоящая синхронизация требует человеческого решения, бот должен ронять сборку, пока человек не примет это решение, а не оставлять вежливую рекомендацию, которая потеряется в море комментариев на ревью.
NET: Swashbuckle автоматически генерирует спецификацию по контроллерам; также можно использовать встроенную поддержку OpenAPI в.NET 9+
Kotlin/Java Spring Boot: SpringDoc OpenAPI инспектирует приложение во время выполнения и генерирует спецификацию из аннотаций без ручной конфигурации
Spec‑first, все платформы: OpenAPI Generator генерирует серверные заглушки и клиентские SDK для Java, Kotlin, C# и десятков других языков как часть вашего CI‑пайплайна
Конфигурационные файлы можно генерировать из шаблонов и рендерить. Документацию по схеме базы данных можно извлекать автоматически. Если настоящая синхронизация требует человеческого решения, бот должен ронять сборку, пока человек не примет это решение, а не оставлять вежливую рекомендацию, которая потеряется в море комментариев на ревью.
Если говорить конкретно о повышении версии, паттерн еще проще. Вместо того чтобы бот писал комментарий «пожалуйста, повысьте версию в gradle.properties», пусть CI вычисляет версию на основе соглашений о семантических коммитах и применяет ее автоматически. Вот GitHub Action, который определяет повышение версии по префиксам в заголовке PR:
# .github/workflows/version.yml
name: Auto Version
on:
pull_request:
types: [opened, edited, synchronize]
jobs:
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Calculate version from PR title
run: |
# Get latest semver tag
LATEST_TAG=$(git tag -l "v*.*.*" --sort=-v:refname | head -n1 || echo "v0.0.0")
# Parse current version
VERSION=${LATEST_TAG#v}
MAJOR=$(echo $VERSION | cut -d. -f1)
MINOR=$(echo $VERSION | cut -d. -f2)
PATCH=$(echo $VERSION | cut -d. -f3)
# Determine bump from PR title prefix
PR_TITLE="${{ github.event.pull_request.title }}"
case "$PR_TITLE" in
BREAKING*) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
FEATURE*) MINOR=$((MINOR + 1)); PATCH=0 ;;
PATCH*) PATCH=$((PATCH + 1)) ;;
*) echo "::error::PR title must start with BREAKING, FEATURE, or PATCH"; exit 1 ;;
esac
echo "NEW_VERSION=$MAJOR.$MINOR.$PATCH" >> $GITHUB_ENV
- name: Update version file
run: |
echo "version=$NEW_VERSION" > gradle.properties
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add gradle.properties
git commit -m "chore: bump version to $NEW_VERSION" || echo "No changes"
git pushТот же паттерн работает и в GitLab CI: мы используем переиспользуемый CI‑шаблон, который вычисляет semver по заголовкам MR и автоматически создает релизы. Единственная задача человека — правильно поставить префикс в заголовке PR; все остальное происходит без вмешательства.
Каскад комментариев от линтера
Этот вариант встречается особенно часто. Ваш CI‑пайплайн запускает SwiftLint, ESLint или любой другой вариант статического анализа, который принят в вашем стеке. Находит семнадцать нарушений. А затем… публикует семнадцать комментариев в вашем merge request и рассказывает, что нужно исправить.
Я видел конфигурации, которые буквально публикуют комментарий: «Проверка линтером не прошла — доступно автоисправление. Запустите pnpm lint‑changed ‑fix локально». («LINTING FAILED — Quick Fix Available. Run pnpm lint‑changed ‑fix locally».) Перечитайте. Автоматизация точно знает, как исправить проблему. Она даже знает команду. Просто отказывается ее запускать.
Это как если бы робот‑пылесос подъезжал к комку пыли, фотографировал его, отправлял вам push‑уведомление с координатами и выключался. «Грязь обнаружена в точке 4.2, 7.8. Пожалуйста, подметите».
Как лучше: исправляйте проблемы до того, как они вообще попадут в CI. Большинство современных IDE умеют автоматически исправлять нарушения правил линтинга при сохранении: разработчик даже не видит предупреждения, код сразу остается корректным. Это и есть настоящая автоматизация: незаметная, мгновенная и не требующая вмешательства человека.
JavaScript/TypeScript: расширение ESLint для VS Code или встроенная поддержка ESLint в IntelliJ позволяют включить автоисправление при сохранении одной настройкой
C#/.NET: встроенное форматирование при сохранении в Rider или CSharpier для VS Code и Rider форматируют код в момент сохранения
Kotlin: плагин ktlint для IntelliJ форматирует код при сохранении; также можно использовать detekt с интеграцией ktlint для комплексного анализа
Главное — убедиться, что у всей команды установлены эти плагины. Обе основные IDE поддерживают коммит конфигурационных файлов, которые при открытии проекта предлагают разработчикам установить нужные расширения:
IntelliJ/Rider: добавьте обязательные плагины через Settings → Build, Execution, Deployment → Required Plugins. Это создаст файл.idea/externalDependencies.xml — закоммитьте его, и при открытии проекта коллегам будет предложено установить недостающие плагины.
VS Code: создайте файл.vscode/extensions.json с массивом recommendations, в котором перечислены ID расширений. Когда коллеги откроют рабочую область, VS Code предложит им установить рекомендованные расширения.
Если вам нужна страховка в CI, линтер все еще можно запускать там, но пусть он автоматически исправляет проблемы и коммитит изменения обратно в ветку, а не публикует комментарии. Некоторые команды считают коммиты из CI сбивающими с толку: «кто сделал этот коммит?» Именно поэтому подход с плагинами для IDE чище: исправление происходит у источника, в руках разработчика, еще до того, как код покинет его машину.
Если проблему действительно нельзя исправить автоматически, роняйте сборку, а не оставляйте комментарий. Упавшая сборка требует действия. Комментарий превращается в археологию: его закапывают другие комментарии, о нем забывают, пока кто‑нибудь спустя недели не заметит нерешенный тред.
Полиция метаданных MR
«В заголовке MR должен быть ID задачи в Jira».
«Пожалуйста, добавьте ревьюеров в этот PR».
«Этот PR не ссылается на релизную веху».
«Обязательный чеклист для контрибьютора заполнен не полностью».
В крупных организациях такие сообщения появляются сотни раз в день. Каждое из них заставляет человека остановиться, переключиться обратно в контекст merge request и вручную внести правку, которую можно было автоматизировать или обеспечить другим способом.
Комик Джерри Сайнфелд однажды так описал свой творческий процесс: «Я ищу путь наименьшего сопротивления, который все еще позволяет сделать работу». Наши системы автоматизации нашли путь наибольшего сопротивления: тот, который как можно чаще прерывает как можно больше людей и при этом дает минимум реальной пользы.
Как лучше: парсить имя ветки или сообщение коммита и автоматически извлекать ID задачи в Jira. Вы удивитесь, сколько людей и так делают это по умолчанию. Автоматически назначайте ревьюеров на основе файлов владения кодом или недавних контрибьюторов — либо сделайте что‑то более умное с учетом истории областей кода, которых они касались. Назначайте релизную веху на основе целевых веток.
Для чеклистов, которые действительно требуют подтверждения человеком, делайте блокирующую проверку при merge, а не проходной комментарий, который можно проигнорировать.
ИИ‑ревью, которое ревьюит, но ничего не делает
Это самый новый и, пожалуй, самый ироничный вариант. Мы внедрили большие языковые модели — системы, которые умеют понимать контекст кода, находить проблемы и генерировать исправления, — а затем настроили их так, чтобы они… писали комментарии и просили людей эти исправления внести.
Мы дали искусственному интеллекту возможность читать код, понимать намерение и выдавать решения. А потом сказали ему быть советчиком с заднего сиденья.
«Эта функция слишком длинная. Стоит разбить ее на более мелкие части».
«Имя этой переменной могло бы быть более описательным».
«Этот дублирующийся код можно вынести во вспомогательную функцию».
ИИ видит проблему. ИИ может предложить решение. ИИ может написать код. Но ИИ настроили так, чтобы он останавливался на этапе «дать совет» — как персональный тренер, который говорит вам поднимать штангу, но отказывается пустить вас в зал.
Как лучше: если ваш ИИ умеет находить проблему и предлагать исправление, пусть он действительно вносит это исправление. Инструменты вроде Claude Code могут запускаться в CI‑пайплайнах и коммитить исправления прямо в ветки: ИИ не просто находит проблему, он ее решает. Вы ревьюите то, что он сделал, а не переписываете вручную то, что он предложил.
Если вы пока не готовы к автономным исправлениям, как минимум используйте механизм предлагаемых изменений, который есть и в GitLab, и в GitHub: оформляйте изменения, предложенные ИИ, как блоки с предлагаемыми изменениями, чтобы разработчики могли принять их одним кликом, а не вручную копировать код из комментария в редактор.
Разница между комментарием «подумайте о переименовании этой переменной» и блоком с предлагаемым изменением, который применяет переименование по клику, — это разница между созданием работы и выполнением работы.
Первопричина: мы автоматизировали легкую часть
Вот паттерн, который я постоянно вижу: команды автоматизируют обнаружение проблем и коммуникацию по ним, а устранение оставляют людям. Но обнаружение часто и есть легкая часть. Сложная часть — та самая, которую и нужно автоматизировать, — это исправление.
Подумайте, что мы на самом деле делаем, когда запускаем бота, который пишет комментарий «пожалуйста, повысьте номер версии»:
Мы написали код для анализа merge request
Интегрировались с API, чтобы публиковать комментарии
Развернули и поддерживаем инфраструктуру для запуска этой автоматизации
Настроили системы уведомлений, чтобы разработчики видели комментарии
Все эти усилия — и результатом становится… набранное сообщение, на которое должен отреагировать человек. Мы построили машину Руба Голдберга для генерации пунктов в списке дел.
Экономист Джон Кеннет Гэлбрейт однажды заметил: «Когда человек оказывается перед выбором — изменить свое мнение или доказать, что в этом нет необходимости, — почти каждый немедленно берется за доказательство». В инженерии, когда у нас есть выбор между полной автоматизацией задачи и автоматизацией ровно настолько, чтобы почувствовать себя продуктивными, мы часто выбираем второе.
Защита незащитимого, которая на самом деле не защита
Теперь мне нужно сказать важную вещь: люди, которые строят такие системы, не делают ничего плохого. Они не ленивы, не некомпетентны и уж точно не действуют со злым умыслом.
Они делают ровно то, что выглядит разумным с учетом доступных им инструментов и знаний. Боты для ревью кода продвигаются как инструменты для публикации комментариев. Туториалы по CI/CD фокусируются на запуске проверок и выводе результатов. Документация этих систем в основном рассказывает, как анализировать и коммуницировать, а не как действовать.
Если вы никогда не видели автоматизацию, которая действительно что‑то исправляет, «автоматизация, которая говорит, что исправить», выглядит как автоматизация. Абсурд становится очевиден только тогда, когда отходишь на шаг назад и сравниваешь это с тем, что вообще возможно.
Именно поэтому обучение важнее критики. Каждый инженер, который научился настраивать бота так, чтобы он оставлял комментарий «пожалуйста, повысьте версию», находится всего в одном конфигурационном файле от автоматизации самого повышения версии. Он уже сделал 80% работы. Ему просто нужно, чтобы кто‑то показал последние 20%.
И вот неудобная правда: если мы не научим людей этому более правильному подходу, паттерн «бота‑комментатора» будет распространяться. Новые инженеры увидят его, решат, что это лучшая практика, и начнут воспроизводить. Антипаттерн будет расползаться не из‑за чьей‑то профнепригодности, а просто потому, что люди наблюдают за тем, что вроде бы работает и вроде бы считается «лучшей практикой» — но это тема для отдельного поста.
В сухом остатке
Мы построили впечатляющую инфраструктуру, в которой компьютеры просят людей что‑то делать. А вот инфраструктуру, в которой компьютеры действительно что‑то делают, во многих случаях так и не построили.
Антипаттерн автоматизации — это не про плохие намерения и не про слабую инженерную работу. Это про то, что мы останавливаемся на шаг раньше: строим слой обнаружения и коммуникации, но не слой действия. Путаем «автоматическое уведомление» с «автоматизированным решением».
Как заметил архитектор Бакминстер Фуллер: «Невозможно изменить что‑то, борясь с существующей реальностью. Чтобы что‑то изменить, постройте новую модель, которая сделает старую устаревшей». Нам не нужно критиковать ботов, которые оставляют комментарии и просят сделать ручную работу. Нужно показывать людям, как строить ботов, которые делают эту работу сами.
В следующий раз, когда будете настраивать CI‑проверку или бота для ревью кода, спросите себя: я автоматизирую решение или автоматизирую просьбу к кому‑то другому это решение реализовать? Я строю инструмент или очень изощренный способ генерировать прерывания?
Разница между автоматизацией и делегированием в том, кто выполняет работу. Если компьютер говорит человеку сделать то, что мог бы сделать компьютер, это не автоматизация. Это просто спам с лишними шагами.
А теперь прошу меня извинить: мне нужно идти ревьюить merge request. Судя по всему, там тред комментариев с семнадцатью нерешенными пунктами, которые говорят мне запустить команду, которую бот мог бы запустить сам.

Автоматизация начинает приносить пользу там, где система не только находит проблему, но и выполняет действие. Ближе познакомиться с такими подходами можно на бесплатных уроках: преподаватели‑практики покажут, как проектировать CI‑процессы, использовать ИИ для исправлений и сокращать ручные операции в разработке.
30 июня, 20:00. «GitLab CI как конструктор workflow». Записаться
18 июня, 20:00. «Тесты, которые чинят себя сами: практика ИИ в UI‑тестировании». Записаться
21 июля, 20:00. «Разработка ИИ‑приложений с Claude Code». Записаться
Все ближайшие открытые уроки OTUS собраны в отдельном дайджесте — там можно выбрать тему под свои задачи.
Ещё несколько материалов об инженерных процессах, управлении разработкой и работе с неопределённостью:






















