Логотип ConstructiCat
CodeBust.
Навигация по разделу ▾

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

ИИ-ассистенты уверенно выдают 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), 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 года

##Причины проблемы

Почему модели это порождают

  • Устаревание из-за обучающего среза. Параметрическое знание 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/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), чтобы модель не могла незаметно вновь ввести старый паттерн.

##Обнаружение анализаторами

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