---
title: "Abstraction superficielle"
type: "ai-smell"
slug: "shallow-abstraction"
url: "http://localhost:3000/fr/ai-smells/shallow-abstraction.md"
category: "Structure"
description: "Un assistant IA enveloppe du code dans des fonctions, classes ou interfaces supplémentaires qui ajoutent une couche d'indirection sans masquer la moindre complexité ni permettre la réutilisation — des abstractions qui ne font que rediriger vers un unique site d'appel."
---
# Abstraction superficielle

> Un assistant IA enveloppe du code dans des fonctions, classes ou interfaces supplémentaires qui ajoutent une couche d'indirection sans masquer la moindre complexité ni permettre la réutilisation — des abstractions qui ne font que rediriger vers un unique site d'appel.

## Signs and Symptoms

Un relecteur reconnaît une Abstraction superficielle lorsqu'une nouvelle fonction/classe/interface ajoute un _nom et une couche_ mais aucun _levier_ : son corps reformule sa signature, elle transmet directement vers un unique appel sous-jacent, elle est invoquée exactement une fois, ou elle ne cache aucune décision, invariant ou variation. Le code _paraît_ structuré en couches et « de qualité entreprise », mais vous devez lire à travers chaque couche pour comprendre quoi que ce soit — l'abstraction n'encapsule rien.

Signes révélateurs :

* Fonctions/méthodes de passage dont le corps est un unique appel délégué sans comportement ajouté.
* Classes enveloppes (`FooService` → `FooRepository` → `db`) où chaque couche se contente d'appeler la suivante.
* Interfaces à une seule implémentation (`IClock` avec uniquement `SystemClock`) introduites de façon spéculative « pour la testabilité » sans deuxième implémentation ni faux objet.
* Fonctions `utils`/`helpers` utilisées à exactement un seul endroit.
* Aliasing/renommage redondant qui transmet une valeur inchangée.

C'est le smell classique **Homme du milieu** / **Classe paresseuse** / **Généralité spéculative**, et le revers du constat de « sur-spécification » d'OX Security (solutions hyper-spécifiques à usage unique au lieu de composants généralisables).

```ts
// L'IA a ajouté trois « couches » qui transmettent chacune à la suivante.
function getUserName(id: string) {
  return fetchUserName(id);            // n'ajoute rien
}
function fetchUserName(id: string) {
  return userRepository.getName(id);   // n'ajoute rien
}
class UserRepository {
  getName(id: string) {
    return db.query("SELECT name FROM users WHERE id = ?", [id]);
  }
}
// getUserName est le seul site d'appel, utilisé une seule fois.
// Deux des trois couches n'ajoutent aucun comportement — pur Homme du milieu.

```

## Reasons for the Problem

**Pourquoi les modèles le produisent**

* **Imiter la _forme_ d'une bonne architecture.** Les corpus d'entraînement sont saturés de tutoriels et d'échafaudages d'entreprise — couches service/repository/factory, injection de dépendances, interfaces à une seule implémentation. Le modèle reproduit par mimétisme le _cérémonial_ de l'abstraction sans la substance, parce qu'un utilitaire nommé est localement plausible et « fait professionnel ». La génération token par token optimise la ressemblance avec du code bien structuré, et non le fait qu'une couche mérite sa place.
* **Aucun contexte du dépôt.** En générant localement, le modèle ne peut pas voir que l'utilitaire n'est appelé qu'une fois, ou qu'une abstraction canonique existe déjà ailleurs — il invente donc de nouvelles couches superficielles au lieu de les réutiliser. C'est exactement la tendance mesurée par GitClear : les lignes refactorisées (« déplacées ») sont passées d'environ 24 % (2020) à moins de 10 % (2024) tandis que les lignes copiées-collées augmentaient et que les clones de code croissaient d'environ 4x. Les modèles ajoutent de la structure mais ne la _consolident_ jamais.
* **Application littérale du manuel + sur-spécification.** OX Security a constaté que l'IA sur-spécifie dans 80 à 90 % du code généré, produisant des « solutions étroites qui ne peuvent pas être réutilisées », et évite les refactorisations dans 80 à 90 % des cas (« s'arrête à assez bon »). Les enveloppes superficielles sont le résidu visible.
* **Littéralisme du prompt et flagornerie.** Quand on lui demande d'« ajouter une couche service » ou de « rendre le code propre/modulaire », le modèle ajoute littéralement des couches même lorsque YAGNI s'applique — la tendance à la sur-ingénierie / au « god agent » que les praticiens décrivent dans la discussion _code-smells-for-AI-agents_.
* **Obsolescence due à la date limite d'entraînement.** Il enveloppera une API dépréciée dans un mince adaptateur plutôt que d'en migrer.

