Apprendre à Claude Code votre monorepo NestJS : routage, règles et compétences
Apprenez à Claude Code à gérer un monorepo NestJS : CLAUDE.md, règles, compétences et permissions qui aident à placer le code dans le bon service et à le maintenir conforme aux conventions de l’équipe.
Sources: Claude Code for NestJS Monorepos: A Practical Setup Guide
Si vous adressez une demande telle que « ajouter un statut visible par les clients à une réservation » à un assistant IA non configuré au sein d’un grand monorepo NestJS, le résultat est prévisible : la modification est appliquée au mauvais microservice, le message d’erreur est une chaîne de caractères codée à l’avance, et l’accès est contrôlé par user.role === 'admin'. Le modèle ne rencontre pas de problème avec TypeScript ; il tente plutôt de deviner les décisions prises il y a longtemps par votre équipe, sans jamais avoir connaissance de l’endroit où ces décisions sont consignées. Ce guide montre comment remplacer cette méthode de devinette par un contexte géré via des contrôles de version, en utilisant les quatre éléments de base proposés par Claude Code : CLAUDE.md, des règles, des compétences et des paramètres de permission. À la fin, vous disposerez d’une structure que vous pourrez soumettre à votre répertoire de code, afin que chaque ingénieur et chaque session d’IA partent des mêmes conventions.
Si vous souhaitez des conseils généraux sur la rédaction du fichier de synthèse lui-même, consultez notre guide pour un CLAUDE.md efficace. Cet article porte sur les changements à prendre en compte lorsque la base de code est un backend NestJS multi-services.
La version courte
Pour les lecteurs qui préfèrent la liste de contrôle avant l’explication :
- Installez Claude Code (par exemple avec
npm install -g @anthropic-ai/claude-code) et authentifiez-vous une fois en exécutantclaude. - Créez un fichier racine
.claude/CLAUDE.mdqui explique quel service gère quels données, l’ordre de votre pipeline de requêtes, ainsi que le format de réponse retourné par chaque point d’entrée. - Ajoutez un répertoire
.claude/rules/contenant des interdictions : pas de requêtes à l’intérieur de boucles, pas de vérifications basées sur des noms de rôles, et pas d’importations relatives.
.claude/skills/ contenant des workflows étape par étape et des modèles de code pour les tâches que vous effectuez régulièrement, comme la création d’une interface..claude/settings.json pour approuver à l’avance les commandes sûres et bloquer celles qui sont destructrices..claude/ afin que la configuration soit partagée et examinée comme du code.Pourquoi un monorepo NestJS déroute les assistants IA
Dans une grande base de code NestJS, la correction du code dépend souvent de conventions que aucun seul fichier ne révèle. Des dizaines de modules, des centaines de DTO, des gardes personnalisés, des intercepteurs, des filtres d’exception et des adaptateurs HTTP créent une véritable ambiguïté. Lorsque rien ne résout cette ambiguïté, le modèle recourt à des valeurs par défaut génériques et sensées, mais ces dernières correspondent rarement aux besoins d’une équipe spécifique.
Symptômes typiques d’une configuration non adaptée :
- Une méthode de service communique directement avec la base de données sans passer par la couche de repository.
- Les messages d’erreur sont des littéraux de chaîne plutôt que des entrées provenant d’un fichier de constantes
ErrorMessages. - Un nouveau fichier est créé dans
operator-servicebien que la logique de réservation appartienne àapi-service. - L’autorisation est vérifiée avec
user.role === 'admin'au lieu d’utiliserPermissionCodes. - Les sorties de débogage utilisent
console.logau lieu du générateur d’entrées structuré. - Les importations parcourent les répertoires via
../../common/utilsau lieu d’utiliser l’alias de cheminsrc/.
La cause racine est l’absence de connaissances partagées au sein de l’équipe
Aucun de ces problèmes n’est dû à des bugs du modèle. Au cours d’une session, l’assistant ne connaît que les fichiers qu’il a ouverts. Il n’a aucun moyen de savoir que l’équipe a délibérément supprimé RolesGuard, que l’option synchronize de TypeORM doit rester à la valeur false, ou que les services communiquent entre eux exclusivement via des adaptateurs HTTP. Ces connaissances résident dans l’esprit des ingénieurs et dans les anciennes discussions de pull request.
La solution consiste à consigner ces connaissances dans des fichiers que l’outil charge, et à associer chaque type de connaissance au mécanisme approprié :
CLAUDE.mdcontient l’architecture, les responsabilités des services et les conventions. Il est chargé automatiquement au début de chaque session.- Rules regroupe les interdictions et les invariants qui doivent guider chaque partie du code généré.
La structure du répertoire présumée dans ce guide
Il est utile de définir une structure concrète avant d’écrire toute configuration. Le backend d’exemple comprend quatre services ainsi qu’un seul répertoire .claude/ à la racine du répertoire :
my-backend/
├── .claude/ # Everything Claude needs to operate well
│ ├── CLAUDE.md # Root-level instructions (always loaded)
│ ├── rules/ # Enforced coding constraints
│ ├── skills/ # Custom slash commands with deep context
│ └── settings.json # Command permissions
├── services/
│ ├── api-service/ # NestJS consumer API (port 5001)
│ ├── operator-service/ # NestJS operator/vendor API (port 5002)
│ ├── chat-service/ # Express + Socket.IO (JavaScript, port 5005)
│ └── job-service/ # Scheduled jobs, no HTTP server
├── db/migration/ # Flyway migrations per schema
└── package.json # Root: only husky + lint-staged
Chaque service est une application autonome disposant de son propre package.json, tsconfig.json et des résultats de compilation. Notez que l’un d’eux, chat-service, est un simple script JavaScript utilisant Express et Socket.IO, tandis que job-service ne dispose d’aucun serveur HTTP. Cette hétérogénéité est précisément le type d’information qu’un assistant ne peut pas déduire à partir d’un seul fichier.
Dans chaque service NestJS, les modules fonctionnels suivent tous la même structure à trois niveaux :
src/modules/booking/
├── booking.module.ts
├── controllers/ # HTTP routing only, no logic
├── services/ # Business logic and orchestration
├── repositories/ # TypeORM queries only
└── dtos/ # Request/response shapes, validation
Les contrôleurs gèrent uniquement les requêtes HTTP, les services contiennent la logique métier et l’orchestration, tandis que les repositories regroupent les requêtes TypeORM. Si l’assistant n’a jamais été informé de cette séparation, il aura tendance à la confondre : un service qui envoie des requêtes, ou un contrôleur qui appelle directement un repository. C’est en définissant clairement ces couches que l’on peut respecter leurs limites.
Étape 1 : installation et authentification
L’installation consiste en un seul package global, suivie d’un authentification unique dans le navigateur :
npm install -g @anthropic-ai/claude-code
claude # opens a browser for one-time OAuth
Une fois authentifié, claude peut être lancé depuis n’importe quel répertoire. Chaque développeur l’installe et s’authentifie sur sa propre machine ; il n’existe pas de clé d’équipe à distribuer. Les options d’installation évoluent avec le temps, il convient donc de consulter la documentation actuelle de Claude Code si l’approche via npm n’est plus recommandée.
Un détail est particulièrement important pour les monorepos. La configuration du projet est chargée par rapport à la racine du répertoire, de sorte que les instructions situées dans .claude/ à la racine s’appliquent partout où vous lancez une session. Lorsque votre travail est limité à un seul service, lancez Claude depuis le dossier de ce service, par exemple services/api-service/, afin que les recherches et commandes s’exécutent sur le bon package.
Étape 2 : rédiger le document CLAUDE.md
CLAUDE.md est le fichier ayant l’impact le plus grand, car c’est le contexte que l’assistant lit en premier. Rédigez-le comme vous présenteriez les informations à un ingénieur senior lors de son premier jour : partez du principe qu’il possède des compétences générales et n’expliquez que ce qui est spécifique à votre système. Dans un monorepo, il est judicieux d’avoir deux niveaux :
- Un
.claude/CLAUDE.mdà la racine pour définir les responsabilités par service, les conventions communes et le pipeline de demande.
services/{service}/CLAUDE.md par service pour les modèles de modules, les détails d’authentification et d’injection de dépendances qui ne s’appliquent qu’à celui-ci.Fichier racine : faire de la responsabilité du service une décision, pas une supposition
La plus coûteuse erreur dans un dépôt multi-services est de placer du code au mauvais endroit, car il passe la revue locale sans problème et ne se révèle qu’ultérieurement comme une violation des limites définies. Le fichier racine empêche cela grâce à un tableau de routage et à une liste explicite des modèles interdits. Voici un exemple de ce que ce fichier peut contenir :
## Monorepo StructureFour services under `services/`, each independently runnable.
Run all commands from inside the service directory.| Service | Port | Stack | Purpose |
| ------------------ | ---- | ------------------------ | -------------------- |
| `api-service` | 5001 | NestJS + TypeScript | Consumer-facing API |
| `operator-service` | 5002 | NestJS + TypeScript | Operator/vendor mgmt |
| `chat-service` | 5005 | Express + Socket.IO + JS | Real-time messaging |
| `job-service` | — | Node.js + TypeScript | Scheduled jobs |## Which Service Owns What- **operator-service** — if the data describes _what an experience is_:
package definitions, pricing, taxes, availability, operator accounts.
- **api-service** — if the data describes _a consumer's interaction_:
bookings, users, auth, trip planning, payments, notifications.| Task involves... | Service |
| ---------------------------------- | ---------------------------------- |
| Package categories, pricing, slots | operator-service |
| Booking status, user profiles | api-service |
| Stripe payments (consumer side) | api-service |
| Cross-service booking flow | Start in api-service → IMS adapter |## Global Request Pipeline (in order)1. `TraceContextMiddleware` — sets `correlationId` in `AsyncLocalStorage`
2. `ValidationPipe` — whitelist mode, flattens DTO errors
3. `LoggerInterceptor` — logs request/response
4. `TransformInterceptor` — wraps return value in the standard shape
5. Exception filters (`HttpExceptionFilter`, `TypeOrmExceptionFilter`, …)## Standard Response ShapeControllers return `{ data, meta }`; `TransformInterceptor` wraps it:
return { data: { items }, meta: { path: request.path } };
// → { success: true, data: { items }, meta: { path }, error: null }## Cross-Service Communicationapi-service NEVER touches operator-service's database. All reads/writes go
through HTTP adapters in `src/common/adapters/` (they handle OAuth refresh and
trace propagation). Need operator data? Call the adapter, never the DB.## What Claude Must Never Do- Use `console.log` → use `logMessage()` from `src/common/utils/logger`
- Check `user.role` → use `PermissionCodes` + `@Permissions()`
- Query the DB inside a loop → use bulk queries (`IN`, `JOIN`)
- Use relative `../../` imports → use the `src/` alias
- Set TypeORM `synchronize: true` → Flyway manages all schema changes
Plusieurs parties de ce fichier méritent une attention particulière. La section relative aux droits d’auteur est formulée sous forme de question ("les données décrivent-elles ce qu’est une expérience, ou l’interaction d’un utilisateur avec elle ?") plutôt que comme une liste de répertoires, ce qui permet à l’assistant de classer les requêtes qu’il n’a jamais vues. La section concernant le pipeline énumère les middleware, les pipes, les intercepteurs et les filtres dans l’ordre d’exécution, afin que le code généré ne duplique pas ce que TransformInterceptor fait déjà. La section relative aux services interconnectés indique clairement que api-service ne touche jamais à la base de données des opérateurs et doit passer par les adaptateurs situés dans src/common/adapters/.
Ce qui distingue un CLAUDE.md utile d’un CLAUDE.md insuffisant :
- Les règles de décision valent mieux que les arbres de fichiers. Une règle telle que « les données décrivant ce qu’est une expérience doivent se trouver dans operator-service » élimine l’ambiguïté sans nécessiter de question de clarification.
- Définissez la structure de la réponse. Sans cela, chaque contrôleur enveloppe ses propres réponses, ce qui rend la structure globale confuse.
- Précisez ce qui est interdit. « Ne jamais utiliser
console.log» est plus facile à appliquer que « utilisez notre outil de journalisation ». - Enregistrez les suppressions délibérées. Si vous avez supprimé
RolesGuardou désactivésynchronize, indiquez-le et expliquez pourquoi. Sinon, l’assistant pourrait les réintroduire, justement parce qu’ils semblent correspondre à la solution habituelle dans NestJS.
Fichiers au niveau du service : une profondeur nécessaire uniquement à un service
Les détails qui ne sont pertinents qu’à l’intérieur d’un service particulier se trouvent dans le fichier propre à ce service. Pour api-service, cela concerne la hiérarchisation des couches, l’authentification, les tokens de DI et les tâches en arrière-plan :
## Module Architecture — never mix layerscontrollers/ HTTP routing only — call service, return data
services/ Business logic — orchestrate repos, no direct DB calls
repositories/ TypeORM queries only — accept EntityManager for transactions## Authentication- `JwtAuthGuard` validates Bearer tokens, attaches `UserRequest`
- `PermissionsGuard` reads `@Permissions([PermissionCodes.X])`
- Never use RolesGuard or check role names — removed intentionally## Dependency InjectionRegister services by interface token, and inject by token, not class:
{ provide: SERVICE_INTERFACE.BOOKING_SERVICE, useClass: BookingService }## Background JobsBullMQ + Redis. Processors live in `src/modules/{module}/processors/`.
Enqueue via the module's service — never from a controller.
Deux points ici sont facilement mal interprétés sans guidance. Premièrement, les fournisseurs sont enregistrés et injectés via des tokens d’interface tels que SERVICE_INTERFACE.BOOKING_SERVICE, et non par classe ; donc un constructeur généré qui injecte directement BookingService se compilerait mais violerait le schéma de travail de l’équipe. Deuxièmement, les tâches BullMQ ne sont mises en file que depuis le service d’un module, jamais depuis un contrôleur, et les traitements se trouvent sous src/modules/{module}/processors/. En ne mettant pas ces informations dans le fichier racine, elles n’affectent pas le contexte lorsque l’on travaille sur chat-service.
Étape 3 : ajouter des règles pour les situations qui ne doivent jamais se produire
CLAUDE.md décrit le fonctionnement du système, tandis que les règles indiquent ce que le code généré ne doit pas contenir. Un modèle mental utile est une configuration ESLint conçue pour les sorties d’IA. Les règles se trouvent sous forme de fichiers Markdown dans .claude/rules/.
Une façon simple de déterminer où placer chaque élément :
- L’architecture, la structure des modules, la responsabilité des services, les conventions ainsi que les références aux commandes doivent figurer dans
CLAUDE.md. - Les interdictions strictes, les schémas qui ne doivent jamais être générés, ainsi que les invariants de sécurité ou de performance doivent se trouver dans
rules/.
N’oubliez pas que les règles sont des instructions chargées dans le contexte du modèle, et non un analyseur statique. Elles réduisent fortement le risque de violations, mais pour les invariants vraiment importants, vous devez toujours les renforcer avec des règles de linting, des tests ou des vérifications CI.
Règle : interdiction des requêtes N+1
Les requêtes N+1 sont faciles à écrire et facilement négligées lors des revues, car chaque ligne individuelle semble inoffensive. Une règle dans .claude/rules/database-queries.md est la plus efficace lorsqu’elle montre à la fois le schéma interdit et sa substitution. La version interdite génère une requête par réservation :
// Wrong — N queries
for (const b of bookings) {
b.user = await this.userRepo.findOne(b.userId);
}
La substitution charge tous les utilisateurs en une seule requête à l’aide de l’opérateur In de TypeORM, puis les associe en mémoire via un Map indexé par l’id :
// Correct — one query
const users = await this.userRepo.find({ where: { id: In(userIds) } });
const userMap = new Map(users.map((u) => [u.id, u]));
bookings.forEach((b) => {
b.user = userMap.get(b.userId);
});
L’avantage est que la requête en masse est écrite au moment de la génération, de sorte que le problème de performance n’atteint jamais une demande de fusion. N’oubliez pas de collecter d’abord les userIds provenant des réservations, et envisagez de les dédoublonner lorsque plusieurs réservations partagent un utilisateur.
Règle : permissions, jamais des noms de rôles
La deuxième règle, .claude/rules/permissions.md, interdit les vérifications de rôles ad hoc comme celle-ci :
// Wrong
if (user.roles.includes('admin')) { ... }
À la place, les points de terminaison déclarent l’autorisation dont elles ont besoin et permettent aux gardiens de la faire respecter :
// Correct
@Permissions([PermissionCodes.MANAGE_BOOKINGS])
@UseGuards(JwtAuthGuard, PermissionsGuard)
La logique doit également figurer dans le fichier de règles, car cela aide le modèle à généraliser : les rôles sont fusionnés, divisés et renommés au fur et à mesure que le produit évolue, et toute vérification basée sur le nom d’un rôle cesse de fonctionner silencieusement lorsqu’une telle modification a lieu. Les codes d’autorisation sont des constantes stables qui survivent à de telles réorganisations. La règle doit également indiquer où ils se trouvent, dans ce cas src/common/constants/permissions.constant.ts.
Règle : alias de chemin plutôt que imports relatifs
Une troisième règle, .claude/rules/imports.md, standardise les imports :
// Wrong
import { UserService } from "../../services/user.service";
// Correct
import { UserService } from "src/modules/user/services/user.service";
Cet alias fonctionne parce que tsconfig.json définit baseUrl sur "./", et que moduleNameMapper de Jest utilise le même préfixe. Il est important d’en mentionner les deux aspects : si l’assistant ne connaissait que le côté TypeScript, il pourrait en conclure que cet alias n’est pas sûr dans les tests.
D’autres règles à établir
Le même format « interdire et proposer une alternative » convient à de nombreux problèmes récurrents dans NestJS. Exemples pour leurs propres fichiers :
- Gestion des erreurs : générer des exceptions NestJS à partir des constantes
ErrorMessagesouResponseCodeKeys; éviter de coder manuellement les messages ou de retourner des objets d’erreur bruts. - Journalisation : utiliser l’aide-mémoire structuré
logMessage()avec le niveau approprié ;console.logest interdit.
EntityManager, de sorte qu’une panne ne peut pas laisser les données partiellement enregistrées.class-validator. Comme ValidationPipe fonctionne en mode liste blanche, les propriétés sans décorateurs sont supprimées silencieusement, ce qui ressemble à une perte de données plutôt qu’à une erreur.synchronize reste false.@ApiSpec ainsi que d’un DTO de réponse typé, afin que la documentation API générée reste précise.Étape 4 : transformer les workflows répétitifs en compétences
Les compétences sont des commandes spécifiques à un projet qui regroupent une procédure, des modèles et du matériel de référence. Ce sont les outils idéaux pour toute tâche impliquant plusieurs fichiers que vous effectuez fréquemment : ajouter un point de terminaison, écrire une migration, créer la structure d’un module, examiner une demande de fusion. Contrairement à CLAUDE.md, le contenu d’une compétence n’est chargé que lorsque celle-ci est utilisée, de sorte que les modèles détaillés ne consomment pas de ressources en mémoire à chaque session.
Anatomie d’un répertoire de compétences
Une compétence destinée à créer des points de terminaison peut être organisée comme ceci, avec le flux de travail dans SKILL.md, les modèles dans assets/ et le matériel de référence dans references/ :
.claude/skills/api-development/
├── SKILL.md # Workflow, trigger, steps
├── assets/
│ ├── controller-template.md
│ ├── service-template.md
│ ├── repository-template.md
│ └── dto-template.md
└── references/
├── nestjs-conventions.md
└── swagger-documentation.md
Le flux de travail de SKILL.md
Le fichier commence par un titre simple :
API Development Skill
Le corps du fichier indique ensuite quand appeler la compétence et décrit les étapes dans un ordre fixe :
## TriggerInvoke with `/api-development` when building or modifying a NestJS endpoint.## Workflow1. **Determine service & module** — check CLAUDE.md routing; ask if ambiguous.
2. **Read what exists** — entities, repositories, DTOs already in the module.
3. **Generate the DTO first** — it defines the contract. class-validator
decorators; extend `PaginationRequestDTO` for lists.
4. **Generate the repository** — inject `DataSource`; accept an optional
`EntityManager` for transactions; queries only, no logic.
5. **Generate the service** — inject the repository interface token; use
`ErrorMessages` constants; throw `NotFoundException` etc.
6. **Generate the controller** — `@ApiSpec` + `@ApiOkResponse`; return
`{ data, meta }`; no business logic.
7. **Wire the module** — add to `providers`, export by interface token.
L’ordre choisi n’est pas fortuit. Vérifier d’abord le tableau de routage empêche cette compétence de s’intégrer dans le mauvais service. La lecture des entités existantes, des repositories et des DTO permet d’éviter leur duplication. La génération du DTO avant tout le reste définit le contrat, sur lequel sont construits les différents niveaux suivants : le repository accepte un EntityManager optionnel afin de pouvoir gérer les transactions, le service est injecté via un token d’interface et lance des exceptions standard accompagnées de constantes ErrorMessages, tandis que le contrôleur se contente de documenter, de déléguer et de renvoyer { data, meta }. Enfin, l’étape finale relie les fournisseurs au module et les exporte via un token.
Une remarque pratique : les versions actuelles de Claude Code détectent les compétences grâce au frontmatter YAML en haut du fichier SKILL.md (un name et une description indiquant quand l’utiliser). L’exemple ci-dessus l’omet, il convient donc de consulter la documentation actuelle pour connaître le format exact à utiliser lors de la création de vos propres fichiers.
Les modèles doivent être du code réel
Les modèles situés dans assets/ ne sont utiles que s’ils se compilent. Le pseudocode pousse le modèle à combler les lacunes avec ses propres valeurs par défaut. Le modèle du contrôleur commence par la déclaration de la classe et l’injection basée sur des tokens :
@ApiTags("bookings")
@Controller("bookings")
export class BookingController {
constructor(
@Inject(SERVICE_INTERFACE.BOOKING_SERVICE)
private readonly bookingService: IBookingService,
) {}
Il se poursuit par un endpoint complet qui affiche en même temps l’ordre des vérifications, le décorateur de permission, les décorateurs Swagger ainsi que la structure du retour :
@Post()
@UseGuards(JwtAuthGuard, PermissionsGuard)
@Permissions([PermissionCodes.CREATE_BOOKING])
@ApiSpec({ summary: "Create a new booking" })
@ApiCreatedResponse({ type: CreateBookingSuccessDto })
async createBooking(
@Body() dto: CreateBookingDto,
@Req() req: UserRequest,
): Promise<{ data: BookingEntity }> {
const booking = await this.bookingService.createBooking(dto, req.user.id);
return { data: booking };
}
}
Remarquez ce que cette seule méthode enseigne : JwtAuthGuard s’exécute avant PermissionsGuard, l’identifiant de l’utilisateur provient du type UserRequest, le contrôleur ne contient aucune logique métier, et il renvoie { data } afin que TransformInterceptor puisse l’encapsuler. Grâce à une telle compétence, le scénario décrit ici permet de créer un endpoint complet (contrôleur, service, repository et DTOs, intégrés au module) en moins de trois minutes, contre environ vingt minutes selon la méthode habituelle de l’équipe dès la première tentative. Vos propres temps dépendront de la taille de vos templates et du nombre d’examens que vous effectuerez encore.
Autres compétences à considérer
Tout flux de travail répétitif suivant des conventions propres à l’équipe est un bon candidat :
- Générateur de migrations : crée une migration Flyway dans le dossier du schéma approprié en utilisant votre système de versionnement et de nommage, ainsi que la modification d’entité correspondante.
- Générateur de squelette de module : produit un fichier de module, les trois couches et les tokens DI déjà connectés.
- Vérification des demandes de pull : vérifie les différences pour détecter les requêtes N+1, l’utilisation des permissions, les violations de couche et l’absence de tests.
- Générateur de tests : écrit des fichiers
*.spec.tsconformes aux conventions de simulation et d’éléments fixes des services. - Adaptateur inter-service : ajoute une méthode d’adaptateur IMS ainsi que son interface de réponse et son outil de transformation.
- Définition des cas d’utilisation : transforme une exigence en histoires utilisateur avec des critères d’acceptation de type Given/When/Then.
Étape 5 : configurer les permissions
.claude/settings.json détermine quels commandes de shell l’assistant peut exécuter sans demander d’autorisation au préalable. Une base raisonnable approuve à l’avance les vérifications de syntaxe, les tests et les compilations, tout en refusant explicitement les commandes qui détruisent du travail ou le publient :
{
"permissions": {
"allow": [
"Bash(npm run lint:*)",
"Bash(npm run test:*)",
"Bash(npm run build)",
"Bash(npx jest *)"
],
"deny": ["Bash(rm -rf *)", "Bash(git push *)", "Bash(git reset --hard *)"]
}
}
Les paramètres se divisent en deux niveaux :
.claude/settings.jsons’applique à toute l’équipe et est mis en version contrôlée..claude/settings.local.jsoncontient des modifications personnelles, ne s’applique qu’à vous et doit être ignoré par git.
La liste des commandes interdites constitue un filet de sécurité, et non un environnement isolé. Les modèles correspondent aux chaînes de commandes, il convient donc de les considérer comme un moyen de prévenir les accidents plutôt que comme une barrière de sécurité.
Pratiques pour maintenir un environnement en bon état
Traitez .claude/ comme du code de production : il diffuse vos conventions à chaque ingénieur et lors de chaque session, il mérite donc d’être revu, suivi dans son historique et régulièrement simplifié.
- Gardez
CLAUDE.mdcourt, avec au maximum 300 lignes. Tout ce qui s’y trouve concourt à occuper de l’espace contextuel avec le code que l’assistant doit lire. Si une information peut être déduite du code, omettez-la. - Formulez les règles en interdictions. En pratique, l’instruction "ne jamais interroger la base de données à l’intérieur d’une boucle" tient mieux la route sous pression que "préférez les requêtes en masse">.
- Gardez les modèles dans les compétences. Ils ne doivent s’charger que lorsque la compétence est exécutée, et non à chaque session.
- Gérez la version
.claude/dans git. De nouvelles règles arrivent via des pull requests ; les anciennes sont supprimées. - Utilisez des tableaux de décision chaque fois que deux options sont plausibles, comme pour le placement des DTO, l’emplacement des entités ou les limites des services. Dans les fichiers Markdown destinés à l’assistant, un tableau est plus clair que des paragraphes.
Un layout complet pour la configuration décrite dans ce guide :
.claude/
├── CLAUDE.md # Root guide (<300 lines)
├── settings.json # Team permissions (committed)
├── rules/
│ ├── database-queries.md # No N+1
│ ├── permissions.md # PermissionCodes, no role checks
│ └── imports.md # src/ alias only
└── skills/
└── api-development/
├── SKILL.md
├── assets/ # controller/service/repository/dto templates
└── references/ # conventions, swagger
Erreurs qui détériorent silencieusement la sortie
Chacune d’elles génère du code qui semble correct tout en violant vos conventions, ce qui en fait des erreurs coûteuses.
- Écrire
CLAUDE.mdcomme un tutoriel NestJS. Le modèle connaît déjà le framework. Documentez uniquement ce qui est spécifique à vous : noms de modules, constantes d’erreur, enveloppe de réponse, système de permissions. - Omettre les règles négatives. Les erreurs coûteuses sont celles qui semblent correctes ; indiquer à l’assistant ce qu’il ne faut pas faire élimine toute une catégorie d’erreurs.
- Mettre tout dans le fichier racine. Le fichier racine est chargé à chaque session, donc une profondeur spécifique aux services y pollue le contexte lorsque vous travaillez ailleurs.
synchronize: false. Le modèle supposera que TypeORM gère le schéma. Avec Flyway en charge, toute modification d’entité sans migration échouera silencieusement, il faut donc l’indiquer explicitement.Décortiquage : une requête, avant et après
Supposons qu’un responsable produit demande l’exposition d’une état des réservations afin que les clients puissent le voir sur leur colis.
Dans un dépôt non configuré, l’assistant en déduit que les paquets sont définis dans operator-service et modifie l’entité de paquet correspondante, ce qui concerne le mauvais service. Il code en dur la chaîne de statut et protège ce changement avec if (user.roles.includes('admin')).
Avec la configuration de ce guide, la demande est résolue étape par étape :
- Le tableau de routage classe un statut visible par le consommateur comme faisant partie de son interaction, donc ce travail doit avoir lieu dans
api-service. - La règle inter-service lui rappelle que les données des paquets d’opérateur sont lues via un adaptateur IMS, ce qui amène à ajouter le champ dans la transformation de l’adaptateur plutôt qu’à accéder à une autre base de données.
- L’exécution de
/api-developmentgénère les quatre couches dans l’ordre, en partant du DTO pour arriver au contrôleur.
@Permissions([PermissionCodes.VIEW_BOOKING_STATUS]) au lieu d’une vérification de rôle.NotFoundException à partir d’une constante ErrorMessages.Une seule phrase vague se transforme en modifications conformes aux conventions dans quatre fichiers, sans nécessiter de questions pour plus de clarté.
Quelles sont les modifications quotidiennes
La configuration prend quelques heures une seule fois. Les modifications qu’elle est censée générer :
- Le focus de l’examen change. Au lieu de vérifier chaque fichier généré pour y détecter des chaînes de caractères enregistrées, des importations incorrectes et des vérifications de rôle, les examinateurs concentrent leur attention sur la logique.
- Les points d’entrée deviennent plus rapides. Dans la configuration décrite, le raccordement manuel de quatre couches prenait environ vingt minutes ; cette compétence permet de le réduire à moins de trois minutes.
console.log sont empêchés pendant la génération, au lieu d’être découverts dans les pull requests.Certains avantages ne se manifestent que sur le long terme. Le code généré à des intervalles éloignés semble cohérent car il provient des mêmes modèles et règles. Les reviewers cessent d’écrire les mêmes commentaires. Les tables de routage et les règles servent de documentation réellement mise en pratique. De plus, un CLAUDE.md succinct associé à des compétences sur demande permet d’allouer le temps de travail à la lecture du code plutôt qu’à la redécouverte des conventions.
Questions fréquentes
Où placer les fichiers dans un monorepo ? Placez le CLAUDE.md partagé dans .claude/ au niveau racine de git pour les aspects transversaux, et un CLAUDE.md à l’intérieur de chaque service pour ses spécificités. Le fichier racine est toujours chargé ; le fichier du service est consulté lorsque vous travaillez sur ce dernier.
En quoi CLAUDE.md diffère-t-il des règles ? CLAUDE.md explique le fonctionnement du système ; les règles indiquent ce qui ne doit jamais apparaître, comme des requêtes dans des boucles ou des vérifications de noms de rôles.
Comment se comportent les compétences ? Une compétence telle que /api-development est soutenue par un workflow SKILL.md ainsi que par des modèles et des références. Lorsqu’elle est invoquée, l’assistant suit une procédure fixe en plusieurs étapes au lieu d’improviser.
Faut-il commiter .claude/ ? Oui : CLAUDE.md, rules/, skills/ et settings.json doivent tous être dans git afin que l’équipe partagee une seule configuration. Les modifications personnelles se trouvent dans settings.local.json qui est ignoré par git.
Points clés
- Dans un monorepo NestJS, la correction dépend de conventions qui ne sont pas visibles dans un seul fichier, ce qui oblige un assistant sans contexte à deviner.
- Divisez ces connaissances par type :
CLAUDE.mdpour expliquer le fonctionnement des éléments, des règles pour ce qui est interdit, des procédures répétitives dans des fichiers dédiés, ainsi que des paramètres pour ce qui peut fonctionner sans surveillance. - Le tableau de décision concernant la responsabilité des services est l’élément le plus important ; écrivez-le en premier.
- Dokumentez les suppressions intentionnelles et les choix non par défaut tels que
synchronize: false, car ce sont précisément les choses que un modèle tenterait sinon de « corriger ». - Les règles orientent fortement la génération mais ne servent pas à l’imposer ; utilisez lint, des tests et des processus CI pour les invariants que vous ne pouvez pas vous permettre de briser.
- Cet effort représente un investissement unique de quelques heures qui se révèle utile pour chaque fonctionnalité et chaque nouvel employé, à condition que les fichiers soient révisés et affinés comme du code.