Logo ConstructiCat
CodeBust.
Parcourir la section ▾

Code répétitif verbeux.

Les assistants IA alourdissent une logique simple de commentaires qui répètent chaque ligne, de narration pas à pas, d'échelles défensives de vérifications de null et d'expressions à rallonge là où un idiome du langage ou un helper existant suffirait — le nombre de lignes gonfle sans ajouter de comportement.

##Signes et symptômes

Un relecteur repère le code répétitif verbeux quand un diff est bien plus long que le problème ne le justifie : chaque ligne est narrée par un commentaire qui se contente de la reformuler, les expressions simples sont enveloppées dans des échelles défensives de null/undefined, et la même forme de code réapparaît deux ou trois fois au lieu d'être factorisée en un seul helper. L'indice est un fort ratio commentaire/logique et ligne/comportement, plus des blocs qui auraient pu tenir en trois lignes de code idiomatique.

// Fonction pour obtenir le nom complet de l'utilisateur
function getFullName(user: User): string {
  // Vérifie si l'utilisateur est défini
  if (user === null || user === undefined) {
    // Retourne une chaîne vide quand aucun utilisateur n'est fourni
    return "";
  }
  // Récupère le prénom, par défaut chaîne vide
  const firstName = user.firstName ? user.firstName : "";
  // Récupère le nom, par défaut chaîne vide
  const lastName = user.lastName ? user.lastName : "";
  // Concatène le prénom et le nom avec un espace
  const fullName = firstName + " " + lastName;
  // Supprime les espaces et retourne le résultat
  return fullName.trim();
}

Trois lignes de comportement enterrées dans ~12 lignes de cérémonie. Autres indices : constructeurs ou wrappers vides qui ne font que transmettre, la cérémonie try { ... } catch (e) { throw e }, x ? x : fallback là où ?? suffirait, et un validateur d'e-mail/URL fait maison à côté du module de validation que le projet possède déjà. Quand cette même forme verbeuse est en plus copiée en getDisplayName, getLabel et getInitials, chacune réécrivant une logique qu'un util formatName() couvre déjà, la verbosité est devenue du Code dupliqué — voir Duplication au lieu d'extraction.

##Causes du problème

Pourquoi les modèles le produisent

  • Biais de verbosité du token suivant. Les LLM sont entraînés sur d'énormes volumes de code de tutoriel, de Stack Overflow et de débutant où les commentaires pas à pas et la cérémonie explicite sont la norme. La suite la plus probable est la plus abondamment expliquée, pas la plus concise.
  • Le RLHF récompense l'« apparence de minutie ». L'ajustement à l'utilité/la verbosité pousse les modèles vers une sortie qui paraît complète et auto-explicative — des commentaires sur chaque ligne, des vérifications défensives partout — que les évaluateurs et les utilisateurs récompensent même quand cela n'apporte aucune valeur.
  • Pas de contexte du dépôt, pas de réutilisation. Sans la base de code environnante dans le contexte, le modèle ne peut pas savoir qu'un util formatName() ou validateEmail() existe déjà, il en redérive donc la logique sur place. Cela correspond à deux motifs du rapport d'OX Security : la sur-spécification (Over-Specification — "des solutions hyper-spécifiques, à usage unique, impossibles à adapter à d'autres besoins", dans 80 à 90 % du code IA) et l'évitement des refactorings (Avoidance of Refactors, 80 à 90 %).
  • Générer, pas consolider. Les agents produisent du code neuf à chaque prompt et reviennent rarement le resserrer. L'analyse 2025 de GitClear portant sur 211 millions de lignes modifiées 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 % et dépassaient pour la première fois les lignes déplacées en 2024 ; les blocs de cinq lignes dupliquées ou plus ont été multipliés par huit environ en 2024.
  • Tout-commenter par défaut. OX Security a trouvé « des commentaires partout » dans 90 à 100 % du code généré par IA.

