Startseite / Artikel

Wie man Claude Code sein NestJS-Monorepo beibringt: Routing, Regeln und Fähigkeiten

Lehren Sie Claude Code ein NestJS-Monorepo: CLAUDE.md, Regeln, Fähigkeiten und Berechtigungen, die den Code in den richtigen Service leiten und sicherstellen, dass er den Teamkonventionen folgt.

Verlag: REACTAPP.TOP20 Min. Lesezeit4492 Wörter

Quellen: Claude Code for NestJS Monorepos: A Practical Setup Guide

Fügen Sie einem unkonfigurierten KI-Assistenten in einem großen NestJS-Monorepo eine Anfrage wie „Füge einen für den Verbraucher sichtbaren Status zu einer Buchung hinzu“ hinzu – das Ergebnis ist vorhersehbar: Die Änderung landet im falschen Microservice, die Fehlermeldung ist eine hardcodierte Zeichenkette und der Zugriff wird durch user.role === 'admin' geschützt. Das Modell versagt nicht aufgrund von TypeScript; es tippt vielmehr Entscheidungen aus, die Ihr Team vor langer Zeit getroffen hat und nie schriftlich festgehalten hat, wo es sie finden könnte. Dieser Leitfaden zeigt, wie man dieses Raten durch kontextbasierte Versionierung ersetzen kann, indem man die vier Bausteine nutzt, die Claude Code bietet: CLAUDE.md, Regeln, Fähigkeiten sowie Berechtigungseinstellungen. Am Ende werden Sie ein Layout haben, das Sie in Ihr Repository speichern können, sodass jeder Entwickler sowie jede KI-Sitzung von denselben Konventionen ausgeht.

Für allgemeine Ratschläge zur Erstellung der Beschreibungsdokumente selbst siehe unser Leitfaden für ein effektives CLAUDE.md. Dieser Artikel konzentriert sich auf die Veränderungen, wenn die Codebasis ein mehrdienstiger NestJS-Backend ist.

Kurzfassung

