---
title: "Outdated or Deprecated Pattern"
type: "ai-smell"
slug: "deprecated-pattern"
url: "http://localhost:3000/en/ai-smells/deprecated-pattern.md"
category: "Maintenance"
description: "AI assistants confidently emit APIs, libraries, and idioms that were current years ago but are now deprecated, removed, or superseded, because their parametric knowledge is frozen at a training cutoff and the old pattern is still the statistically most common one in their corpus."
---
# Outdated or Deprecated Pattern

> AI assistants confidently emit APIs, libraries, and idioms that were current years ago but are now deprecated, removed, or superseded, because their parametric knowledge is frozen at a training cutoff and the old pattern is still the statistically most common one in their corpus.

## Signs and Symptoms

A reviewer recognizes this smell when generated code _runs_ but matches the style of a two- or three-year-old tutorial: it calls APIs the framework now warns about, pins old major versions, or reaches for legacy idioms instead of the modern stdlib/syntax. Tell-tale signs:

* **Deprecation warnings at install/build/runtime** that the model ignored (`DeprecationWarning`, `[DEP0005]`, `componentWillMount is deprecated`).
* **Idioms that predate the current language baseline**: `var` over `const`/`let`, callbacks/`.then()` chains where the codebase uses `async/await`, `require()` in an ESM project, `moment.js` instead of `Intl`/`Temporal`/date-fns.
* **Library calls removed in the version you actually have installed**: pandas `df.append(...)` / `.ix[]`, React legacy lifecycles, `datetime.utcnow()`, Node `new Buffer()`, `url.parse()`.
* **Legacy security defaults** that dominate old training data: MD5/SHA1 hashing, `crypto.createCipher`, raw string-concatenated SQL.
* **Reinventing what the platform now ships** (hand-rolled `deepClone`, `groupBy`, UUID) instead of `structuredClone`, `Object.groupBy`, `crypto.randomUUID()`.

```js
// AI-generated: compiles, but every line is a now-deprecated pattern
const crypto = require('crypto');                 // ESM project uses import

const buf = new Buffer(userInput);                // Buffer() deprecated since Node 6
const id  = crypto.createHash('md5')              // MD5 deprecated for any security use
              .update(token).digest('hex');
const cipher = crypto.createCipher('aes-256-cbc', key);  // removed-path API, weak KDF

request(apiUrl, (err, res, body) => { /* ... */ });      // 'request' unmaintained since 2020

```

## Reasons for the Problem

**Why models produce it**

