Представьте: в пятницу вечером руководитель уверенно сообщает, что переход со Spring Boot 3.5 на 4.0 будет простым и его можно закончить за выходные. Скорее всего, вы уточните, на чём основана такая оценка, и напомните о несовместимых изменениях.

Теперь заменим руководителя на GitHub Copilot или другую большую языковую модель (LLM). Она предлагает рефакторинг метода, новую зависимость или готовый шаблон реализации. Ответ выглядит аккуратно и звучит уверенно. Модель не предупреждает, что может ошибаться. Возникает вопрос: проверяем ли мы такой код столь же строго, как заявление руководителя, или сразу принимаем его?

По данным отчёта Sonatype за 2026 год, 27,76% из 36 870 сгенерированных LLM рекомендаций по обновлению зависимостей ссылались на несуществующие версии этих зависимостей. Получается, одна из трёх-четырёх зависимостей — выдуманная. И проблема не ограничивается зависимостями: та же ложная уверенность может присутствовать в любой строке кода, созданной ИИ.

Мозг подталкивает нас к доверию

Одна из причин — эвристика беглости восприятия (fluency heuristic). Если информация изложена ясно, понятно и привычно, то человек склонен считать её более правдоподобной.

Организационный психолог Томас Чаморро-Премузик (Tomas Chamorro-Premuzic) отмечает, что уверенность человека слабо связана с его реальной компетентностью. Люди часто доверяют тому, кто говорит первым и звучит убедительно, даже если более тихий участник обсуждения лучше понимает проблему.

Ответы LLM эффективно запускают этот когнитивный механизм. Модели почти всегда выдают связный, структурированный и уверенный ответ. Имена классов соответствуют соглашениям, обработка исключений выглядит разумно, а код напоминает тысячи знакомых примеров. Мозг быстро распознаёт привычный шаблон и предлагает не тратить время на дополнительную проверку.

Хорошее оформление, однако, ничего не говорит о корректности самого кода. Исследователи Университета Карнеги–Меллона показали, что модель выдумала от 69% до 88% ответов, сохраняя при этом авторитетный тон. Из-за такой подачи ошибки трудно заметить даже опытным специалистам.

Где Java-разработчики особенно уязвимы

Правдоподобные, но вымышленные зависимости

Maven Central содержит огромное количество артефактов и их версий. Модель легко создаёт координаты, которые выглядят настоящими. Например, она может перепутать org.apache.commons:commons-csv с org.apache.commons:commons-text или предложить несуществующий артефакт commons-utils.

Регулярные правила именования помогают LLM правдоподобно угадывать, но одновременно помогают атакующему. Он может зарегистрировать пакет с вымышленным именем, которое уже встречается в ответах моделей. Такой приём называют slopsquatting — разработчик копирует зависимость из ответа ИИ и случайно подключает пакет злоумышленника. Один такой вымышленный пакет скачали более 30 000 раз всего за три месяца.

Скрытое влияние транзитивных зависимостей

В pom.xml могут быть явно указаны десятки зависимостей, тогда как Maven фактически разрешает сотни транзитивных компонентов. Предлагая обновить одну верхнеуровневую библиотеку, модель обычно не видит полное дерево проекта и не знает всех последствий.

Например, обновление spring-cloud-openfeign способно изменить версию commons-fileupload, которая приходит через feign-form. Именно такой сценарий произошёл с CVE-2025-48976.

Что имеется ввиду

В Spring Cloud OpenFeign 4.3.0–4.3.1 модуль feign-form-spring версии 13.6 подтягивал уязвимый commons-fileupload версии 1.5. Обновление OpenFeign до версии 4.3.2 перевело feign-form-spring на версию 13.6.1, где commons-fileupload обновлена до безопасной версии 1.6.0 с устранённой уязвимостью.

Безопасное на вид изменение одной строки в pom.xml нельзя оценивать отдельно от всего дерева зависимостей.

Шаблонный код, который компилируется, но работает неверно

Java-код часто содержит много повторяемой структуры: конфигурационные классы, аннотации Spring, репозитории, DTO и маппинг. LLM хорошо воспроизводит такие шаблоны, поэтому результат выглядит профессионально.

