Устаревший или вышедший из употребления паттерн.
ИИ-ассистенты уверенно выдают API, библиотеки и идиомы, которые были актуальны несколько лет назад, но теперь объявлены устаревшими, удалены или вытеснены, потому что их параметрическое знание заморожено на моменте обучающего среза, а старый паттерн по-прежнему статистически наиболее распространён в их корпусе.
##Признаки и симптомы
Ревьюер распознаёт этот запах, когда сгенерированный код работает, но соответствует стилю двух- или трёхлетней давности учебника: он вызывает API, о которых фреймворк теперь предупреждает, закрепляет старые мажорные версии или тянется к легаси-идиомам вместо современной stdlib/синтаксиса. Характерные признаки:
- Предупреждения об устаревании при установке/сборке/выполнении, которые модель проигнорировала (
DeprecationWarning,[DEP0005],componentWillMount is deprecated). - Идиомы, предшествующие текущему базовому уровню языка:
varвместоconst/let, колбэки/цепочки.then()там, где кодовая база используетasync/await,require()в ESM-проекте,moment.jsвместоIntl/Temporal/date-fns. - Вызовы библиотек, удалённые или объявленные устаревшими в той версии, что у вас фактически установлена: удалены — pandas
df.append(...)(нет начиная с pandas 2.0) и.ix[](нет начиная с 1.0); устарели, но пока доступны — легаси-методы жизненного цикла React,datetime.utcnow()(устарел начиная с Python 3.12), Nodenew Buffer(),url.parse(). - Легаси-настройки безопасности по умолчанию, доминирующие в старых обучающих данных: хеширование MD5/SHA1,
crypto.createCipher, сырой SQL из конкатенации строк. - Переизобретение того, что платформа теперь поставляет (самописные
deepClone,groupBy, UUID) вместоstructuredClone,Object.groupBy,crypto.randomUUID().
// Сгенерировано ИИ: компилируется, но каждая строка — теперь устаревший паттерн
const crypto = require('crypto'); // ESM-проект использует import
const buf = new Buffer(userInput); // Buffer() устарел с Node 6
const id = crypto.createHash('md5') // MD5 устарел для любого применения в безопасности
.update(token).digest('hex');
const cipher = crypto.createCipher('aes-256-cbc', key); // API удалённого пути, слабый KDF
request(apiUrl, (err, res, body) => { /* ... */ }); // 'request' не сопровождается с 2020 года
##Причины проблемы
Почему модели это порождают
- Устаревание из-за обучающего среза. Параметрическое знание LLM заморожено на моменте среза, но библиотеки продолжают развиваться. Измеряющие это исследования обнаруживают долю использования устаревших API в 25–38 % по восьми Python-библиотекам в дополнениях LLM (Wang et al., ICSE 2025), а на 328 задачах с привязкой к версии библиотеки коммерческие модели в базовом режиме решают лишь 48–51 % (GitChameleon 2.0, ACL 2026). На момент вывода нет осведомлённости об API/версиях в реальном времени.
- Частота следующего токена благоприятствует старому способу. Корпус содержит годы легаси-кода, поэтому статистически наиболее вероятным дополнением часто оказывается устаревшее. OX Security отмечает, что «конкатенация строк чаще всего встречается в обучающих данных», а «MD5 часто появляется в примерах хеширования по всему обучающему корпусу» (OX Security); Veracode же обнаружила, что при выборе между безопасным и небезопасным методом модели выбирали небезопасный в 45 % случаев (отчёт Veracode 2025 GenAI Code Security Report).
- Нет контекста репозитория. Модель обычно не знает, какую мажорную версию вы на самом деле установили, что закреплено в вашем lockfile или что
lib/hash.tsуже существует, — поэтому не может ориентироваться на API ваших версий. Эно Рейес (Factory) называет агента «отличным распознавателем паттернов»: «хороший код в паттерне — значит, хороший код на выходе»; не имея актуального примера, агент откатывается к среднему по своим обучающим данным (интервью с Эно Рейесом в блоге Stack Overflow). - Поощрение за «чтобы работало» + угодничество. Модели оптимизируют код, который правдоподобно компилируется и удовлетворяет запросу, а не «использует текущий, поддерживаемый путь». Устаревший вызов всё ещё работает сегодня, поэтому выглядит корректным.
Чем это вредит
- Сопровождаемость / поломка с бомбой замедленного действия. «Устарело сегодня» означает удалено в следующем мажоре: код работает, пока обновление незаметно его не сломает, и такие паттерны копипастят, а не рефакторят. Анализ GitClear 2025 года (211 млн изменённых строк, 2020–2024) показал, что доля строк, связанных с рефакторингом («перемещённых»), упала с 25% изменённых строк в 2021 году до менее чем 10% в 2024-м, а доля скопированных/вставленных строк за тот же период выросла с 8,3% до 12,3%; 2024 год стал первым, когда скопированных строк оказалось больше, чем перемещённых, а число блоков из пяти и более дублирующихся строк за 2024 год выросло примерно в восемь раз (GitClear 2025). Поэтому одна устаревшая идиома расползается по коду, вместо того чтобы быть исправленной один раз.
- Корректность. Вытесненные API часто имеют тонко отличающуюся семантику (
utcnow()возвращает «наивный» datetime; легаси-методы жизненного цикла срабатывают в другое время), порождая баги, которые проходят беглую дымовую проверку. - Безопасность. Устаревшая криптография (MD5,
createCipher) и устаревшие версии пакетов несут известные CVE, которые модель никогда не сверяет ни с одной базой уязвимостей; в тестах Veracode 2025 года сгенерированный ИИ код вносил уязвимости в 45 % случаев (Veracode). - Нагрузка на ревью и техдолг. Каждое вхождение — это отдельная ручная правка, а поскольку паттерн дублируется, а не централизуется, издержки нарастают по всей кодовой базе.
##Лечение
Тактики ревью и промптинга
- Укажите версии в промпте или контексте. «Мы на React 19, pandas 2.2, Node 22 (ESM) — используй только API, актуальные в этих версиях.» Подтяните актуальную документацию в контекст (docs-MCP / Context7) вместо доверия параметрической памяти.
- Принудите прогон инструментов. Потребуйте, чтобы модель прогнала линтер, проверку типов и
npm outdated/pip list --outdated, а затем исправила каждое предупреждение об устаревании, прежде чем объявлять готово. Предупреждения об устаревании — самый дешёвый возможный детектор; не позволяйте их игнорировать. - Требуйте переиспользования вместо переизобретения. «Используй существующий хелпер в
lib/hash.ts; не пиши хеширование вручную.» Укажите на канонический модуль, чтобы модель не сгенерировала легаси-версию заново (Дублирование кода). - Кодмоды вместо ручных правок. Для системных обновлений запускайте официальные кодмоды/модернизаторы, а не просите модель переписать вручную:
npx @next/codemod, кодмоды React, правилаpyupgrade/RuffUP. Они детерминированы и полны.
Рефакторинг — замена устаревшего API его поддерживаемым преемником — это по сути приём «Замена алгоритма», а сведение дублированных устаревших вызовов в один хелпер — «Извлечение метода» против запаха «Дублирование кода»:
// До — API устаревших/удалённых путей
const buf = new Buffer(userInput);
const id = crypto.createHash('md5').update(token).digest('hex');
const cipher = crypto.createCipher('aes-256-cbc', key);
// После — актуально, поддерживается, безопасно для криптографии
import crypto from 'node:crypto';
const buf = Buffer.from(userInput); // безопасный конструктор
const id = crypto.createHash('sha256').update(token).digest('hex');
const iv = crypto.randomBytes(16); // явный IV
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
// (для паролей используйте argon2/bcrypt — а не сырой хеш)
Для зависимостей эквивалентное исправление — поднять закреплённую версию и дать кодмоду мигрировать места вызовов, а затем перекрыть будущий дрейф в CI (правило линтера ниже + политика Dependabot/Renovate), чтобы модель не могла незаметно вновь ввести старый паттерн.
##Обнаружение анализаторами
- ESLint (eslint-plugin-n) n/no-deprecated-api — Запрет устаревших core-API Node.js (Buffer(), crypto.createCipher, url.parse и т. д.)
- typescript-eslint @typescript-eslint/no-deprecated — Помечает использование любого символа, отмеченного @deprecated в его JSDoc/TS-объявлении
- ESLint (eslint-plugin-react) react/no-deprecated — Помечает устаревшие API React и методы жизненного цикла (componentWillMount и т. д.)
- SonarQube / SonarSource RSPEC-1874 — Сообщает об использовании кода, аннотированного/помеченного как устаревший
- Ruff pyupgrade (UP) — Обнаруживает и переписывает устаревшие/вытесненные идиомы Python в современные эквиваленты
- Dependabot version-updates — Помечает устаревшие версии зависимостей (грань «протухшего пакета» этого запаха)