**Pourquoi c'est nuisible**

* **Maintenabilité et charge de revue.** Chaque modification doit traverser des couches mortes ; les lecteurs lisent à travers des Hommes du milieu. Le constat « Armée de juniors » d'OX : les relecteurs ne peuvent pas suivre le rythme d'un code qui _paraît_ structuré mais n'a aucune véritable couture.
* **Correction et sécurité.** arXiv 2509.20491 et SonarSource notent tous deux que l'aliasing et les enveloppes « masquent les sites d'appel décisifs », dégradant l'analyse du flux de données/de contrôle et l'analyse de teinte (taint analysis) — une enveloppe peut silencieusement avaler une erreur ou cacher un puits (sink) à un scanner.
* **Accumulation de dette technique.** Les interfaces spéculatives à une seule implémentation se figent ; et parce que chaque abstraction superficielle est à usage unique, la variation _suivante_ est copiée-collée plutôt que paramétrée — alimentant directement la croissance des clones mesurée par GitClear. Vous payez l'indirection sans jamais récolter la réutilisation qu'elle promettait.

## Treatment

**Tactiques de revue et de prompting**

* Appliquez la **Règle de trois** : « N'ajoutez une fonction/classe/interface que si elle est utilisée à ≥2 endroits _ou_ qu'elle cache une véritable décision/invariant. Intégrez en ligne (inline) les utilitaires à usage unique (YAGNI). »
* Forcez la réutilisation : « Avant d'ajouter un utilitaire/service/interface, cherchez-en un existant dans le dépôt et réutilisez-le. » Orientez le modèle vers le module canonique.
* Faites-lui justifier chaque couche : « Énumérez chaque nouvelle fonction/classe et indiquez quelle variation ou quel invariant elle cache ; supprimez toute couche qui se contente de transmettre. »
* Exigez qu'il **exécute le linter** (`no-useless-constructor`, `no-useless-rename`) et un **scan de duplication** (`jscpd`) et qu'il corrige ce qu'ils signalent.

**Refactorisations (nommez-les) :** **Intégrer une fonction**, **Intégrer une classe**, **Supprimer l'homme du milieu** pour les passages directs ; **Réduire la hiérarchie** / abandonner l'interface pour les interfaces à une seule implémentation. Quand la duplication est le _véritable_ moteur, faites **Extraire une fonction une seule fois** dans un utilitaire véritablement partagé et paramétré — pas une enveloppe par site d'appel.

Avant — Homme du milieu :

```ts
function getUserName(id: string) { return fetchUserName(id); }
function fetchUserName(id: string) { return userRepository.getName(id); }

```

Après — Intégrer une fonction / Supprimer l'homme du milieu (un seul site d'appel, aucun comportement ajouté) :

```ts
const name = userRepository.getName(id);

```

Avant — Interface spéculative à une seule implémentation :

```ts
interface IClock { now(): number; }
class SystemClock implements IClock { now() { return Date.now(); } }

```

Après — Réduire la hiérarchie (utilisez le type concret ; réintroduisez l'interface uniquement quand une seconde implémentation, par ex. un faux objet de test, existe réellement) :

```ts
class SystemClock { now() { return Date.now(); } }

```

## Detected by

- **ESLint** `no-useless-constructor` — Constructeur de passage qui ne fait que déléguer à super ou qui est vide (https://eslint.org/docs/latest/rules/no-useless-constructor)
- **ESLint** `no-useless-rename` — Aliasing redondant d'import/export/déstructuration qui transmet un nom inchangé (https://eslint.org/docs/latest/rules/no-useless-rename)
- **PMD (Java)** `UselessOverridingMethod` — Méthode de surcharge qui se contente d'appeler super avec les mêmes arguments (https://pmd.github.io/pmd/pmd_rules_java_design.html)
- **SonarSource (Sonar S4144)** `Functions/methods should not have identical implementations` — Signale les abstractions dupliquées à usage unique ayant des corps identiques (https://rules.sonarsource.com/java/RSPEC-4144/)
- **jscpd** `duplication threshold` — Détecte les utilitaires à usage unique quasi dupliqués que les abstractions superficielles ne parviennent pas à consolider (https://github.com/kucherenko/jscpd)