Pourquoi c'est nuisible

  • Maintenabilité : plus de surface à lire et à modifier ; les commentaires redondants se désynchronisent du code et finissent par induire en erreur (l'odeur Commentaires de Fowler).
  • Exactitude et sécurité : dès qu'un bloc verbeux est copié, un bug ou une vulnérabilité doit être corrigé à N endroits — le motif Bugs Déjà-Vu d'OX (70 à 80 %), qui fait que "des bugs identiques réapparaissent partout dans la base de code et exigent plusieurs fois la même correction".
  • Charge de revue : les gros diffs à faible signal masquent les vrais changements et induisent la fatigue du relecteur, si bien que de véritables problèmes passent à travers.
  • Dette technique cumulative : chaque quasi-doublon rend l'extraction suivante plus difficile, enracinant le problème que le modèle ne peut pas voir d'un fichier à l'autre.

##Traitement

Tactiques de revue / prompting

  • Forcez la réutilisation avant la génération : "Cherche dans la base de code les helpers existants (formatName, validate*) et réutilise-les ; ne réimplémente pas." Fournissez les utils pertinents en contexte.
  • Budgétez la sortie : "Garde ceci sous ~15 lignes ; aucun commentaire qui reformule le code — uniquement des commentaires expliquant le pourquoi."
  • Faites exécuter les outils par le modèle : exigez eslint/ruff et une passe de duplication jscpd, et faites-lui rapporter et corriger les violations avant de renvoyer.
  • Demandez le plus petit diff et une étape explicite de dédoublonnage : "Si un bloc se répète, extrais une seule fonction partagée et appelle-la" (Extraire une méthode).
  • Consolidez en revue : à la troisième quasi-copie, appliquez Extraire une méthode, Remonter une méthode ou Regrouper les fragments conditionnels dupliqués ; là où un commentaire ne fait qu'expliquer ce que fait une expression, appliquez Extraire une variable ou Renommer la méthode pour que le code porte l'explication et que le commentaire puisse disparaître.

Refactoring (avant → après)

// après : commentaires supprimés (le code parle de lui-même), cérémonie réduite,
// et l'util formatName() existant réutilisé au lieu d'être réécrit sur place
import { formatName } from "@/utils/format";

export const getFullName = (user?: User): string =>
  formatName(user?.firstName, user?.lastName);

Et éliminez carrément les doublons — si getDisplayName/getLabel faisaient la même chose, redirigez leurs appelants vers une seule fonction et supprimez les copies (Intégrer la méthode) ; ce qui reste sans appelant relève du Code mort et part aussi. Remplacez le validateur écrit à la main par l'import du module existant au lieu de garder une copie parallèle.

Règle empirique pour le catalogue : traitez tout bloc IA où les commentaires dépassent les lignes de logique, ou où vous pouvez nommer un helper existant qu'il a ignoré, comme candidat à la suppression-par-réutilisation — le correctif le plus rapide pour le code répétitif est généralement moins de code, pas plus.

##Détecté par

  • ESLint (core) no-useless-catch — Clauses catch inutiles (cérémonie « attraper puis relancer » : catch (e) { throw e })
  • ESLint (core) no-unneeded-ternary — Opérateurs ternaires là où une forme plus simple existe (avec defaultAssignment: false, signale x ? x : fallback)
  • typescript-eslint @typescript-eslint/prefer-nullish-coalescing — Préférer ?? à || et aux ternaires avec test explicite de null
  • ESLint (core) no-useless-constructor — Constructeur vide redondant / code répétitif de transmission
  • ESLint (core) max-lines-per-function — Longueur de fonction excessive/verbeuse (indicateur partiel)
  • jscpd --min-tokens / --threshold — Détection de copier-coller, pour les blocs verbeux qui finissent copiés-collés (seuil --min-tokens / --threshold)
  • SonarQube / SonarCloud typescript:S4144 — Les fonctions et méthodes ne doivent pas avoir des implémentations identiques
  • ESLint (eslint-plugin-sonarjs) sonarjs/no-identical-functions — Les fonctions ne doivent pas avoir des implémentations identiques