Устаревший или вышедший из употребления паттерн.
ИИ-ассистенты уверенно выдают API, библиотеки и идиомы, которые были актуальны несколько лет назад, но теперь объявлены устаревшими, удалены или вытеснены, потому что их параметрическое знание заморожено на моменте обучающего среза, а старый паттерн по-прежнему статистически наиболее распространён в их корпусе.
##Signs and Symptoms
Ревьюер распознаёт этот запах, когда сгенерированный код работает, но соответствует стилю двух- или трёхлетней давности учебника: он вызывает API, о которых фреймворк теперь предупреждает, закрепляет старые мажорные версии или тянется к легаси-идиомам вместо современной stdlib/синтаксиса. Характерные признаки:
- Предупреждения об устаревании при установке/сборке/выполнении, которые модель проигнорировала (
DeprecationWarning,[DEP0005],componentWillMount is deprecated). - Идиомы, предшествующие текущему базовому уровню языка:
varвместоconst/let, колбэки/цепочки.then()там, где кодовая база используетasync/await,require()в ESM-проекте,moment.jsвместоIntl/Temporal/date-fns. - Вызовы библиотек, удалённые в той версии, что у вас фактически установлена: pandas
df.append(...)/.ix[], легаси-методы жизненного цикла React,datetime.utcnow(), 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 года
##Reasons for the Problem
Почему модели это порождают
- Устаревание из-за обучающего среза. Параметрическое знание LLM заморожено на моменте среза, но библиотеки продолжают развиваться. Измеряющие это исследования обнаруживают долю использования устаревших API в 25–38 % по восьми Python-библиотекам в дополнениях LLM (Wang et al., ICSE 2025), а даже самые передовые модели набирают лишь 48–51 % в генерации с учётом версии (GitChameleon 2.0). На момент вывода нет осведомлённости об API/версиях в реальном времени.
- Частота следующего токена благоприятствует старому способу. Корпус содержит годы легаси-кода, поэтому статистически наиболее вероятным дополнением часто оказывается устаревшее. OX Security отмечает, что модели по умолчанию выбирают конкатенацию строк и MD5/SHA1 именно потому, что те «чаще всего встречаются в обучающих данных», а Veracode обнаружила, что при наличии выбора модели выбирают небезопасный/легаси-вариант примерно в 45 % случаев (OX Security).
- Нет контекста репозитория. Модель обычно не знает, какую мажорную версию вы на самом деле установили, что закреплено в вашем lockfile или что
lib/hash.tsуже существует, — поэтому не может нацелиться на вашу поверхность API. Формулировка Stack Overflow: агент — «отличный распознаватель паттернов… хороший код на входе, хороший код на выходе»: не имея актуального примера, он откатывается к среднему по своим обучающим данным (интервью с Рейесом). - Поощрение за «чтобы работало» + угодничество. Модели оптимизируют код, который правдоподобно компилируется и удовлетворяет запросу, а не «использует текущий, поддерживаемый путь». Устаревший вызов всё ещё работает сегодня, поэтому выглядит корректным.
Чем это вредит
- Сопровождаемость / поломка с бомбой замедленного действия. «Устарело сегодня» означает удалено в следующем мажоре: код работает, пока обновление незаметно его не сломает, и такие паттерны копипастят, а не рефакторят. Анализ 211 млн строк от GitClear показывает, что доля копипастных строк выросла с 8,3 % до 12,3 %, тогда как доля отрефакторенных («перемещённых») строк упала с 24,1 % до 9,5 % за 2020–2024 годы, при этом дублирующихся блоков стало примерно в 8 раз больше, — так что единичная протухшая идиома распространяется, вместо того чтобы быть исправленной один раз (GitClear 2025).
- Корректность. Вытесненные API часто имеют тонко отличающуюся семантику (
utcnow()возвращает «наивный» datetime; легаси-методы жизненного цикла срабатывают в другое время), порождая баги, которые проходят беглую дымовую проверку. - Безопасность. Устаревшая криптография (MD5,
createCipher) и устаревшие версии пакетов несут известные CVE, которые модель никогда не сверяет ни с одной базой уязвимостей, — ключевой фактор вывода о том, что большинство сгенерированного ИИ кода поставляется хотя бы с одной уязвимостью. - Нагрузка на ревью и техдолг. Каждое вхождение — это отдельная ручная правка, а поскольку паттерн дублируется, а не централизуется, издержки нарастают по всей кодовой базе.
##Treatment
Тактики ревью и промптинга
- Укажите версии в промпте или контексте. «Мы на 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), чтобы модель не могла незаметно вновь ввести старый паттерн.
##Detected by
- 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 ("@Deprecated" code should not be used) — Сообщает об использовании кода, аннотированного/помеченного как устаревший
- Ruff pyupgrade (UP) rules — Обнаруживает и переписывает устаревшие/вытесненные идиомы Python в современные эквиваленты
- Dependabot version-updates — Помечает устаревшие версии зависимостей (грань «протухшего пакета» этого запаха)