ConstructiCat Logo
CodeBust.
Browse section ▾

Устаревший или вышедший из употребления паттерн.

ИИ-ассистенты уверенно выдают 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(), Node new 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/Ruff UP. Они детерминированы и полны.

Рефакторинг — по сути это Замена алгоритма / замена устаревшего 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Помечает устаревшие версии зависимостей (грань «протухшего пакета» этого запаха)