---
title: "Reinvented Wheel"
type: "ai-smell"
slug: "reinvented-wheel"
url: "http://localhost:3000/en/ai-smells/reinvented-wheel.md"
category: "Structure"
description: "The model hand-writes bespoke code for something a standard library, an existing dependency, or a helper already in the repo already does — adding duplicated, less-tested logic instead of calling what is already there."
---
# Reinvented Wheel

> The model hand-writes bespoke code for something a standard library, an existing dependency, or a helper already in the repo already does — adding duplicated, less-tested logic instead of calling what is already there.

## Signs and Symptoms

A reviewer spots Reinvented Wheel when a diff introduces a non-trivial chunk of "from scratch" logic to solve a problem that is already solved — by the language runtime, by a dependency already in `package.json`, or by a utility that already exists elsewhere in the repo. Common tells:

* A hand-rolled `deepClone`, `debounce`, `groupBy`, `chunk`, `retry`, `slugify`, `deepMerge`, or `uuid` when the runtime or an installed library provides it.
* A bespoke email/URL/UUID **regex** instead of a validator that is already a dependency.
* Custom date math, query-string parsing, or pagination logic that re-implements `Intl`/`URLSearchParams`/an ORM feature.
* Two or three near-identical private helpers across files (the same wheel re-carved per feature) — the duplication that shows up in copy/paste metrics.
* The reinvented version is subtly **wrong**: it handles the happy path but misses edge cases the battle-tested original covers.

```ts
// AI-written: a bespoke deep clone reinvented inline
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (Array.isArray(obj)) return obj.map(deepClone);
  const out = {};
  for (const k in obj) out[k] = deepClone(obj[k]);
  return out; // silently drops Date, Map, Set, RegExp; loops on cycles
}
// ...even though the runtime ships structuredClone(), AND
// the repo already exports cloneDeep() from src/utils/object.ts

```

The fastest check: search the repo and `node_modules` for the capability before accepting the new code. If `structuredClone`, `lodash`, `date-fns`, or a local helper already covers it, the new function is a reinvented wheel.

## Reasons for the Problem

**Why models produce it**

* **No repo context by default.** The model often cannot see your `src/utils`, your installed dependencies, or your house conventions, so it reaches for the most statistically likely completion: a self-contained inline implementation. It re-derives the wheel because it never saw yours.
* **Next-token locality.** LLMs optimize for a locally plausible continuation, not global minimality. Writing `function groupBy(...)` is a high-probability sequence; pausing to discover that `lodash.groupBy` is already imported three files over is not something token prediction does.
* **Training incentives reward self-containment.** A huge share of training data is tutorials, Stack Overflow answers, and snippets that deliberately show the full implementation. The model learned that "answering well" means emitting complete, standalone code — exactly the wrong instinct inside a mature codebase.
* **Training-cutoff staleness.** The model may not know a capability was promoted into the standard library (e.g. `structuredClone`, `Array.prototype.at`, `Object.groupBy`) or that your repo adopted a helper after its cutoff, so it polyfills something that already exists.
* **Sycophancy / least-resistance.** Asked to "add X," the model adds X in the most direct way rather than pushing back with "we already have this." It rarely volunteers "you don't need to write this."
* **Over-specification.** OX Security's analysis of 300+ repos found AI tends toward "vanilla style" coding that rebuilds common functionality instead of using proven libraries, with narrowly-scoped, non-reusable solutions in roughly 80–90% of cases — each new variation gets fresh code instead of reuse.

**Why it hurts**

* **Maintainability & tech debt.** This is the measurable face of the AI code-quality decline. GitClear's analysis of 211M lines found copy/pasted code rose from 8.3% (2020) to 12.3% (2024), blocks of 5+ duplicated lines grew \~8x in 2024, and "moved" (refactored) lines fell from 24.1% to 9.5% — 2024 was the first year copy/paste exceeded refactoring. Reinvented wheels are how that duplication enters.
* **Correctness.** The bespoke version skips the edge cases the mature implementation earned through years of bug reports (timezones, Unicode, cycles, escaping). It looks right and fails in the long tail.
* **Security.** Re-rolling crypto, auth, sanitization, or validation instead of a vetted library is how AI-written code "violates engineering best practices" at scale (OX's "Army of Juniors" effect) — vulnerable patterns reach production faster than review can catch them.
* **Review load.** Reviewers now have to read, reason about, and test 40 lines of custom logic instead of recognizing one trusted library call — multiplied across every PR.

## Treatment

**Review & prompting tactics**

* **Give the model the context it lacks.** Before generating, point it at your utilities and dependencies: "Reuse helpers from `src/utils/*` and libraries already in `package.json`; do not add new ones without asking." Paste the relevant `package.json` deps and your utils index.
* **Make "search first" a rule.** Instruct: "Before writing any helper, check whether the standard library, an existing dependency, or a repo utility already does this; if so, call it." Agentic tools should grep the repo first.
* **Challenge every new private helper in review.** For each hand-rolled utility ask: does the runtime do this? does a dependency do this? do we already have this? If yes, it's Reinvented Wheel — replace it.
* **Run the toolchain.** A duplication scanner (jscpd / PMD CPD / SonarQube) in CI flags the copy/paste facet automatically; wire it into the gate so re-carved wheels fail the build.

**The refactor** — this is classic **Duplicate Code** and **Reinvent the Wheel**; the fix is **Substitute Algorithm** (swap the bespoke body for the library/standard call) and, where several copies exist, **Extract Function** / pull-up to a single shared helper.

```ts
// BEFORE — reinvented, partially-correct, duplicated per feature
function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (Array.isArray(obj)) return obj.map(deepClone);
  const out = {};
  for (const k in obj) out[k] = deepClone(obj[k]);
  return out;
}
const copy = deepClone(state);

```

```ts
// AFTER — call what already exists (correct edge cases, zero new code)
const copy = structuredClone(state);
// or, if the repo standard is the local helper:
import { cloneDeep } from "@/utils/object";
const copy = cloneDeep(state);

```

If the wheel was reinvented in several files, delete all copies and route every caller through the single source of truth. Net result: fewer lines, fewer bugs, and the duplication metrics move back in the right direction.

## Detected by

- **jscpd** `duplication` — Copy/paste detection (min-tokens / threshold) (https://github.com/kucherenko/jscpd)
- **PMD** `cpd` — CPD (Copy/Paste Detector) (https://docs.pmd-code.org/latest/pmd_userdocs_cpd.html)
- **SonarQube** `common-duplications` — Duplicated blocks / duplicated_lines_density (https://docs.sonarsource.com/sonarqube-server/latest/analyzing-source-code/test-coverage/duplications/)
