Configuration de l'environnement de travail pour éliminer le code boilerplate sans intérêt généré par les agents IA
Lorsqu'on intègre des agents IA dans un environnement de développement réel, leurs limites apparaissent très rapidement. Sans comprendre le contexte métier, ils ont tendance à surutiliser mécaniquement le pattern Factory ou à générer sans fin des fonctions utilitaires totalement inutiles. Selon un rapport d'analyse publié par GitClear en 2024 portant sur 623 millions de lignes de commits, la duplication de code a augmenté de 81 % après la généralisation des outils d'IA, tandis que le taux de refactorisation visant à nettoyer le code existant est passé de 25 % à moins de 10 %. En raison du biais de récence de la fenêtre de contexte, l'agent se réfugie systématiquement dans les patterns standards les plus courants issus des données d'entraînement pour éviter les risques. Il est donc indispensable d'encadrer les dysfonctionnements de l'agent en explicitant les règles d'architecture et la logique de synchronisation partagées par l'équipe à l'aide de fichiers de contexte.
1. Bloquer les écarts par rapport aux règles dans une base de code legacy
Pour contrôler des directives souvent éparpillées selon les outils de développement, il faut commencer par placer à la racine du projet un fichier AGENTS.md faisant office de source unique de vérité. Les outils tels que Cursor ou Claude Code lisent ces règles. Le fichier unique .cursorrules a été obsolète il y a déjà bien longtemps. Il convient désormais de le découper en fichiers MDC dans le répertoire .cursor/rules/, et dans l'environnement Claude Code, d'importer @AGENTS.md à l'intérieur de CLAUDE.md afin d'éviter toute dérive ou perte de règles.
`yaml
description: Backend API and Domain Service architectural constraints
globs: apps/api//*.ts, services//*.ts
alwaysApply: false
Backend Architectural Constraints
Mandatory Patterns
- Use Canonical Response DTOs located in
apps/api/src/common/dto.
- All database mutations must go through the Unit of Work pattern defined in
services/shared/uow.
Explicit Negative Constraints
- NEVER create single-child base classes or single-implementation interfaces.
- NEVER write wrapper functions that merely pass arguments to lower-level services.
- NEVER implement strategy or factory patterns when standard conditional logic (if/switch) suffices.
- NEVER add defensive "just-in-case" try-catch blocks that return dummy fallback values without rethrowing.
`
Lorsque la longueur totale du contexte dépasse 800 lignes (soit environ 2 000 tokens), l'agent commence à ignorer les contraintes définies au début du document. Il faut donc ne conserver dans AGENTS.md que les interdictions strictes et non négociables, et déporter le registre détaillé des décisions d'architecture vers le répertoire docs/context/.
Ordre d'application des contraintes
- Tâche : Sélectionner 1 module clé et définir ses patterns interdits ainsi que ses règles d'exception.
- Procédure d'exécution :
- Créer le fichier
AGENTS.md à la racine du projet et y inscrire l'interdiction des interfaces à implémentation unique ainsi que des blocs try-catch défensifs.
- Configurer le chemin des fichiers cibles (
globs) et les contraintes de YAML Frontmatter dans .cursor/rules/backend-constraints.mdc.
- Ajouter la syntaxe
@AGENTS.md dans CLAUDE.md pour harmoniser l'environnement.
- Résultat attendu : La génération de boilerplate inutile diminue, réduisant le temps consacré à la correction de code d'environ 4 heures par semaine.
2. Pipeline de vérification du code généré par l'agent
Derrière le code en apparence plausible généré par un agent se cachent des pièges. Selon une étude menée par Veracode en 2024, des vulnérabilités de sécurité équivalentes au Top 10 OWASP ont été trouvées dans 45 % des propositions de code fournies par les IA. La mauvaise pratique consistant à englober une logique risquée dans un bloc try-catch sommaire pour retourner un objet vide ou null afin de masquer l'erreur est commise 47 % plus souvent par les IA que par les développeurs humains. L'absence de verrouillage optimiste lors des opérations Read-Modify-Write ou les problèmes de requêtes N+1 exécutées dans des boucles DB surviennent également très fréquemment.
| Domaine de vérification |
Éléments de contrôle détaillés |
Patterns à risque et pièges de l'agent |
Critères de blocage du merge |
| Vulnérabilités de sécurité |
Utilisation de Parameterized Query, isolation multi-tenant, vérification des packages non autorisés |
Injections SQL basées sur la concaténation de chaînes, appels à des packages non vérifiés créés par hallucination |
Blocage en cas d'absence de validation des entrées ou d'ajout de dépendances externes d'origine indéterminée |
| Goulots d'étranglement de performance |
Chargement différé ORM (Lazy loading), boucles imbriquées sur les Hot paths, indexation DB |
Requêtes itératives sur chaque entité au sein d'une boucle, filtrage de tables complètes en mémoire |
Blocage si présence d'appels DB ou API externes dans une boucle, ou en cas d'absence de pagination |
| Sécurité des types |
Vérification Strict Type, gestion des exceptions aux frontières (Boundary), contrôle de concurrence |
Surutilisation de as any, dissimulation d'erreurs par des blocs Catch vides |
Blocage en présence de any ou de casts as abusifs, blocage des blocs Catch sans enregistrement de logs |
Le code de test généré par un agent se limite bien souvent à une simple tautologie recopiant servilement le code d'implémentation. Ce type de test est totalement incapable de détecter les véritables anomalies métier. Il convient donc d'appliquer rigoureusement la règle suivante : "Si un utilitaire existant est déjà présent, l'utilitaire nouvellement créé par l'agent doit être supprimé au profit du réemploi du code existant".
Procédure de vérification manuelle
- Tâche : Intégrer une checklist de vérification de la sécurité, des performances et des types dans les modèles de travail et les portes CI.
- Procédure d'exécution :
- Insérer dans le modèle de PR les points de contrôle relatifs aux Parameterized Queries, à la prévention des requêtes N+1 et à l'interdiction de
any.
- Configurer des outils d'analyse statique sur le Git Pre-commit Hook afin de rejeter le commit en cas de détection de
as any ou de await au sein d'une boucle.
- Lors de la revue de code, remplacer systématiquement les nouveaux utilitaires créés de façon intempestive par l'agent par les modules communs existants.
- Résultat attendu : Prévenir le passage en production de défauts tels que les requêtes N+1 ou les fuites de mémoire.
3. Prompting décomposé pour éviter la stagnation de la réflexion
Si vous tentez de soumettre une logique de règlement complexe ou un traitement de commande basé sur une machine à états au moyen d'un prompt unique, l'agent risque de s'enfermer dans une boucle d'appels d'outils ou de ne restituer qu'un code vide de substance. Cela s'explique par le fait que le traitement simultané de la conception du schéma, des interfaces API, de la gestion des exceptions et des règles métier vient embrouiller l'ordre d'attribution des tokens.
`
[Étape 1 : Modélisation des données] -> Création des entités DB et des schémas Zod
│
▼ (Transmission du résultat en tant que contexte)
[Étape 2 : Définition des interfaces] -> Définition des DTO API, des Custom Errors et des signatures de service
│
▼ (Transmission des résultats des étapes 1+2 en tant que contexte)
[Étape 3 : Implémentation de la logique métier] -> Finalisation des transactions, des transitions d'état et du contrôle de concurrence
`
Effectuer cet aller-retour à la main est assez fastidieux. Il est préférable d'écrire un script d'automatisation CLI (scripts/agent-decomposed-build.ts) pour lier les étapes de sorte que le résultat de la phase précédente alimente le contexte d'entrée de la suivante.
`typescript
import { execSync } from 'child_process';
import * as fs from 'fs';
interface TaskPipeline {
featureName: string;
stage1Prompt: string;
stage2Prompt: string;
stage3Prompt: string;
}
async function runDecomposedAgentPipeline(pipeline: TaskPipeline) {
console.log([Stage 1] Executing Data Modeling for ${pipeline.featureName}...);
const stage1Output = execSync(claude --print "${pipeline.stage1Prompt}").toString();
fs.writeFileSync(./tmp/${pipeline.featureName}_stage1.ts, stage1Output);
console.log([Stage 2] Executing Interface Definition...);
const stage2InputPrompt = ${pipeline.stage2Prompt}\n\nContext Models:\n${stage1Output};
const stage2Output = execSync(claude --print "${stage2InputPrompt}").toString();
fs.writeFileSync(./tmp/${pipeline.featureName}_stage2.ts, stage2Output);
console.log([Stage 3] Executing Business Logic Implementation...);
const stage3InputPrompt = ${pipeline.stage3Prompt}\n\nContext Models:\n${stage1Output}\n\nContext Contracts:\n${stage2Output};
const stage3Output = execSync(claude --print "${stage3InputPrompt}").toString();
fs.writeFileSync(./src/services/${pipeline.featureName}.service.ts, stage3Output);
console.log([Pipeline Complete] Business logic generated cleanly without cognitive stagnation.);
}
`
Mise en place d'un script de prompting décomposé
- Tâche : Établir un modèle de prompt en 3 étapes et intégrer un script CLI permettant son exécution séquentielle.
- Procédure d'exécution :
- Découper le prompt en 3 parties : modélisation des données, définition des interfaces et implémentation de la logique métier.
- Rédiger
scripts/agent-decomposed-build.ts pour que la sortie précédente soit réinjectée dans le contexte du prompt suivant.
- Exécuter ce script lors de la création de tout nouveau module complexe.
- Résultat attendu : Disparition des blocages où l'agent reste inactif sans fournir de réponse, et chute du taux de réécriture manuelle du code de plus de 20 % à moins de 5 %.
4. Configuration pour conserver la justification des décisions dans le code
Plus on recourt aux outils de codage par IA, plus on accumule du "code à écriture seule" dépourvu de justification quant aux choix d'implémentation. Un code où les compromis d'architecture ne sont pas consignés devient une charge très lourde à maintenir pour les développeurs humains par la suite. Il faut imposer dans AGENTS.md la rédaction de commentaires aux normes TSDoc directement dans le code source lorsque l'agent génère du code.
`typescript
/**
- @description Processes deferred settlement payouts for multi-vendor orders.
- @why Uses pessimistic database locking on the Wallet entity instead of optimistic locking because payout calculation involves high-frequency concurrent balance updates.
- @tradeoff Slight P95 latency increase under high contention in exchange for 0% financial drift.
- @complexity Time: O(N log N) due to vendor sorting | Space: O(N) for batch processing buffer.
*/
export async function processDeferredSettlement(orderId: string): Promise {
if (account.hasOutstandingBalance()) {
this.applySettlementHold(account);
}
}
`
Afin d'éviter les divergences de configuration des agents entre les développeurs, il est nécessaire de définir AGENTS.md comme source unique de vérité et d'exécuter un script de synchronisation (tools/sync-agent-rules.ts) vers CLAUDE.md et .cursor/rules/global.mdc lors des commits.
`typescript
import * as fs from 'fs';
import * as path from 'path';
const AGENTS_MD_PATH = path.join(__dirname, '../AGENTS.md');
const CLAUDE_MD_PATH = path.join(__dirname, '../CLAUDE.md');
const CURSOR_RULE_PATH = path.join(__dirname, '../.cursor/rules/global.mdc');
function syncRules() {
if (!fs.existsSync(AGENTS_MD_PATH)) {
console.error('Error: AGENTS.md does not exist.');
process.exit(1);
}
const baseRules = fs.readFileSync(AGENTS_MD_PATH, 'utf-8');
const claudeContent = # AUTOMATICALLY GENERATED FROM AGENTS.md - DO NOT EDIT DIRECTLY\n\n${baseRules};
fs.writeFileSync(CLAUDE_MD_PATH, claudeContent);
const mdcHeader = ---\ndescription: Global Agent Rule Sync\nglobs: **/*\nalwaysApply: true\n---\n\n;
fs.writeFileSync(CURSOR_RULE_PATH, ${mdcHeader}${baseRules});
console.log('Successfully synchronized AGENTS.md to CLAUDE.md and Cursor MDC rules.');
}
syncRules();
`