Réinvention de la roue.
Le modèle écrit à la main du code sur mesure pour quelque chose qu'une bibliothèque standard, une dépendance existante ou un helper déjà présent dans le dépôt fait déjà — ajoutant une logique dupliquée et moins testée au lieu d'appeler ce qui existe déjà.
##Signes et symptômes
Un relecteur repère une réinvention de la roue lorsqu'un diff introduit un morceau non trivial de logique « repartie de zéro » pour résoudre un problème déjà résolu — par le runtime du langage, par une dépendance déjà présente dans package.json, ou par un utilitaire qui existe déjà ailleurs dans le dépôt. Indices courants :
- Un
deepClone,debounce,groupBy,chunk,retry,slugify,deepMergeouuuidécrit à la main alors que le runtime ou une bibliothèque installée le fournit. - Une regex d'e-mail/URL/UUID sur mesure au lieu d'un validateur qui est déjà une dépendance.
- Des calculs de dates, du parsing de query-string ou une logique de pagination sur mesure qui réimplémentent
Intl/URLSearchParams/une fonctionnalité d'ORM. - Deux ou trois helpers privés quasi identiques répartis dans des fichiers (la même roue retaillée par fonctionnalité) — la duplication qui apparaît dans les métriques de copier-coller.
- La version réinventée est subtilement fausse : elle gère le chemin heureux mais manque les cas limites que l'original éprouvé couvre.
// Écrit par IA : un deep clone sur mesure réinventé en ligne
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; // perd silencieusement Date, Map, Set, RegExp ; boucle sur les cycles
}
// ...alors même que le runtime fournit structuredClone(), ET
// que le dépôt exporte déjà cloneDeep() depuis src/utils/object.ts
La vérification la plus rapide : cherchez la capacité dans le dépôt et dans node_modules avant d'accepter le nouveau code. Si structuredClone, lodash, date-fns ou un helper local la couvre déjà, la nouvelle fonction est une roue réinventée.
##Causes du problème
Pourquoi les modèles le produisent
- Aucun contexte du dépôt par défaut. Le modèle ne voit souvent pas votre
src/utils, vos dépendances installées ni vos conventions maison, alors il recourt à la complétion statistiquement la plus probable : une implémentation autonome en ligne. Il réinvente la roue parce qu'il n'a jamais vu la vôtre. - Localité du token suivant. Les LLM optimisent une suite localement plausible, pas une minimalité globale. Écrire
function groupBy(...)est une séquence à forte probabilité ; s'arrêter pour découvrir quelodash.groupByest déjà importé trois fichiers plus loin n'est pas ce que fait la prédiction de token. - Les incitations d'entraînement récompensent l'autonomie. Une énorme part des données d'entraînement est constituée de tutoriels, de réponses Stack Overflow et d'extraits qui montrent délibérément l'implémentation complète. Le modèle a appris que « bien répondre » signifie émettre du code complet et autonome — exactement le mauvais instinct au sein d'une base de code mature.
- Obsolescence due à la date de coupure d'entraînement. Le modèle peut ignorer qu'une capacité a été promue dans la bibliothèque standard (par ex.
structuredClone,Array.prototype.at,Object.groupBy) ou que votre dépôt a adopté un helper après sa coupure, alors il polyfille quelque chose qui existe déjà. - Complaisance / moindre résistance. Quand on lui demande d'« ajouter X », le modèle ajoute X de la manière la plus directe plutôt que de rétorquer « on a déjà ça ». Il propose rarement de lui-même « tu n'as pas besoin d'écrire ça ».
- Vanilla Style et sur-spécification. L'analyse d'OX Security portant sur plus de 300 dépôts décrit deux motifs voisins : le Vanilla Style, où l'IA "réimplémente des fonctionnalités à partir de zéro" au lieu d'utiliser des bibliothèques établies (40 à 50 % du code généré par IA), et la sur-spécification (Over-Specification) — "des solutions hyper-spécifiques, à usage unique, impossibles à adapter à d'autres besoins" (80 à 90 %). Ensemble, ils font que chaque nouvelle variante reçoit du code neuf au lieu d'être réutilisée.
Pourquoi c'est nuisible
- Maintenabilité et dette technique. C'est le visage mesurable du déclin de la qualité du code IA. L'analyse 2025 de GitClear portant sur 211 millions de lignes modifiées (2020–2024) a montré que les lignes associées au refactoring ("déplacées") sont passées de 25 % des lignes modifiées en 2021 à moins de 10 % en 2024, tandis que les lignes copiées-collées passaient de 8,3 % à 12,3 % sur la même période ; 2024 a été la première année où les lignes copiées-collées ont dépassé les lignes déplacées, et les blocs de cinq lignes dupliquées ou plus ont été multipliés par huit environ en 2024. Les roues réinventées sont l'une des voies par lesquelles cette duplication entre dans le code.
- Correction. La version sur mesure saute les cas limites que l'implémentation mature a gagnés au fil d'années de rapports de bugs (fuseaux horaires, Unicode, cycles, échappement). Elle a l'air juste et échoue dans la longue traîne.
- Sécurité. Réécrire la cryptographie, l'authentification, l'assainissement ou la validation au lieu d'une bibliothèque éprouvée est la façon dont le code écrit par IA « viole les bonnes pratiques d'ingénierie » à grande échelle (l'effet « Armée de juniors » d'OX) — des schémas vulnérables atteignent la production plus vite que la revue ne peut les attraper.
- Charge de revue. Les relecteurs doivent désormais lire, comprendre et tester 40 lignes de logique sur mesure au lieu de reconnaître un seul appel de bibliothèque éprouvée — multiplié sur chaque PR.
##Traitement
Tactiques de revue et de prompting
- Donnez au modèle le contexte qui lui manque. Avant de générer, pointez-le vers vos utilitaires et dépendances : "Réutilise les helpers de
src/utils/*et les bibliothèques déjà présentes danspackage.json; n'en ajoute pas de nouvelles sans demander." Collez les dépendances pertinentes depackage.jsonet l'index de vos utils. - Faites de « chercher d'abord » une règle. Donnez la consigne : "Avant d'écrire un helper, vérifie si la bibliothèque standard, une dépendance existante ou un utilitaire du dépôt le fait déjà ; si oui, appelle-le." Les outils agentiques devraient d'abord grep le dépôt.
- Remettez en question chaque nouveau helper privé en revue. Pour chaque utilitaire écrit à la main, demandez : le runtime fait-il cela ? une dépendance le fait-elle ? l'avons-nous déjà ? Si oui, c'est une réinvention de la roue — remplacez-la.
- Exécutez la chaîne d'outils. Un détecteur de duplication (jscpd / PMD CPD / SonarQube) en CI signale automatiquement la facette copier-coller ; câblez-le dans la barrière pour que les roues retaillées fassent échouer le build.
Le refactoring — c'est le classique Code dupliqué plus l'antipattern « réinventer la roue »; le correctif est Remplacer l’algorithme (remplacer le corps fait maison par l'appel à la bibliothèque ou au standard) et, là où plusieurs copies existent, Extraire une méthode / Remonter une méthode vers un seul helper partagé.
// AVANT — réinventé, partiellement correct, dupliqué par fonctionnalité
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);
// APRÈS — appeler ce qui existe déjà (cas limites corrects, zéro nouveau code)
const copy = structuredClone(state);
// ou, si le standard du dépôt est le helper local :
import { cloneDeep } from "@/utils/object";
const copy = cloneDeep(state);
Si la roue a été réinventée dans plusieurs fichiers, supprimez toutes les copies et routez chaque appelant via la source unique de vérité. Résultat net : moins de lignes, moins de bugs, et les métriques de duplication repartent dans le bon sens.
##Détecté par
- jscpd --min-tokens / --threshold — Détection de copier-coller (min-tokens / seuil)
- PMD CPD — CPD (Détecteur de Copier/Coller)
- SonarQube duplicated_lines_density — Blocs dupliqués / duplicated_lines_density