Für Leser, die zuerst die Checkliste und danach die Erklärungen möchten:

  1. Installieren Sie Claude Code (zum Beispiel mit npm install -g @anthropic-ai/claude-code) und authentifizieren Sie sich einmal, indem Sie claude ausführen.
  2. Erstellen Sie eine Wurzeldatei .claude/CLAUDE.md, in der erklärt wird, welcher Dienst welche Daten besitzt, die Reihenfolge Ihres Anfragenpipelines sowie das Antwortformat, das jeder Endpunkt zurückgibt.
  3. Fügen Sie ein Verzeichnis .claude/rules/ hinzu, in dem Verbote festgelegt werden: Keine Abfragen innerhalb von Schleifen, keine Überprüfungen nach Rollennamen und keine relativen Importe.
  • Fügen Sie ein Verzeichnis .claude/skills/ hinzu, das schrittweise Arbeitsabläufe sowie Code-Vorlagen für wiederkehrende Aufgaben wie die Erstellung eines Endpunkts enthält.
  • Fügen Sie .claude/settings.json hinzu, um sichere Befehle im Voraus zu genehmigen und zerstörerische zu blockieren.
  • Committen Sie das gesamte Verzeichnis .claude/, damit die Konfiguration wie Code geteilt und überprüft werden kann.
  • Warum NestJS-Monorepos KI-Assistenten verwirren

    In einer großen NestJS-Codebasis hängt es oft von Konventionen ab, ob der Code korrekt ist – Konventionen, die in keinem einzelnen Datei nicht sichtbar sind. Dutzende von Modulen, Hunderte von DTOs, benutzerdefinierte Guards, Interceptors, Ausnahmefilter sowie HTTP-Adapter erzeugen echte Unklarheiten. Wenn diese Unklarheiten nicht beseitigt werden, greift das Modell auf sinnvolle Standardwerte zurück – doch solche Standardwerte passen selten zu einem bestimmten Team.

    Typische Symptome einer unkonfigurierten Einrichtung:

    • Eine Service-Methode kommuniziert direkt mit der Datenbank, anstatt über die Repository-Schicht zu gehen.
    • Fehlermeldungen sind String-Literale statt Einträge aus einer ErrorMessages-Konstantendatei.
    • In operator-service wird eine neue Datei erstellt, obwohl die Buchungslogik zum api-service gehört.
    • Die Autorisierung wird als user.role === 'admin' implementiert, anstatt PermissionCodes zu verwenden.
    • Für die Debug-Ausgabe wird console.log statt des strukturierten Loggers genutzt.
    • Importe gehen mit ../../common/utils durch die Verzeichnisse, anstatt den Pfadalias src/ zu verwenden.

    Die Ursache liegt im Fehlen von internem Wissen

    Keines davon ist ein Fehler im Modell. Während einer Sitzung kennt der Assistent nur die Dateien, die er geöffnet hat. Er hat keine Möglichkeit zu erfahren, dass das Team RolesGuard absichtlich gelöscht hat, dass die synchronize-Option von TypeORM unbedingt auf false bleiben muss, oder dass Dienste ausschließlich über HTTP-Adapter miteinander kommunizieren. Dieses Wissen befindet sich im Kopf der Ingenieure sowie in alten Pull-Request-Threads.

    Die Lösung besteht darin, dieses Wissen in Dateien niederzuschreiben, die das Tool lädt, und jede Art von Wissen dem richtigen Mechanismus zuzuordnen:

    • CLAUDE.md enthält Informationen zur Architektur, zur Verantwortlichkeit der Dienste sowie zu gängigen Konventionen. Es wird automatisch zu Beginn jeder Sitzung geladen.
    • Regeln enthalten Verbote und Invarianten, die jeden generierten Code beeinflussen sollten.
  • Fähigkeiten umfassen mehrstufige Workflows und Vorlagen, die nur dann herangezogen werden, wenn eine entsprechende Aufgabe auftaucht.
  • Die Repository-Struktur, die in diesem Leitfaden vorausgesetzt wird

    Es ist hilfreich, eine konkrete Struktur festzulegen, bevor man Konfigurationen erstellt. Das Beispiel-Backend verfügt über vier Dienste sowie ein einzelnes .claude/-Verzeichnis im Wurzelverzeichnis des Repositoriums:

    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
    

    Jeder Dienst ist eine eigenständige Anwendung mit seinem eigenen package.json, tsconfig.json sowie den Ergebnissen der Kompilierung. Beachten Sie, dass einer davon, chat-service, reines JavaScript auf Basis von Express und Socket.IO ist, während job-service überhaupt keinen HTTP-Server besitzt. Genau diese Heterogenität ist etwas, was ein Assistent nicht aus einer einzigen Datei ableiten kann.

    In jedem NestJS-Dienst teilen sich die Funktionsmodule dieselbe dreischichtige Struktur:

    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
    

    Controller leiten HTTP-Anfragen weiter und nichts weiter, Services enthalten die Geschäftslogik sowie die Orchestrierung, und Repositorien enthalten TypeORM-Abfragen. Wenn dem Assistenten diese Aufteilung noch nie erklärt wurde, wird er sie gerne verschwimmen lassen: ein Service, der Abfragen ausführt, oder ein Controller, der direkt ein Repository aufruft. Nur durch die schriftliche Festlegung dieser Schichten kann er die Grenzen einhalten.

    Schritt 1: Installation und Authentifizierung

    Die Installation erfolgt mit einem einzigen globalen Paket, gefolgt von einer einmaligen Anmeldung im Browser:

    npm install -g @anthropic-ai/claude-code
    claude   # opens a browser for one-time OAuth
    

    Sobald die Authentifizierung abgeschlossen ist, kann claude aus jedem Verzeichnis gestartet werden. Jeder Entwickler installiert das Programm und meldet sich auf seinem eigenen Rechner an; es gibt keinen gemeinsamen Team-Schlüssel, der verteilt werden muss. Die Installationsoptionen ändern sich im Laufe der Zeit, daher sollten Sie die aktuelle Claude Code-Dokumentation prüfen, falls der npm-Route nicht mehr empfohlen wird.

    Ein Detail ist insbesondere für Monorepos wichtig. Die Projektkonfiguration wird relativ zum Repository-Root erfasst, sodass die Anweisungen im Root-Verzeichnis .claude/ überall dort gelten, wo Sie eine Sitzung starten. Wenn Ihre Arbeit auf einem einzigen Service beschränkt ist, starten Sie Claude aus dem entsprechenden Service-Verzeichnis, wie zum Beispiel services/api-service/, damit Suchanfragen und Befehle am richtigen Paket ausgeführt werden.

    Schritt 2: Verfassen des CLAUDE.md-Briefings

    CLAUDE.md ist die Datei mit dem größten Einfluss, da es der Kontext ist, den der Assistent zuerst liest. Schreiben Sie ihn so, als würden Sie einem erfahrenen Ingenieur an seinem ersten Tag Informationen geben: gehen Sie von allgemeiner Kompetenz aus und erklären Sie nur das, was für Ihr System spezifisch ist. In einem Monorepo macht es Sinn, zwei Ebenen zu verwenden:

    1. Ein Root-Verzeichnis .claude/CLAUDE.md für die Verantwortlichkeiten der Services, gemeinsame Konventionen sowie den Anfragefluss.
  • Eine services/{service}/CLAUDE.md-Datei pro Service für Muster von Modulen, Informationen zur Authentifizierung sowie Details zur Abhängigkeitsinjektion, die nur dort gelten.
  • Wurzeldatei: Die Verantwortung für Services klar festlegen, statt zu raten

    Der teuerste Fehler in einem Repository mit mehreren Services ist es, Code an den falschen Ort zu platzieren – er wird zunächst lokal geprüft und zeigt erst später als Grenzverletzung auf. Die Wurzeldatei verhindert dies mithilfe einer Routing-Tabelle sowie einer expliziten Liste der verbotenen Muster. Hier ist ein Beispiel dafür, was diese Datei enthalten kann:

    ## 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
    

    Mehrere Teile dieser Datei erfordern Aufmerksamkeit. Der Abschnitt zur Eigentumsverteilung ist als Test formuliert (“Beschreibt die Daten, was eine Erfahrung ausmacht, oder die Interaktion eines Nutzers damit?”) anstatt als Verzeichnisliste, was dem Assistenten ermöglicht, Anfragen zu klassifizieren, die er noch nie gesehen hat. Der Abschnitt zur Pipeline listet Middleware, Pipes, Interceptor und Filter in der Ausführungsreihenfolge auf, sodass der generierte Code nicht das wiederholt, was TransformInterceptor bereits erledigt. Der Abschnitt zu den cross-service-Beziehungen besagt klar, dass api-service niemals die Operator-Datenbank berührt und über die Adapter in src/common/adapters/ gehen muss.

    Was einen nützlichen CLAUDE.md von einem schwachen unterscheidet:

    • Entscheidungsregeln sind besser als Dateistrukturen. Eine Regel wie „Daten, die beschreiben, was eine Erfahrung ist, gehören in operator-service“, beseitigt Unklarheiten ohne dass eine klärende Frage gestellt werden muss.
    • Geben Sie die Struktur der Antwort an. Ohne diese muss jeder Controller seine eigenen Antworten verpacken, wodurch die Struktur unübersichtlich wird.
    • Auflisten Sie, was verboten ist. „Verwenden Sie niemals console.log“ ist leichter umzusetzen als „Verwenden Sie unseren Logger“.
    • Protokollieren Sie bewusste Entfernungen. Wenn Sie RolesGuard entfernt oder synchronize deaktiviert haben, geben Sie das an und erklären Sie den Grund. Andernfalls könnte der Assistent sie erneut einfügen, gerade weil sie wie die übliche Wahl in NestJS aussehen.

    Dienstebene-Dateien: Tiefe, die nur ein Dienst benötigt

    Die Details, die nur innerhalb eines Services relevant sind, werden in der eigenen Datei dieses Services gespeichert. Für api-service umfassen diese Aspekte die Schichtstruktur, Authentifizierung, DI-Tokens sowie Hintergrundaufgaben:

    ## 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.
    

    Zwei Punkte hier lassen sich ohne Anleitung leicht falsch verstehen. Erstens werden Provider über Interface-Tokens wie SERVICE_INTERFACE.BOOKING_SERVICE und nicht über Klassen registriert und injiziert, sodass ein generierter Konstruktor, der direkt BookingService injiziert, zwar kompilieren würde, aber das von dem Team verwendete Muster verletzen würde. Zweitens werden BullMQ-Aufgaben nur aus dem Service eines Moduls in die Warteschlange gegeben, niemals aus einem Controller, und die Verarbeiter befinden sich unter src/modules/{module}/processors/. Indem diese Fakten aus der Root-Datei ausgeblendet werden, verbrauchen sie keinen Kontext, wenn man in chat-service arbeitet.

    Schritt 3: Regeln für Dinge hinzufügen, die auf keinen Fall geschehen dürfen

    Während CLAUDE.md beschreibt, wie das System funktioniert, legen Regeln fest, was im generierten Code nicht enthalten sein darf. Ein nützliches mentales Modell ist eine ESLint-Konfiguration, die auf Ausgaben von KI ausgerichtet ist. Die Regeln sind Markdown-Dateien innerhalb von .claude/rules/.

    Eine einfache Methode, um zu entscheiden, wohin etwas gehört:

    • Architektur, Modulstruktur, Verantwortlichkeiten für Services, Konventionen sowie Befehlsreferenzen gehören in CLAUDE.md.
    • Harte Verbote, Muster, die auf keinen Fall ausgegeben werden dürfen, sowie Sicherheits- oder Leistungsaspekte gehören in rules/.

    Denken Sie daran, dass Regeln Anweisungen sind, die in den Kontext des Modells geladen werden, und kein statischer Analysewerkzeug. Sie verringern Verstöße erheblich, doch für wirklich wichtige Aspekte sollten Sie sie weiterhin durch Lint-Regeln, Tests oder CI-Prüfungen unterstützen.

    Regel: Keine N+1-Abfragen

    N+1-Abfragen sind einfach zu schreiben und leicht übersehen bei der Überprüfung, da jede einzelne Zeile unschuldig erscheint. Eine Regel in .claude/rules/database-queries.md funktioniert am besten, wenn sie sowohl das verbotene Muster als auch die Ersatzlösung zeigt. Die verbotene Version führt eine Abfrage pro Buchung aus:

    // Wrong — N queries
    for (const b of bookings) {
      b.user = await this.userRepo.findOne(b.userId);
    }
    

    Die Ersatzlösung lädt alle Benutzer mit dem In-Operator von TypeORM in einer einzigen Abfrage und fügt sie anschließend über ein mit id verschlüsseltes Map im Speicher hinzu:

    // 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);
    });
    

    Der Vorteil ist, dass die Massenabfrage zur Generierungzeit erstellt wird, sodass das Leistungsproblem niemals in einen Pull Request gelangt. Vergessen Sie nicht, zunächst die userIds aus den Buchungen zu sammeln, und überlegen Sie, sie zu deduplizieren, wenn viele Buchungen denselben Benutzer teilen.

    Regel: Berechtigungen, niemals Rollennamen

    Die zweite Regel, .claude/rules/permissions.md, verbietet ad hoc durchgeführte Rollenprüfungen wie diese:

    // Wrong
    if (user.roles.includes('admin')) { ... }
    

    Anstelle dessen deklarieren die Endpunkte die benötigte Berechtigung und überlassen es den Wächtern, diese durchzusetzen:

    // Correct
    @Permissions([PermissionCodes.MANAGE_BOOKINGS])
    @UseGuards(JwtAuthGuard, PermissionsGuard)
    

    Die Begründung sollte ebenfalls im Regelwerk enthalten sein, denn sie hilft dem Modell dabei, zu generalisieren: Im Laufe der Entwicklung werden Rollen zusammengeführt, aufgeteilt oder umbenannt, und jede Prüfung, die auf den Namen einer Rolle ausgerichtet ist, funktioniert dann nicht mehr. Berechtigungscode sind stabile Konstanten, die solche Umstrukturierungen überstehen. Die Regel sollte außerdem angeben, wo sich diese befinden – in diesem Fall in src/common/constants/permissions.constant.ts.

    Regel: Pfad-Alias anstelle von relativen Importen

    Eine dritte Regel, .claude/rules/imports.md, standardisiert die Importe:

    // Wrong
    import { UserService } from "../../services/user.service";
    // Correct
    import { UserService } from "src/modules/user/services/user.service";
    

    Der Alias funktioniert, weil tsconfig.json baseUrl auf "./" setzt und Jests moduleNameMapper dasselbe Präfix verwendet. Es ist wichtig, beides zu erwähnen: Wenn der Assistent nur über die TypeScript-Seite Bescheid wüsste, könnte er zu dem Schluss kommen, dass der Alias in Tests unsicher ist.

    Weitere Regeln, die es wert sind, festgelegt zu werden

    Das gleiche Format „verboten und Alternative anzeigen“ eignet sich für viele wiederkehrende NestJS-Probleme. Beispiele für eigene Dateien:

    • Fehlerbehandlung: Werfen Sie NestJS-Exceptions, die aus den Konstanten ErrorMessages oder ResponseCodeKeys erstellt werden; kodieren Sie niemals Nachrichten fest oder geben Sie rohe Fehlerobjekte zurück.
    • Logging: Verwenden Sie den strukturierten Hilfsfunktion logMessage() mit dem entsprechenden Level; console.log ist nicht zulässig.
  • Transaktionen: Operationen, die mehrfach geschrieben werden, müssen eine einzige EntityManager-Transaktion teilen, damit ein Fehler nicht dazu führt, dass die Daten nur teilweise gespeichert werden.
  • DTO-Validierung: Jedes AnfragedTO benötigt Decorator von class-validator. Da ValidationPipe im Whitelist-Modus läuft, werden Eigenschaften ohne Decorator stumm entfernt, was wie ein Datenverlust und nicht als Fehler erscheint.
  • Gleichheit von Entity und Migration: Jeder Änderung an einer Entity erfordert eine entsprechende Flyway-Migration, und synchronize bleibt auf false.
  • Swagger-Coverage: Jeder Endpunkt erhält @ApiSpec sowie ein typisiertes Antwort-DTO, damit die generierte API-Dokumentation aktuell bleibt.
  • Schritt 4: Wiederkehrende Workflows in Fähigkeiten umwandeln

    Skills sind projektbezogene Slash-Befehle, die Verfahren, Vorlagen und Referenzmaterialien zusammenfassen. Sie sind das richtige Werkzeug für alle mehrdateibasierten Aufgaben, die Sie häufig durchführen: das Hinzufügen eines Endpunkts, das Schreiben einer Migration, das Erstellen eines Moduls oder die Überprüfung eines Pull Requests. Im Gegensatz zu CLAUDE.md werden die Inhalte einer Skill nur dann geladen, wenn sie verwendet wird, sodass detaillierte Vorlagen in jeder Sitzung keinen zusätzlichen Speicherbedarf verursachen.

    Anatomie eines Skill-Verzeichnisses

    Eine Skill zur Erstellung von Endpunkten könnte wie folgt organisiert sein, wobei der Workflow in SKILL.md, die Vorlagen in assets/ und das Hintergrundmaterial in references/ gespeichert sind:

    .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
    

    Der Workflow in SKILL.md

    Die Datei beginnt mit einem einfachen Titel:

    API Development Skill
    

    Anschließend wird beschrieben, wann die Skill aufgerufen werden soll, und die Schritte werden in einer festgelegten Reihenfolge dargestellt:

    ## 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.
    

    Die Reihenfolge ist absichtlich gewählt. Die Überprüfung der Routing-Tabelle zuerst verhindert, dass die Komponente in den falschen Service eingebunden wird. Das Lesen vorhandener Entitäten, Repositorien und DTOs vermeidet deren Duplizierung. Die Erstellung des DTOs vor allem anderen stellt den Vertrag sicher, an dem jede nachfolgende Schicht gebaut wird: Das Repository akzeptiert einen optionalen EntityManager, um Transaktionen zu verknüpfen, der Service wird über ein Interface-Token injiziert und wirft Standardausnahmen mit ErrorMessages-Konstanten aus, während der Controller lediglich dokumentiert, delegiert und { data, meta } zurückgibt. Der letzte Schritt verbindet die Anbieter mit dem Modul und exportiert sie über ein Token.

    Eine praktische Hinweis: Aktuelle Versionen von Claude Code erkennen Fähigkeiten über YAML-Frontmatter am Anfang von SKILL.md – dabei handelt es sich um einen name sowie eine description, die angeben, wann die Fähigkeit verwendet werden soll. Das obige Beispiel lässt dies aus; prüfen Sie daher die aktuelle Dokumentation nach dem genauen Format, wenn Sie eigene Dateien erstellen.

    Templates sollten echter Code sein

    Templates in assets/ sind nur dann nützlich, wenn sie kompiliert werden können. Pseudocode veranlasst das Modell dazu, Lücken mit eigenen Standardwerten zu füllen. Das Controller-Template beginnt mit der Klassenerklärung sowie der tokenbasierten Injektion:

    @ApiTags("bookings")
    @Controller("bookings")
    export class BookingController {
      constructor(
        @Inject(SERVICE_INTERFACE.BOOKING_SERVICE)
        private readonly bookingService: IBookingService,
      ) {}
    

    Darauf folgt ein vollständiger Endpunkt, der in einem Zug den Schutzablauf, den Berechtigungsdekorator, die Swagger-Dekoratoren sowie die Rückgabestruktur zeigt:

      @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 };
      }
    }
    

    Achten Sie darauf, was diese einzige Methode lehrt: JwtAuthGuard wird vor PermissionsGuard ausgeführt, die Benutzer-ID stammt vom typisierten UserRequest, der Controller enthält keine Geschäftslogik, und er gibt { data } zurück, damit TransformInterceptor es umhüllen kann. Mit einer solchen Fähigkeit lässt sich das hier beschriebene Szenario so umsetzen, dass ein vollständiger Endpunkt (Controller, Service, Repository sowie DTOs, die im Modul miteinander verbunden sind) in weniger als drei Minuten erstellt werden kann – anstatt wie bisher etwa zwanzig Minuten. Ihre eigenen Zeiten hängen von der Größe Ihrer Vorlagen sowie davon ab, wie viel Überprüfung Sie noch durchführen.

    Weitere Fähigkeiten, die in Betracht gezogen werden sollten

    Jeder wiederkehrende Workflow, der team-spezifische Konventionen beinhaltet, eignet sich gut dafür:

    • Migrationsgenerator: Erstellt eine Flyway-Migration im richtigen Schema-Ordner unter Verwendung Ihres Versionierungs- und Benennungsschemas sowie der entsprechenden Entitätsänderungen.
    • Modul-Strukturierer: Erzeugt eine Moduldatei, die drei Schichten sowie DI-Tokens, die bereits vorkonfiguriert sind.
    • Prüfung von Pull-Requests: Überprüft den Diff auf N+1-Anfragen, Berechtigungsverwendungen, Schichtverstöße sowie fehlende Tests.
    • Testgenerator: Erstellt *.spec.ts-Dateien, die den Konventionen für Mocking und Fixtures von Services folgen.
    • Zwischen-Service-Adapter: Fügt einen IMS-Adapter-Methoden sowie deren Antwortinterface und Transformationshilfe hinzu.
    • Dokumentation von Anwendungsfällen: Wandelt Anforderungen in Nutzergeschichten mit Akzeptanzkriterien im Stil von Given/When/Then um.

    Schritt 5: Berechtigungen konfigurieren

    .claude/settings.json bestimmt, welche Shell-Befehle der Assistent ohne vorherige Anfrage ausführen darf. Eine angemessene Grundlage genehmigt standardmäßig Linting, Tests und Builds und verwehrt ausdrücklich Befehle, die Arbeit zerstören oder veröffentlichen:

    {
      "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 *)"]
      }
    }
    

    Die Einstellungen sind in zwei Ebenen unterteilt:

    • .claude/settings.json gilt für das gesamte Team und wird in Git gespeichert.
    • .claude/settings.local.json enthält persönliche Anpassungen, gilt nur für Sie und sollte mit gitignored markiert werden.

    Die Verwehrliste dient als Sicherheitsnetz, nicht als Sandbox. Die Muster passen auf Befehlszeichenfolgen, daher sollten sie eher als Schutz vor Unfällen denn als Sicherheitsgrenze betrachtet werden.

    Praktiken, die eine gesunde Einrichtung gewährleisten

    Behandeln Sie .claude/ wie Produktionskodung: Er verteilt Ihre Konventionen an jeden Entwickler und bei jeder Sitzung, weshalb er einer Überprüfung, Historisierung und Pflege bedarf.

    • Halten Sie CLAUDE.md kurz – maximal etwa 300 Zeilen. Alles darin konkurriert um den Kontext mit dem Code, den der Assistent lesen muss. Wenn eine Information aus dem Code abgeleitet werden kann, lassen Sie sie weg.
    • Fassen Sie Regeln als Verbote zusammen. In der Praxis hält sich „Fragen Sie niemals innerhalb einer Schleife zur Datenbank“ unter Druck viel besser als „Wählen Sie lieber Batch-Anfragen“.
    • Halten Sie Vorlagen in den Fähigkeiten. Sie sollten nur geladen werden, wenn die jeweilige Fähigkeit ausgeführt wird, und nicht bei jeder Sitzung.
    • Versionieren Sie .claude/ in git. Neue Regeln werden über Pull Requests eingefügt; veraltete werden gelöscht.
    • Verwenden Sie Entscheidungstabellen, wo immer zwei Optionen möglich sind, wie zum Beispiel bei der Platzierung von DTOs, dem Standort von Entitäten oder den Grenzen von Services. In Markdown-Dateien für den Assistenten ist eine Tabelle klarer als Absätze.

    Ein vollständiges Layout für die in diesem Leitfaden beschriebene Einrichtung:

    .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
    

    Fehler, die die Ausgabe heimlich verschlechtern

    Jeder dieser Fehler erzeugt Code, der auf den ersten Blick in Ordnung erscheint, aber Ihre Konventionen verletzt – genau das macht sie kostspielig.

    1. CLAUDE.md als NestJS-Tutorial zu schreiben. Das Modell kennt bereits das Framework. Dokumentieren Sie nur das, was für Sie spezifisch ist: Modulnamen, Fehlerkonstanten, die Antwortstruktur sowie das Berechtigungssystem.
    2. Negative Regeln wegzulassen. Die kostspieligen Fehler sind jene, die korrekt aussehen; indem Sie dem Assistenten mitteilen, was er nicht tun soll, eliminieren Sie ganze Kategorien solcher Fehler.
    3. Alles im Root-File zu platzieren. Das Root-File wird bei jeder Sitzung geladen, wodurch service-spezifische Strukturen den Kontext verfälschen, wenn Sie an anderer Stelle arbeiten.
  • Auslass der Routing-Tabelle. Die falsche Zuordnung von Diensten resultiert aus Unklarheiten und nicht aus Unwissenheit, wobei eine Tabelle die Entscheidung deterministisch macht.
  • Nicht-Dokumentation des Adapter-Musters. Bei über HTTP verbundenen Diensten versucht der Assistent andernfalls, Importe zwischen Diensten durchzuführen oder Abfragen an die Datenbank eines anderen Dienstes zu senden.
  • Nicht-Erwähnung von synchronize: false. Das Modell geht davon aus, dass TypeORM das Schema verwaltet. Bei Verwendung von Flyway scheitert eine Änderung der Entität ohne Migrationsdatei stillschweigend – daher sollte dies ausdrücklich angegeben werden.
  • Durchgang: eine Anfrage, vor und nach

    Nehmen wir an, ein Produktmanager bittet darum, Buchungen so darzustellen, dass Verbraucher den Status auf ihrer Paketübersicht sehen können.

    In einem unkonfigurierten Repository geht der Assistent davon aus, dass die Pakete in operator-service definiert sind, und bearbeitet dort die Paketentität – was der falsche Service ist. Er kodiert den Statusstring fest und schützt die Änderung mit if (user.roles.includes('admin')).

    Mit der Konfiguration aus diesem Leitfaden wird die Anfrage schrittweise gelöst:

    1. Die Routing-Tabelle klassifiziert einen vom Verbraucher sichtbaren Status als Teil der Interaktion mit dem Verbraucher, wodurch die Bearbeitung in api-service stattfinden sollte.
    2. Die cross-service-Regel erinnert daran, dass die Daten der Operator-Pakete über einen IMS-Adapter gelesen werden, weshalb das Feld in die Transformation des Adapters hinzugefügt wird anstelle, in eine andere Datenbank zuzugreifen.
    3. Durch Ausführung von /api-development werden die vier Schichten nacheinander erzeugt, beginnend mit dem DTO und endend mit dem Controller.
  • Die Berechtigungsregel erzeugt @Permissions([PermissionCodes.VIEW_BOOKING_STATUS]) anstelle einer Rollenprüfung.
  • Die Fehlerbehandlungsregel erzeugt eine NotFoundException, die aus einer ErrorMessages-Konstante gebildet wird.
  • Eine einzige vage Aussage führt zu konventionskonformen Änderungen in vier Dateien, ohne dass klärende Fragen notwendig wären.

    Was sich täglich ändert

    Die Einrichtung dauert einmal einige Stunden. Die Änderungen, die sie bewirken soll:

    • Der Fokus der Überprüfung verschiebt sich. Anstatt jede generierte Datei auf hartecodierte Zeichenfolgen, falsche Importe und Rollenprüfungen zu überprüfen, richten die Prüfer ihre Aufmerksamkeit auf die Logik.
    • Die Endpunkte werden schneller. Das Manuell-Verschalten von vier Schichten dauerte in der beschriebenen Einrichtung etwa zwanzig Minuten; durch die entsprechenden Fähigkeiten lässt sich dies auf unter drei Minuten reduzieren.
  • Die Platzierung ist bereits beim ersten Versuch korrekt. Die Routing-Tabelle platziert die Dateien im richtigen Service, anstatt dass Fehler erst spät entdeckt werden.
  • Häufige Fehler werden bereits an der Quelle vermieden. N+1-Anfragen sowie ungeplante console.log-Aufrufe werden während der Erstellung verhindert, anstatt erst in Pull Requests aufzutauchen.
  • Die Einarbeitung wird verbessert. Neue Entwickler müssen nicht mehr raten, wo die Module enden, da die Konventionen schriftlich festgelegt sind und auf ihre ersten von KI unterstützten Funktionen angewandt werden.
  • Einige Vorteile zeigen sich erst nach Monaten. Code, der in großem Abstand erzeugt wird, wirkt konsistent, weil er aus denselben Vorlagen und Regeln stammt. Reviewer schreiben nicht mehr immer die gleichen Kommentare. Routing-Tabellen und -Regeln dienen als tatsächlich genutzte Dokumentation. Zudem sorgt ein übersichtliches CLAUDE.md zusammen mit auf Anfrage verfügbaren Fähigkeiten dafür, dass die Ressourcen für das Verständnis des Codes statt für das Erkennen von Konventionen genutzt werden.

    Häufige Fragen

    Wohin gehören die Dateien in einem Monorepo? Legen Sie das gemeinsame CLAUDE.md in .claude/ am Root des Git-Verzeichnisses für gemeinsame Anforderungen und ein CLAUDE.md innerhalb jeder Service-Datei für deren spezifische Anforderungen. Die Root-Datei wird immer geladen; die Service-Datei wird erst dann verwendet, wenn Sie in diesem Service arbeiten.

    Inwiefern unterscheidet sich CLAUDE.md von den Regeln? CLAUDE.md erklärt, wie das System funktioniert; die Regeln legen fest, was auf keinen Fall vorkommen darf, wie beispielsweise Abfragen in Schleifen oder Überprüfungen von Rollennamen.

    Wie verhalten sich die Fähigkeiten? Eine Fähigkeit wie /api-development wird durch ein SKILL.md-Workflow sowie Vorlagen und Referenzen unterstützt. Durch deren Aufruf befolgt der Assistent ein festgelegtes, mehrstufiges Verfahren anstelle einer Improvisation.

    Sollte .claude/ in Git gespeichert werden? Ja: CLAUDE.md, rules/, skills/ und settings.json gehören alle in Git, damit das Team eine einzige Konfiguration teilen kann. Persönliche Anpassungen werden in settings.local.json, das in gitignored liegt, gespeichert.

    Hauptergebnisse

    • In einem NestJS-Monorepo hängt die Richtigkeit von Konventionen ab, die in keinem einzelnen File dargestellt werden, weshalb ein Assistent ohne Kontext nur raten kann.
    • Teilen Sie dieses Wissen nach Typen auf: CLAUDE.md für die Funktionsweise, Regeln für das Verbotene, Anleitungen für wiederholbare Vorgänge sowie Einstellungen für Prozesse, die unüberwacht laufen dürfen.
    • Die Entscheidungstabelle zur Service-Zuordnung ist das am meisten einflussreiche Element; schreiben Sie sie zuerst.
    • Dokumentieren Sie bewusste Entfernungen sowie nicht-standardmäßige Einstellungen wie synchronize: false, denn genau das würde ein Modell sonst „korrigieren“.
    • Regeln leiten die Erstellung stark steuernd, sind aber keine Durchsetzungsmechanismen; nutzen Sie Lint-Tools, Tests und CI für Invarianten, die Sie sich nicht leisten können, zu brechen.
    • Die Anstrengung ist eine einmalige Investition von einigen Stunden, die sich bei jeder Funktion und jedem neuen Mitarbeiter auszahlt, solange die Files wie Code überprüft und optimiert werden.

    Verwandte Artikel

  • Vom Prose Rules zum Mechanical Gates: Die Absicherung einer Claude Code Agent Squad — Wie ein mehragentenbasierter Claude Code-Plugin ignorierte Persona-Anweisungen schrittweise durch Skripte, Hooks und gehäschte Beweise ersetzt hat, Version für Version, sowie was man kopieren kann.
  • Where Claude Code Instructions Belong: CLAUDE.md, Path Rules or Hooks — Learn why Claude Code treats CLAUDE.md as context, how to trim it, scope rules by path, move must-run steps into hooks, and verify what actually loaded.
  • NestJS-Injektionstoken: Warum useClass Singletone stillschweigend duplizieren kann — Verstehen Sie Typen, Tokens und Anbieter in NestJS DI, warum Interfaces nicht injiziert werden können, und wie useExisting das Erstellen von zwei Instanzen desselben zustandsbehafteten Services vermeidet.
  • Laravel oder NestJS? Abwägung von Architektur, Geschwindigkeit und Teamzusammenpassung — Ein praktischer Vergleich von Laravel und NestJS, der Architektur, ORMs, Standard-Sicherheitsmaßnahmen, Laufzeit- und Ladezeiten, die Lernkurve sowie die Anwendungsfälle beider Frameworks behandelt.