* **Training-cutoff staleness.** An LLM's parametric knowledge is frozen at its cutoff, but libraries keep evolving. Studies measuring this find a **25–38% deprecated-API usage rate** across eight Python libraries in LLM completions ([Wang et al., ICSE 2025](https://dl.acm.org/doi/10.1109/ICSE55347.2025.00245)), and even state-of-the-art models score only **48–51%** on version-conditioned generation ([GitChameleon 2.0](https://openreview.net/pdf?id=wtqdcVfJUN)). There is no real-time API/version awareness at inference time.
* **Next-token frequency favors the old way.** The corpus contains _years_ of legacy code, so the statistically most likely completion is often the obsolete one. OX Security notes models default to string concatenation and MD5/SHA1 precisely because those "appear most frequently in training data," and Veracode found models pick the insecure/legacy option **\~45% of the time** when given a choice ([OX Security](https://www.ox.security/blog/vibe-coding-security/)).
* **No repo context.** The model usually doesn't know which major version you actually installed, what your lockfile pins, or that `lib/hash.ts` already exists — so it can't target _your_ API surface. Stack Overflow's framing: an agent is "a great pattern recognizer… great code in, great code out" — given no current example, it falls back to the average of its training data ([Reyes Q&A](https://stackoverflow.blog/2026/02/04/code-smells-for-ai-agents-q-and-a-with-eno-reyes-of-factory/)).
* **"Make it work" reward + sycophancy.** Models optimize for code that plausibly compiles and satisfies the prompt, not for "uses the current, supported path." The deprecated call still works _today_, so it looks correct.

**Why it hurts**

* **Maintainability / time-bomb breakage.** Deprecated today means _removed in the next major_ — the code works until an upgrade silently breaks it, and these patterns get copy-pasted rather than refactored. GitClear's 211M-line analysis shows copy/pasted lines rose **8.3%→12.3%** while refactored ("moved") lines fell **24.1%→9.5%** from 2020–2024, with duplicate blocks up \~8x — so a single stale idiom propagates instead of being fixed once ([GitClear 2025](https://www.gitclear.com/ai%5Fassistant%5Fcode%5Fquality%5F2025%5Fresearch)).
* **Correctness.** Superseded APIs often have subtly different semantics (`utcnow()` returns a naive datetime; legacy lifecycles fire at different times), producing bugs that pass a quick smoke test.
* **Security.** Deprecated crypto (MD5, `createCipher`) and outdated package versions carry known CVEs the model never checks against any advisory database — a core driver of the finding that the _majority_ of AI-generated code ships with at least one vulnerability.
* **Review load & tech debt.** Each occurrence is a separate hand-fix, and because the pattern is duplicated rather than centralized, the cost compounds across the codebase.

## Treatment

**Review & prompting tactics**

* **State the versions in the prompt or context.** "We're on React 19, pandas 2.2, Node 22 (ESM) — use only APIs current in those versions." Pull the current docs into context (a docs MCP / Context7) instead of trusting parametric memory.
* **Force a tool pass.** Require the model to run the linter, typechecker, and `npm outdated` / `pip list --outdated`, then fix every deprecation warning before declaring done. Deprecation warnings are the cheapest possible detector — don't let them be ignored.
* **Require reuse over reinvention.** "Use the existing helper in `lib/hash.ts`; do not hand-roll hashing." Point at the canonical module so the model doesn't regenerate a legacy version (Duplicate Code).
* **Codemods over hand-edits.** For systematic upgrades, run the official codemods/modernizers rather than asking the model to rewrite by hand: `npx @next/codemod`, React codemods, `pyupgrade`/Ruff `UP` rules. They are deterministic and complete.

**The refactor** — this is essentially _Substitute Algorithm_ / replace-deprecated-API, and consolidating the duplicated stale calls into one helper applies _Extract Function_ against _Duplicate Code_:

```js
// Before — deprecated/removed-path APIs
const buf    = new Buffer(userInput);
const id     = crypto.createHash('md5').update(token).digest('hex');
const cipher = crypto.createCipher('aes-256-cbc', key);

// After — current, supported, security-sane
import crypto from 'node:crypto';

const buf    = Buffer.from(userInput);                 // safe constructor
const id     = crypto.createHash('sha256').update(token).digest('hex');
const iv     = crypto.randomBytes(16);                 // explicit IV
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
// (for passwords, use argon2/bcrypt — not a raw hash)

```

For dependencies, the equivalent fix is bumping the pin and letting the codemod migrate call sites, then gating future drift in CI (lint rule below + a Dependabot/Renovate policy) so the model can't silently reintroduce the old pattern.

## Detected by

- **ESLint (eslint-plugin-n)** `n/no-deprecated-api` — Disallow deprecated Node.js core APIs (Buffer(), crypto.createCipher, url.parse, etc.) (https://github.com/eslint-community/eslint-plugin-n/blob/master/docs/rules/no-deprecated-api.md)
- **typescript-eslint** `@typescript-eslint/no-deprecated` — Flag use of any symbol marked @deprecated in its JSDoc/TS declaration (https://typescript-eslint.io/rules/no-deprecated/)
- **ESLint (eslint-plugin-react)** `react/no-deprecated` — Flag deprecated React APIs and lifecycle methods (componentWillMount, etc.) (https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md)
- **SonarQube / SonarSource** `RSPEC-1874 ("@Deprecated" code should not be used)` — Reports usage of code annotated/marked as deprecated (https://rules.sonarsource.com/java/RSPEC-1874/)
- **Ruff** `pyupgrade (UP) rules` — Detects and rewrites outdated/superseded Python idioms to modern equivalents (https://docs.astral.sh/ruff/rules/#pyupgrade-up)
- **Dependabot** `version-updates` — Flags outdated dependency versions (the stale-package facet of this smell) (https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/about-dependabot-version-updates)
