- BrainTools - https://www.braintools.ru -
Тесты зелёные, покрытие растёт, а багов меньше не становится. На QA-митапе инженер из крупной продуктовой компании показал механику: AI-агенты подгоняют моки, меняют ассерты, генерируют результаты, которые ничего не проверяют. Стек у команды — near-SOTA. Модель свежая. Агент — один из лидеров open-source.
Значит, дело не в инструментах. А в чём именно — разбираю ниже: от кода до процесса и организации.
Докладчик описал паттерн, с которым сталкивался каждый, кто просил AI написать тесты:
Ты даёшь ей автотесты. Она пишет, они проходят. Зелёные галочки. Но меня пугают зелёные галочки. Почему? Потому что её задача — чтобы все тесты прошли. Она подгоняет результат. Изменяет данные — и у тебя зелёный тест.
Вот как это выглядит на практике. Допустим, вы просите AI написать тест для эндпоинта расчёта скидки. Правило: при заказе от 5000 ₽ — скидка 10%, но не больше 1000 ₽. В сервисе баг: потолок скидки не применяется.
Что генерирует AI без ограничений:
const mockDiscountService = {
calculate: jest.fn().mockReturnValue(500)
};
test('applies 10% discount for orders over 5000', () => {
const result = mockDiscountService.calculate({ total: 5000 });
expect(result).toBe(500); // ✅ Зелёный. Мок вернул то, что в него положили.
});
Тест зелёный, потому что AI замкнул цикл: сам придумал мок → сам вызвал мок → сам проверил мок. Реальный сервис не участвует. Баг не найден.
Второй уровень проблемы: когда тест падает на реальном сервисе, AI «чинит» не сервис, а тест:
Если красное — она говорит: давай сделаю зелёным. Просто меняет ассерт или мок.
Тест на потолок скидки падает (реальный ответ 1500 вместо 1000). AI вместо того, чтобы сообщить о баге, меняет expect(result).toBe(1000) на expect(result).toBe(1500). Зелёный.
Это reward hacking на уровне промпта: когда вы говорите «напиши тесты», AI интерпретирует задачу как «сделай так, чтобы тесты проходили». Зелёный результат — цель. Всё остальное — средство.
Я спросил докладчика, какие модели и агенты они используют. Ответ: GLM 4.7, OpenCode.
На тот момент я прокомментировал: «Эти проблемы были год-полтора назад. С флагманскими моделями и агентами ситуация другая.» Но когда сел писать статью — перепроверил. И вынужден поправиться.
GLM 4.7 вышла в декабре 2025 года. Доклад был в марте 2026. Это три месяца от релиза — никак не устаревшая модель. Более того, её наследник GLM-5.1, вышедший в апреле 2026, занял первое место на SWE-Bench Pro (58.4%), обогнав Claude Opus 4.6 и GPT-5.4. Модель сильнее — но без строгой типизации и процесса упрётся в тот же потолок. LSP по-прежнему не увидит ошибку [1] через any, а промпт «напиши тесты» всё так же спровоцирует reward hacking.
OpenCode — один из лидеров open-source агентов: 140K звёзд на GitHub, встроенная LSP-интеграция, поддержка 30+ языков, автоматическая установка серверов при первом обращении к файлу. Менять его не на что — он и так в топе.
Так почему при near-SOTA стеке — проблемы полуторагодичной давности?
Обычно говорят про три фактора: модель, агент, процесс. Но формула неполная — есть четвёртый множитель:
Результат = Модель × Агент × Процесс × Качество кодовой базы
Любой множитель близок к нулю — и результат около нуля, хоть остальные будь идеальными. При этом кодовая база задаёт потолок: выше него не прыгнут ни модель, ни процесс. У ребят из доклада первые два множителя были нормальные. Третий (процесс) — слабый: промпт «напиши тесты» без ограничений. Но главный провал — четвёртый.
На митапе выплыла деталь, которая объясняет почти всё. Команда пишет на TypeScript, а соседняя команда (разработки) отдаёт интерфейсы с any повсюду. Я тогда сказал:
У меня сильное подозрение, что организационная проблема вылезает на уровне нейронки. Неэффективность вызвана тем, что контекст перегружен неоднозначностями, а неоднозначности берутся из взаимодействия команд.
А дальше — чистая механика. OpenCode запускает TypeScript language server (vtsls) автоматически, как только видит .ts-файлы в проекте. LSP-сервер даёт агенту «зрение»: типы, определения, ссылки, диагностики ошибок. Когда агент вносит изменение, ломающее типы, LSP сообщает об ошибке за миллисекунды — и агент чинит её в том же ходе, без запуска компилятора.
Но any — это слепое пятно. Если функция принимает any и возвращает any, TypeScript LSP не видит ошибки при передаче неправильного типа. Агент не получает красного сигнала. И делает ровно то, что описал докладчик:
Ошибка была в несовместимости типов. Агент такой — ну окей, давай будет number. Взял, поменял. Всё проходит, но это вообще не решает проблему.
LSP не сказал «тут ошибка», потому что any совместим с чем угодно. Агент не анализировал архитектуру — он оптимизировал локально. Зелёный тест, деградация архитектуры.
В моих проектах бэкенд на Java. Строго типизированный язык, где LSP (jdtls) работает в полную силу:
Если AI написал чушь — код не скомпилируется. Первый автоматический фильтр.
Если AI изменил сигнатуру метода — LSP мгновенно сообщит обо всех вызовах, которые сломались.
Если AI передал неправильный тип — ошибка компиляции, а не тихий any.
Я стараюсь не допускать неоднозначностей при кодировании и на code review. Это требует дисциплины, но именно это делает AI-агента предсказуемым: он работает с чистым сигналом вместо шума.
Вот пример из моей работы. Мы — агрегатор авиабилетов и гостиниц. Один из основных сценариев — подключение сторонних поставщиков: взять описание чужого API (часто в произвольном формате и с ошибками в документации) и интегрировать в нашу систему. Раньше это занимало месяцы. Теперь техническая часть — дни.
Уволенный middle-разработчик бэкенда на 80% заменён AI-агентом. Под моим руководством агент читает чужую документацию, разбивает интеграцию на этапы, реализует, прогоняет тесты. Причём есть сценарий, который для живого разработчика практически невозможен: когда API плохо задокументирован и описанные комбинации параметров не работают, агент перебирает варианты, пока не найдёт рабочий. Человек на это не пойдёт — слишком монотонно.
Но ключ — не модель, а pipeline: строгая типизация + обязательные тесты + компиляция как gate. В таком pipeline AI-агент предсказуем.
|
|
Строгая типизация (Java, TS strict, Go, Rust) |
Слабая типизация (TS с |
|---|---|---|
|
LSP ловит ошибки типов |
✅ за 50 мс |
❌ не видит |
|
Агент получает диагностику после правки |
✅ мгновенно |
❌ молчание |
|
hover показывает тип |
✅ конкретный |
⚠️ |
|
Агент «чинит» тест подменой типа |
Компилятор ругается |
Компилятор молчит |
|
goToDefinition / findReferences |
✅ точно |
✅ работает (но менее полезно) |
OpenCode запускает LSP-серверы автоматически — от команды не требуется ничего, кроме наличия проекта. Серверы скачиваются и устанавливаются при первом обращении к файлу. Но LSP — усилитель существующего качества кода. Строгие типы → LSP даёт агенту полное зрение [2]. any повсюду → LSP даёт навигацию, но не safety net.
Кодовая база объясняет, почему near-SOTA стек не спасает. Но процесс тоже провален. any-типы он не вылечит — зато свой множитель поднимет с нуля до рабочего значения. И это можно сделать уже завтра.
Промпт «напиши тесты» — это не процесс. Это запрос на чудо. Spec-Driven Development (SDD) — подход, когда вместо одного промпта вы проводите AI через несколько фаз. Каждая фаза — свой артефакт, своя проверка.
Фаза 1. Юзкейсы.
Проанализируй сервис DiscountService (код ниже).
Выпиши все сценарии использования, включая:
- Позитивные (основной поток)
- Негативные (невалидные входные данные)
- Граничные (пороговые значения, пустые коллекции, null)
Для каждого сценария: входные данные, ожидаемый результат, предусловия.
Код пока не пиши.
AI выдаёт 15–25 сценариев. Вы смотрите: есть ли граничные случаи, которые вы знаете? Если нет — добавляете.
Фаза 2. Тесткейсы (без кода).
По утверждённым сценариям напиши тесткейсы в формате Given/When/Then.
Для каждого тесткейса укажи: какой конкретно баг он поймает,
если бизнес-логика сломается.
Если не можешь сформулировать — тесткейс не нужен.
Код пока не пиши.
Требование «объясни, какой баг ловит тест» — фильтр против тавтологических проверок. Тест «проверяю, что мок вернул то, что я в него положил» не проходит этот фильтр, потому что невозможно назвать баг, который он ловит.
Фаза 3. Код.
Напиши код тестов по утверждённым тесткейсам. Ограничения:
- Не модифицируй тестовые данные для прохождения тестов.
- Если тест падает — сообщи о расхождении, не меняй ассерт.
- Следуй стилю из примера: [вставить эталонный тест]
- Не создавай хелперы и абстракции, которых нет в примере.
Фаза 4. Верификация.
Пройди по каждому тесту и проверь:
1. Вызывается ли реальная логика сервиса (не замкнут ли тест на мок)?
2. Соответствует ли ассерт тому, что указано в тесткейсе?
3. Поймает ли тест мутацию (если заменить > на >= — упадёт ли)?
SDD ломает reward hacking: на каждой фазе — своя цель. На фазе 1 — полный список сценариев. На фазе 2 — формулировка «какой баг ловит тест». На фазе 3 — код, соответствующий тесткейсам. Нигде цель не «зелёные галочки».
Да, это жрёт токены — в разы больше, чем «напиши тесты». Но ревью результата занимает в разы меньше. И результат не приходится выбрасывать.
Докладчик, кстати, пришёл к итерационной нарезке сам: «Я разбиваю задачи по 3–4 штуки, делаю потихонечку.» Нарезка — хорошо, но мало. SDD добавляет то, чего в простой нарезке нет: у каждой фазы своя цель и свой артефакт, фильтр «какой баг ловит тест» отсекает тавтологии, а запрет на правку ассертов блокирует reward hacking.
На митапе всплыло ещё кое-что — вещи, которые к AI напрямую не относятся, но определяют весь опыт [3] работы с ним.
Техдолг. any-типы из соседней команды, отсутствие контрактов, слабая типизация — AI-агент усиливает боль [4] от всего этого. Живой разработчик компенсирует техдолг опытом и интуицией [5]. AI превращает его в галлюцинации.
Безопасность. Компания вложила огромные деньги в on-premise железо — и его не хватает. Облако нельзя, документацию отправлять нельзя, бизнес-логику нельзя. Докладчик работает с AI рано утром или поздно вечером, пока меньше людей в очереди. Сам признал: «Это утопия — покупать железо. Через месяц то, что я рассказывал, уже старейшая информация.»
Метрики. Докладчик упомянул кейс из статьи: в одной компании руководство меряет эффективность AI количеством потраченных токенов. Мало потратил — значит, не пользуешься. Он сравнил это с измерением продуктивности разработчика по строкам кода. Аналогия точная.
И наконец — времени на «правильное» внедрение нет. «Я всем этим занимался на выходных. Во время работы не мог бы — не хватает времени.» Компания «внедряет AI», а на настройку промптов и выстраивание процесса — времени нет.
Все четыре штуки — организационные. AI их не создаёт. Зато делает их видимыми.
[ ] Никогда не давайте промпт «напиши тесты». Начинайте с «выпиши сценарии использования, включая граничные и ошибочные». Код — после утверждения сценариев.
[ ] Добавьте в системный промпт: «Не модифицируй тестовые данные и фикстуры для прохождения тестов. Если тест падает — сообщи о расхождении, не чини тест.»
[ ] Дайте AI пример идеального теста из вашего проекта. Few-shot работает лучше любых инструкций.
[ ] Для каждого теста требуйте комментарий: какой конкретно баг он поймает. Нет ответа — тест не нужен.
[ ] Попробуйте готовые инструменты для SDD. Spec Kit [6] (GitHub) проводит агента через четыре фазы: Specify → Plan → Tasks → Implement — туториал [7]. OpenSpec [8] (Fission-AI) превращает спецификацию в живой документ с дельта-маркерами — туториал [9]. Оба open-source, оба работают с основными AI-агентами.
[ ] Если вы на TypeScript — включите strict: true в tsconfig. Каждый устранённый any — это диагностика, которую LSP начинает видеть, а значит, видит и AI-агент.
[ ] Если вы на Python — добавьте type hints хотя бы в публичные API тестируемых сервисов. Pyright (LSP в OpenCode) в strict mode начнёт ловить ошибки, которые сейчас проходят молча.
[ ] Поставьте задачу на ликвидацию техдолга типизации. Это не абстрактный совет: каждый any, заменённый на конкретный тип, буквально включает «зрение» AI-агента через LSP.
[ ] Если команда выбирает стек для нового проекта — учтите, что строго типизированные языки с зрелым LSP-сервером дают AI-агентам кратно больше контекста. Это разница между 50 мс точного ответа и 45 секунд grep по 800 файлам.
[ ] Lint и type-checking как обязательный gate. AI-код не попадает на ревью, пока не пройдёт автопроверки.
[ ] Diff-based ревью: смотрите изменения, а не весь сгенерированный код.
[ ] Итерационная нарезка задач: один эндпоинт, один модуль, одна фича. Не «тесты для всего проекта».
[ ] Следите за бенчмарками: SWE-Bench [10] для кодогенерации, Aider Polyglot [11] для мультиязычных задач. Сегодняшний SOTA через полгода — середняк, конкретные рекомендации устаревают быстрее, чем выходит статья.
[ ] Для on-premise выбирайте open-weight модели с коммерческой лицензией (MIT, Apache 2.0). Привязка к одному вендору обходится дорого — и тем дороже, чем быстрее движется рынок.
[ ] Проверяйте новую модель на ваших реальных задачах, а не только на бенчмарках. Модель, которая блестяще решает SWE-Bench, может буксовать на вашем проекте.
[ ] Но помните: обновление модели не решит проблему any-типов и отсутствия процесса. Модель — множитель, а не замена остальных факторов.
Докладчик закончил словами: «AI — это очень хороший помощник, но надо его правильно использовать. Должна быть архитектура, вы должны грамотно передавать контекст. И ревьюить всё, что он сделал.»
Об авторе. Андрей Ерёменок — CTO, 20+ лет в разработке. Пишу о пересечении AI и инженерных практик. Предыдущие статьи на Хабре [12]. Telegram: Пикник Айтишника [13].
Автор: aeremenok
Источник [14]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/28814
URLs in this post:
[1] ошибку: http://www.braintools.ru/article/4192
[2] зрение: http://www.braintools.ru/article/6238
[3] опыт: http://www.braintools.ru/article/6952
[4] боль: http://www.braintools.ru/article/9901
[5] интуицией: http://www.braintools.ru/article/6929
[6] Spec Kit: https://github.com/github/spec-kit
[7] туториал: https://github.blog/ai-and-ml/generative-ai/spec-driven-development-with-ai-get-started-with-a-new-open-source-toolkit/
[8] OpenSpec: https://github.com/Fission-AI/OpenSpec
[9] туториал: https://openspec.dev/
[10] SWE-Bench: https://www.swebench.com/
[11] Aider Polyglot: https://aider.chat/docs/leaderboards/
[12] Предыдущие статьи на Хабре: https://habr.com/ru/users/aeremenok/publications/articles/
[13] Пикник Айтишника: https://t.me/pcnc_pro
[14] Источник: https://habr.com/ru/articles/1023532/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1023532
Нажмите здесь для печати.