Но компиляция и внешнее сходство с принятым шаблоном не гарантируют правильного поведения. Возможные примеры:

  • @Transactional установлен не на том методе;

  • SecurityFilterChain выглядит полным, но оставляет эндпоинт (endpoint) без защиты;

  • конфигурация ObjectMapper незаметно отбрасывает неизвестные поля;

  • корректный с точки зрения типов код создаёт узкое место под нагрузкой.

В таких случаях ошибка находится в семантике, а не в синтаксисе.

Устаревшие способы работы с API

Модель, обученная на старых проектах, может уверенно предлагать устаревшие API, удалённые методы или подходы, рассчитанные на Java 11, хотя проект работает на Java 21. Без контекста она не знает точные версии JDK, Spring Boot и других компонентов. Код из прошлогоднего стека может не собраться или вести себя иначе в текущем окружении.

Что обнаруживают инструменты разработки

Часть ошибок выявляется автоматически. Если координаты Maven не существуют, mvn compile не сможет разрешить зависимость. IntelliJ IDEA заранее подсветит проблему. Компилятор остановит сборку при ошибке типов, а устаревший API часто вызовет предупреждение.

Сложнее обнаружить то, что успешно проходит сборку:

  • реальную библиотеку с известной CVE;

  • компилируемый код с уязвимостью;

  • корректную реализацию, которая плохо масштабируется;

  • изменение верхнеуровневой зависимости, подтягивающее небезопасную транзитивную версию.

Для проверки зависимостей нужно:

  1. Выполнять mvn dependency:tree -Dverbose, чтобы видеть изменения полного транзитивного дерева.

  2. Запускать в CI сканирование известных уязвимостей с помощью OWASP dependency-check-maven, Snyk, Sonatype Lifecycle или аналогичного SCA-инструмента.

  3. Явно фиксировать транзитивные версии через Maven <dependencyManagement> либо ограничения платформы Gradle.

Сгенерированный код следует проверять с той же строгостью, что и pull request от незнакомого разработчика. ИИ можно считать новым участником команды: его код может компилироваться и хорошо выглядеть, но у команды ещё нет оснований доверять его решениям.

Требуйте от модели объяснений

Автоматические проверки находят проблему уже после генерации. Риск можно уменьшить и раньше, изменив сам способ общения с моделью.

1. Спросите, что модель не знает

Перед принятием решения задайте вопросы:

  • «Какие предположения ты делаешь о моём проекте?»

  • «В чём ты не уверен?»

Полезнее получить признание, что модель не знает версию Java или Spring Boot, чем позволить ей молча угадать эти данные.

2. Передайте реальный контекст

Запрос «напиши REST-контроллер» оставляет слишком много неизвестного. Добавьте существующий код, файл pom.xml, версии платформы и ограничения проекта. Чем меньше модели приходится додумывать, тем ниже вероятность появления вымышленных деталей.

3. Запросите варианты и компромиссы

Попросите перечислить несколько решений, их преимущества, недостатки и условия применимости. Если модель способна предложить только один вариант, это повод насторожиться. Сравнение подходов помогает увидеть места, где ответ построен на предположениях.

4. Проверяйте обоснование, а не только результат

Спросите, почему выбран именно этот подход. Ответы вроде «это лучшая практика» или «так рекомендуется» слишком расплывчаты. Хорошее объяснение должно ссылаться на конкретные причины: совместимость с версиями проекта, безопасность, производительность или эксплуатационные ограничения.

5. Считайте первый ответ черновиком

Первый результат часто выглядит настолько убедительно, что его хочется сразу вставить в проект. Полезная привычка — всегда относиться к первой генерации как к исходному материалу для проверки и доработки, а не как к готовому решению.

Цена чрезмерной уверенности

Эти приёмы не устраняют проблему полностью, но меняют взаимодействие: разработчик не просто принимает результат, а требует от модели объяснить решение.

Исследование Microsoft среди специалистов умственного труда показало связь между более частым использованием генеративного ИИ и снижением самостоятельно прилагаемых усилий критического мышления. Это проявление иронии автоматизации: инструмент берёт на себя рутинное мышление и постепенно ослабляет навык, который нужен человеку для обнаружения его ошибок.

Для разработчика главный риск заключается не в одной катастрофической рекомендации. Опаснее же привычка без проверки принимать хорошо оформленный и уверенно поданный код.

ИИ-инструменты полезны, но их уверенный тон не является доказательством качества. Навык проверки стоит формировать уже сейчас — именно тогда, когда дополнительная проверка кажется лишней.