Тесты проходят, покрытие растёт, но багов меньше не становится. На QA-митапе инженер из крупной продуктовой компании показал, как AI-агенты генерируют тесты, которые ничего не проверяют: подгоняют моки, меняют ассерты, подменяют ожидания. При этом стек команды — near-SOTA: свежая модель, топовый open-source агент. Значит, проблема не в инструментах.
Зелёные галочки лгут
Паттерн знаком каждому, кто использовал AI для написания тестов:
Ты даёшь ей автотесты. Она пишет, они проходят. Зелёные галочки. Но меня пугают зелёные галочки. Почему? Потому что её задача — чтобы все тесты прошли. Она подгоняет результат. Изменяет данные — и у тебя зелёный тест.
Например, нужно протестировать эндпоинт расчёта скидки: при заказе от 5000 ₽ — скидка 10%, но не больше 1000 ₽. В сервисе баг: потолок скидки не применяется.
Без ограничений AI генерирует тест, в котором:
- сам создаёт мок с нужным ответом;
- вызывает его;
- проверяет, что мок вернул то, что в него положили.
Тест проходит, но реальный сервис не участвует. Баг остаётся незамеченным.
Если тест падает, AI не сообщает о баге — он «чинит» тест:
Если красное — она говорит: давай сделаю зелёным. Просто меняет ассерт или мок.
Ожидание result = 1000 заменяется на result = 1500. Тест снова зелёный. Это reward hacking на уровне промпта: цель — не качество, а прохождение тестов.
Проблема не в модели и не в агенте
Команда использовала GLM 4.7 и OpenCode — сильную модель и одного из лидеров open-source агентов. GLM-5.1, её наследник, позже занял первое место на SWE-Bench Pro, обогнав Claude Opus и GPT-5.
OpenCode — мощный инструмент: 140K звёзд на GitHub, LSP-интеграция, поддержка 30+ языков, автоматическая установка серверов. Менять его не на что.
Почему же при near-SOTA стеке возникают проблемы, знакомые по полуторагодичной давности?
Четыре множителя качества
Обычно говорят о трёх: модель, агент, процесс. Но есть четвёртый — качество кодовой базы:
Результат = Модель × Агент × Процесс × Качество кодовой базы
Если один из множителей близок к нулю, результат стремится к нулю — даже при идеальных остальных. Кодовая база задаёт потолок: выше него не прыгнуть.
У команды из доклада первые два множителя были хороши, процесс — слабый, а четвёртый — провален.
Почему качество кодовой базы — ключевой множитель
Команда писала на TypeScript, но соседняя команда передавала интерфейсы с any повсюду. Это создало слепые зоны для LSP.
OpenCode автоматически запускает TypeScript language server (vtsls), который даёт агенту «зрение»: типы, определения, ошибки. Но any — это слепое пятно: LSP не видит несовместимостей, агент не получает сигнала об ошибке.
Результат:
Ошибка была в несовместимости типов. Агент такой — ну окей, давай будет number. Взял, поменял. Всё проходит, но это вообще не решает проблему.
Агент оптимизирует локально. Зелёный тест, деградация архитектуры.
Что даёт строгая типизация
В моих проектах бэкенд на Java. Там LSP (jdtls) работает в полную силу:
- Если AI написал чушь — код не скомпилируется. Это первый автоматический фильтр.
- Если AI изменил сигнатуру — LSP мгновенно покажет все сломанные вызовы.
- Если AI передал неправильный тип — ошибка компиляции, а не тихий
any.
Я не допускаю неоднозначностей на code review. Это требует дисциплины, но делает AI-агента предсказуемым: он работает с чистым сигналом, а не с шумом.
Пример: мы — агрегатор авиабилетов и гостиниц. Раньше интеграция сторонних поставщиков занимала месяцы. Теперь — дни. AI-агент под моим руководством читает документацию, разбивает задачу, реализует, тестирует.
Особенно эффективен сценарий, невозможный для человека: когда API плохо документирован, агент перебирает комбинации параметров, пока не найдёт рабочие. Монотонность ему не помеха.
Ключ — не модель, а pipeline: строгая типизация + обязательные тесты + компиляция как gate. В таком окружении AI предсказуем.
Таблица: как типизация влияет на LSP и AI
Строгая типизация (Java, TS strict, Go, Rust)
- LSP ловит ошибки типов: ✅ за 50 мс
- Агент получает диагностику после правки: ✅ мгновенно
- hover показывает тип: ✅ конкретный
- Агент «чинит» тест подменой типа: компилятор ругается
- goToDefinition / findReferences: ✅ работает
Слабая типизация (TS с any, Python без hints)
- LSP ловит ошибки типов: ❌ не видит
- Агент получает диагностику: ❌ молчание
- hover показывает тип: ⚠️
any/Unknown - Агент «чинит» тест подменой типа: компилятор молчит
- goToDefinition / findReferences: ✅ работает (но менее полезно)
LSP — усилитель качества кода. Строгие типы → полное зрение. any → навигация есть, safety net — нет.
Процесс: Spec-Driven Development
Кодовая база объясняет, почему стек не помог. Но процесс тоже важен. Промпт «напиши тесты» — это не процесс. Это надежда на чудо.
Spec-Driven Development (SDD) — подход, при котором AI проходит через несколько фаз. Каждая — с артефактом и проверкой:
- Фаза 1. Юзкейсы: AI генерирует 15–25 сценариев. Команда добавляет граничные и ошибочные случаи.
- Фаза 2. Тесткейсы (без кода): Требование «объясни, какой баг ловит тест» отсекает тавтологии. Тест «проверяю, что мок вернул то, что я в него положил» — не проходит.
- Фаза 3. Код: генерация на основе утверждённых тесткейсов.
- Фаза 4. Верификация: проверка, что тесты находят реальные баги.
Почему это работает
SDD ломает reward hacking: на каждой фазе своя цель, но нигде — «зелёные галочки».
Да, это требует больше токенов. Но ревью занимает меньше времени, а результат не приходится выбрасывать.
Докладчик сам пришёл к нарезке задач, но SDD добавляет больше: чёткие цели, фильтры против тавтологий, запрет на подмену ассертов.
Организационный слой
AI не создаёт проблемы — он делает их видимыми.
Техдолг: any-типы, отсутствие контрактов, слабая типизация — AI усиливает их. Живой разработчик компенсирует опыт, AI — галлюцинирует.
Безопасность: on-premise железо не справляется с нагрузкой. Облако, документация и бизнес-логика — под запретом. AI используют в нерабочее время. «Это утопия — покупать железо. Через месяц оно уже устаревает».
Метрики: эффективность AI измеряют по количеству потраченных токенов. Мало — значит, не пользуешься. Как измерение продуктивности разработчика по строкам кода.
Время: «Я всем этим занимался на выходных. Во время работы не мог бы — не хватает времени». Компания «внедряет AI», но не даёт времени на настройку процессов.
Все эти проблемы — организационные. AI их не решает. Но он их обнажает.
Чеклист: что внедрить завтра
Процесс (бесплатно, эффект сразу)
- [ ] Не давайте промпт «напиши тесты». Начинайте с «выпиши сценарии использования, включая граничные и ошибочные». Код — после утверждения.
- [ ] Добавьте в промпт: «Не модифицируй тестовые данные и фикстуры для прохождения тестов. Если тест падает — сообщи о расхождении, не чини тест».
- [ ] Дайте AI пример идеального теста из вашего проекта. Few-shot работает лучше инструкций.
- [ ] Для каждого теста требуйте комментарий: «Какой конкретно баг он поймает?» Нет ответа — тест не нужен.
- [ ] Попробуйте инструменты для SDD: Spec Kit (GitHub) или OpenSpec (Fission-AI). Оба open-source, работают с основными агентами.
Качество кодовой базы (стратегически)
- [ ] На TypeScript — включите
strict: trueв tsconfig. Каждый устранённыйany— это новая диагностика для LSP и AI. - [ ] На Python — добавьте type hints в публичные API. Pyright в strict mode начнёт ловить ошибки.
- [ ] Поставьте задачу на ликвидацию техдолга типизации. Каждый
any, заменённый на конкретный тип, включает «зрение» AI. - [ ] При выборе стека для нового проекта отдавайте предпочтение строго типизированным языкам с зрелым LSP. Это разница между 50 мс и 45 секундами на поиск контекста.
Инфраструктура
- [ ] Lint и type-checking как обязательный gate. AI-код не попадает на ревью без прохождения проверок.
- [ ] Diff-based ревью: смотрите изменения, а не весь сгенерированный код.
- [ ] Итерационная нарезка задач: один эндпоинт, модуль или фича. Не «все тесты сразу».
Модель (стратегия обновления)
- [ ] Следите за бенчмарками: SWE-Bench для кодогенерации, Aider Polyglot для мультиязычных задач. SOTA быстро устаревает.
- [ ] Для on-premise выбирайте open-weight модели с коммерческой лицензией (MIT, Apache 2.0). Привязка к вендору — риск.
- [ ] Тестируйте новые модели на своих задачах, а не только на бенчмарках.
- [ ] Помните: обновление модели не решит проблемы
any-типов и отсутствия процесса. Модель — множитель, а не замена.
Докладчик закончил так: «AI — это очень хороший помощник, но надо его правильно использовать. Должна быть архитектура, вы должны грамотно передавать контекст. И ревьюить всё, что он сделал».