Nauczanie Claude Code Twojego monorepo w NestJS: routowanie, zasady i umiejętności
Naucz Claude Code monorepo w NestJS: plik CLAUDE.md, zasady, umiejętności oraz uprawnienia, które umieszczają kod we właściwym serwisie i zapewniają przestrzeganie konwencji zespołu.
Źródła: Claude Code for NestJS Monorepos: A Practical Setup Guide
Jeśli zadasz niekonfigurowanemu asystentowi AI prośbę typu „dodaj status widoczny dla klienta do rezerwacji” w dużym monorepo NestJS, wynik będzie przewidywalny: zmiana trafi do niewłaściwego mikrosłużby, komunikat o błędzie to sztywno zdefiniowana ciąg znaków, a dostęp jest kontrolowany przez user.role === 'admin'. Model nie zawodzi z powodu TypeScript – on jedynie zgaduje decyzje podjęte dawno temu przez twoją zespół, który nigdy ich nie zapisał w miejscu, gdzie model mógłby je odczytać. Ten przewodnik pokazuje, jak zastąpić to zgadywanie kontekstem kontrolowanym wersją, wykorzystując cztery elementy budulcowe oferowane przez Claude Code: CLAUDE.md, reguły, umiejętności oraz ustawienia uprawnień. Na końcu będziesz miał strukturę, którą możesz zapisać w swoim repozytorium, dzięki czemu każdy inżynier oraz każda sesja AI będzie korzystać z tych samych konwencji.
Jeśli szukasz ogólnych wskazówek dotyczących tworzenia samego pliku z informacjami, zapoznaj się z naszym przewodnikiem dotyczącym skutecznego CLAUDE.md. Ten artykuł koncentruje się na tym, co zmienia się, gdy bazą kodu jest wielosłużbowy backend NestJS.
Wersja skrócona
Dla czytelników, którzy chcą najpierw listę kontrolną, a dopiero potem wyjaśnienia:
- Zainstaluj Claude Code (na przykład za pomocą
npm install -g @anthropic-ai/claude-code) i zaloguj się raz, uruchamiającclaude. - Stwórz plik
.claude/CLAUDE.mdw katalogu głównym, który wyjaśnia, która usługa zarządza danymi, jaki jest kolejny krok w procesie obsługi żądania oraz jaką informację zwraca każdy punkt końcowy. - Dodaj katalog
.claude/rules/z zasadami zakazującymi: brak zapytań w pętlach, brak sprawdzania nazw ról oraz brak importów względnych.
.claude/skills/ zawierający szczegółowe procedury i szablony kodu dla zadań, które wykonywane są wielokrotnie, takich jak tworzenie punktów końcowych..claude/settings.json, aby z góry zatwierdzić bezpieczne polecenia i zablokować te destruktywne..claude/, aby konfiguracja mogła być udostępniana i sprawdzana podobnie jak kod.Dlaczego monorepo w NestJS myli asystentów AI
W dużym kodzie NestJS poprawność kodu często zależy od konwencji, których nie ujawnia żaden pojedynczy plik. Dziesiątki modułów, setki DTO-ów, niestandardowe mechanizmy ochronne, intercepcje, filtry wyjątków oraz adaptery HTTP tworzą prawdziwą niejednoznaczność. Gdy nic nie rozwiązuje tej niejednoznaczności, model ucieka się do sensownych domyślnych wartości, a te rzadko odpowiadają specyfice danej grupy.
Typowe objawy niekonfigurowanego setupu:
- Metoda usługi komunikuje się bezpośrednio z bazą danych, zamiast korzystać z warstwy repositoria.
- Wiadomości o błędach to literale ciągu znaków, a nie wpisy z pliku konstant
ErrorMessages. - W folderze
operator-servicetworzony jest nowy plik, mimo że logika rezerwacji należy doapi-service. - Uprawnienia są sprawdzane za pomocą
user.role === 'admin', zamiast wykorzystaniaPermissionCodes. - Dane debugowe są wyświetlane za pomocą
console.log, zamiast strukturyzowanego narzędzia do logowania. - Importy odbywają się poprzez ścieżki typu
../../common/utils, zamiast użycia aliasu ścieżkisrc/.
Główną przyczyną jest brak wiedzy specyficznej dla zespołu
Żaden z tych problemów nie jest błędem modelu. Podczas sesji asystent zna tylko pliki, które otworzył. Nie ma możliwości dowiedzenia się, że zespół celowo usunął RolesGuard, że opcja synchronize w TypeORM musi pozostać na wartości false, lub że usługi komunikują się ze sobą wyłącznie za pośrednictwem adapterów HTTP. Ta wiedza znajduje się w umysłach inżynierów oraz w starych tematach pull requestów.
Rozwiązaniem jest zapisanie tej wiedzy w plikach, które ładuje narzędzie, oraz dopasowanie każdego rodzaju wiedzy do odpowiedniego mechanizmu:
CLAUDE.mdzawiera informacje o architekturze, odpowiedzialności za usługi oraz konwencjach. Jest on automatycznie ładowany na początku każdej sesji.- Zasady określają zakazy i niezmienniki, które powinny kształtować każdy fragment generowanego kodu.
Struktura repozytorium przyjęta w tym przewodniku
Pomaga ustalić konkretną strukturę przed napisaniem jakiejkolwiek konfiguracji. Przykładowy backend składa się z czterech usług oraz pojedynczej ścieżki .claude/ w korzeniu repozytorium:
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
Każda usługa to samodzielna aplikacja z własnym plikiem package.json, tsconfig.json oraz wynikami kompilacji. Należy zauważyć, że jedna z nich, chat-service, jest zwykłym JavaScriptem działającym na Express i Socket.IO, natomiast job-service w ogóle nie posiada serwera HTTP. Ta heterogeniczność jest dokładnie tym rodzajem informacji, której asystent nie może wywnioskować z pojedynczego pliku.
W każdej usłudze NestJS moduły funkcjonalne mają tę samą trójwarstwową 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
Kontrolery kierują żądaniami HTTP i nic więcej, usługi zawierają logikę biznesową oraz mechanizmy orkiestracji, a repozytoria zawierają zapytania TypeORM. Jeśli asystent nigdy nie został poinformowany o takim podziale, chętnie go zniekształci: usługa wyśle zapytania lub kontroler bezpośrednio wezwie repozytorium. Dokumentowanie warstw pozwala zachować te granice.
Krok 1: instalacja i uwierzytelnianie
Instalacja polega na dodaniu jednego globalnego pakietu, a następnie na jednorazowym logowaniu się w przeglądarce:
npm install -g @anthropic-ai/claude-code
claude # opens a browser for one-time OAuth
Po uwierzytelnieniu claude można uruchomić z dowolnego katalogu. Każdy programista instaluje go i loguje się na swoim komputerze; nie ma żadnego wspólnego klucza zespołowego do rozpowszechniania. Opcje instalacji zmieniają się z czasem, więc sprawdź aktualną dokumentację Claude Code, jeśli ścieżka npm nie jest już zalecana.
Jeden szczegół ma kluczowe znaczenie szczególnie w monorepo. Konfiguracja projektu jest pobierana względem korzenia repozytorium, więc instrukcje znajdujące się w katalogu .claude/ obowiązują wszędzie tam, gdzie uruchamiasz sesję. Gdy twoja praca odbywa się wyłącznie w jednej usłudze, uruchom Claude z wnętrza tego katalogu usług, np. services/api-service/, aby wyszukiwania i polecenia działały z odniesieniem do odpowiedniego pakietu.
Krok 2: napisz dokumentację CLAUDE.md
CLAUDE.md to plik o największym wpływie, ponieważ jest to kontekst, który asystent czyta przed wszystkim innym. Napisz go w taki sposób, jakbyś informował doświadczonego inżyniera podczas jego pierwszego dnia pracy: zakładaj ogólną kompetencję i wyjaśnij tylko to, co jest specyficzne dla twojego systemu. W monorepo sensowne jest podzielenie na dwa poziomy:
- Korzeniowy plik
.claude/CLAUDE.mddotyczący odpowiedzialności za usługę, wspólnych konwencji oraz procesu obsługi zapytań.
services/{service}/CLAUDE.md dla każdej usługi, zawierający szczegóły dotyczące wzorów modułowych, autoryzacji oraz iniekcji zależności, które mają zastosowanie wyłącznie tam.Plik główny: spraw, by odpowiedzialność za usługę była decyzją, a nie domysłem
Najdroższy błąd w repozytorium z wieloma usługami to umieszczanie kodu w niewłaściwym miejscu – przeszkażę lokalną weryfikację i pojawi się dopiero później jako naruszenie granic. Plik główny zapobiega temu dzięki tabeli routingu oraz wyraźnemu wykazowi zabronionych wzorów. Oto przykład tego, co może zawierać taki plik:
## 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
Kilka części tego pliku wymaga uwagi. Sekcja dotycząca własności jest sformułowana jako test („Czy dane opisują to, czym jest doświadczenie, czy interakcja konsumenta z nim?”) zamiast jako lista katalogów, co umożliwia asystentowi klasyfikację zapytań, których nigdy wcześniej nie widział. Sekcja dotycząca procesu przetwarzania wymienia middleware, rury przepływu danych, interceptyory i filtry w kolejności wykonywania, dzięki czemu generowany kod nie powtarza tego, co już robi TransformInterceptor. Sekcja dotycząca usług międzyserwisowych jasno stwierdza, że api-service nigdy nie ma dostępu do bazy danych operatora i musi korzystać z adapterów znajdujących się w katalogu src/common/adapters/.
To, co odróżnia przydatny plik CLAUDE.md od słabego:
- Zasady decyzyjne są lepsze od drzew plików. Zasada taka jak „dane opisujące, czym jest dana funkcjonalność, powinny znajdować się w operator-service” rozwiązuje niejednoznaczności bez konieczności zadawania pytań wyjaśniających.
- Określ strukturę odpowiedzi. Bez tego każdy kontroler pakuje swoje własne odpowiedzi, co powoduje chaos w ich organizacji.
- Wyjaśnij dokładnie, co jest zabronione. „Nigdy nie używaj
console.log” jest łatwiejsze do wdrożenia niż „Używaj naszego narzędzia do logowania”. - Zapisuj celowe usunięcia. Jeśli usunąłeś
RolesGuardlub wyłączyłeśsynchronize, napisz o tym i podaj powód. W przeciwnym razie asystent może je ponownie wprowadzić, właśnie dlatego, że wyglądają jak standardowe rozwiązanie w NestJS.
Pliki na poziomie usług: głębokość potrzebna tylko jednej usłudze
Detalie, które mają znaczenie tylko w obrębie jednej usługi, trafiają do jej własnego pliku. W przypadku api-service chodzi tu o warstwowanie, autoryzację, tokeny DI oraz zadania w tle:
## 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.
Dwa aspekty mogą być łatwo błędnie zrozumiane bez odpowiednich wskazówek. Po pierwsze, dostawcy są rejestrowani i injectowani za pomocą tokenów interfejsu, takich jak SERVICE_INTERFACE.BOOKING_SERVICE, a nie według klasy, więc generowany konstruktor, który bezpośrednio injectuje BookingService, mimo że się skompiluje, naruszy ustalone wzorce projektowe. Po drugie, zadania BullMQ są umieszczane w kolejce wyłącznie z usługi modułu, nigdy z kontrolera, a procesory znajdują się w katalogu src/modules/{module}/processors/. Unikanie umieszczania tych informacji w pliku głównym oznacza, że nie wpływają one na kontekst podczas pracy w chat-service.
Krok 3: dodanie zasad dotyczących sytuacji, które nigdy nie mogą się zdarzyć
CLAUDE.md opisuje, jak działa system, natomiast reguły określają, czego nie może zawierać kod wygenerowany. Przydatnym modelem mentalnym jest konfiguracja ESLint przeznaczona do obsługi wyników generowanych przez AI. Reguły to pliki Markdown znajdujące się w katalogu .claude/rules/.
Prosty sposób na decydowanie, gdzie co powinno się znaleźć:
- Architektura, struktura modułów, odpowiedzialność za usługi, konwencje oraz odniesienia do poleceń powinny być umieszczone w
CLAUDE.md. - Bezwzględne zakazy, wzorce, które nigdy nie mogą zostać wygenerowane, oraz aspekty bezpieczeństwa lub wydajności należą do katalogu
rules/.
Pamiętaj, że reguły to instrukcje ładowane do kontekstu modelu, a nie narzędzie do analizy statycznej. Sprawiają one, że naruszenia stają się znacznie mniej prawdopodobne, ale w przypadku krytycznych zasad nadal warto je wspierać regułami lintingu, testami lub kontrolami CI.
Reguła: brak zapytań typu N+1
Zapytania typu N+1 są łatwe do napisania, ale też łatwo o nich zapomnieć podczas przeglądania, ponieważ każda pojedyncza linia wygląda niewinnie. Zasada zawarta w pliku .claude/rules/database-queries.md działa najlepiej, gdy pokazuje zarówno wzorzec zabroniony, jak i jego zamiennik. Wersja zabroniona generuje jedno zapytanie na każdą rezerwację:
// Wrong — N queries
for (const b of bookings) {
b.user = await this.userRepo.findOne(b.userId);
}
Zamiennik ładuje wszystkich użytkowników za pomocą jednego zapytania przy użyciu operatora In z TypeORM, a następnie przechowuje ich w pamięci za pomocą struktury Map opartej na identyfikatorze 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);
});
Zaletą jest to, że zapytanie masowe jest tworzone w momencie generowania, dzięki czemu błąd wydajnościowy nigdy nie trafia do pull requesta. Pamiętaj, aby najpierw zebrać wartości userIds z rezerwacji i rozważyć ich usunięcie w przypadku, gdy wiele rezerwacji dotyczy tego samego użytkownika.
Zasada: uprawnienia, nigdy nazwy ról
Druga zasada, zawarta w pliku .claude/rules/permissions.md, zabrania takim ad hoc sprawdzaniom ról:
// Wrong
if (user.roles.includes('admin')) { ... }
Zamiast tego punkty końcowe deklarują wymagane uprawnienia i pozwalają mechanizmom kontrolnym je egzekwować:
// Correct
@Permissions([PermissionCodes.MANAGE_BOOKINGS])
@UseGuards(JwtAuthGuard, PermissionsGuard)
Uzasadnienie powinno znajdować się również w pliku z regułami, ponieważ pomaga modelowi generalizować: role są łączone, dzielone i przemieniane w miarę rozwoju produktu, a każda kontrola oparta na nazwie role milcząco przestaje działać w takich sytuacjach. Kody uprawnień to stabilne stałe, które przetrwają takie reorganizacje. Reguła powinna również wskazać, gdzie one się znajdują – w tym przypadku w src/common/constants/permissions.constant.ts.
Reguła: aliasy ścieżek zamiast importów względnych
Trzecia reguła, .claude/rules/imports.md, standaryzuje importy:
// Wrong
import { UserService } from "../../services/user.service";
// Correct
import { UserService } from "src/modules/user/services/user.service";
Alias działa, ponieważ plik tsconfig.json ustawia baseUrl na "./", a funkcja moduleNameMapper w Jest mapuje ten sam prefiks. Ważne jest wspomnienie o obu elementach: gdyby asystent znał tylko aspekty związane z TypeScriptem, mógłby uznać, że alias jest niebezpieczny w testach.
Kolejne zasady, które warto opisać
To samo podejście „zabraniać i pokazywać alternatywę” nadaje się do rozwiązania wielu powtarzających się problemów w NestJS. Przykłady dla własnych plików:
- Rozwiązywanie błędów: rzucaj wyjątki typu NestJS utworzone na podstawie stałych
ErrorMessageslubResponseCodeKeys; nigdy nie wpisywaj treści błędów bezpośrednio w kodzie ani nie zwracaj surowych obiektów błędów. - Logowanie: używaj strukturyzowanego narzędzia
logMessage()z odpowiednim poziomem;console.logjest niedozwolone.
EntityManager, dzięki czemu awaria nie może spowodować częściowego zapisania danych.class-validator. Ponieważ ValidationPipe działa w trybie białej listy, właściwości bez dekoratorów są cicho pomijane, co wygląda jak utrata danych, a nie błąd.synchronize pozostaje na poziomie false.@ApiSpec oraz typowanym DTO odpowiedzi, dzięki czemu generowana dokumentacja API pozostaje dokładna.Krok 4: przekształcanie powtarzających się procesów w umiejętności
Umiejętności to specyficzne dla projektu polecenia typu slash, które łączą w sobie procedury, szablony oraz materiały referencyjne. Są one odpowiednim narzędziem do wykonywania często powtarzanych zadań wymagających wielu plików: dodawania punktu końcowego, pisania migracji, tworzenia szkieletu modułu czy przeglądania pull requestów. W odróżnieniu od CLAUDE.md zawartość umiejętności jest ładowana tylko wtedy, gdy jest ona używana, dzięki czemu szczegółowe szablony nie obciążają pamięci w każdej sesji.
Anatomia katalogu umiejętności
Umiejętność służąca do tworzenia punktów końcowych może być zorganizowana w ten sposób: proces pracy opisany jest w pliku SKILL.md, szablony znajdują się w katalogu assets/, a materiały referencyjne – w katalogu 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
Proces pracy opisany w SKILL.md
Plik zaczyna się od prostego tytułu:
API Development Skill
Następnie w jego treści określono, kiedy należy wywołać daną umiejętność, oraz przedstawiono kroki w ustalonej kolejności:
## 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.
Kolejność ta jest celowa. Sprawdzenie tabeli routingu najpierw zapobiega przekierowaniu umiejętności do niewłaściwej usługi. Czytanie istniejących entytetów, repositoriów i DTO-ów unika ich duplikowania. Generowanie DTO przed czymkolwiek innym ustala kontrakt, a każda kolejna warstwa jest budowana na jego podstawie: repositoryj akceptuje opcjonalny EntityManager, aby móc łączyć transakcje, usługa jest iniektowana za pomocą tokenu interfejsu i rzuca standardowe wyjątki z użyciem stałych ErrorMessages, natomiast kontroler jedynie dokumentuje, deleguje i zwraca { data, meta }. Ostatni krok łączy dostawców z modułem i eksportuje je za pomocą tokena.
Jedna praktyczna uwaga: aktualne wersje Claude Code odkrywają umiejętności za pomocą danych YAML znajdujących się na początku pliku SKILL.md (pole name oraz description, które określają, kiedy należy je użyć). Powyższy przykład ich pomija, więc sprawdź aktualną dokumentację pod kątem dokładnego formatu przy tworzeniu własnych plików.
Środki powinny być prawdziwym kodem
Środki w katalogu assets/ są przydatne tylko wtedy, gdy dają się skompilować. Pseudokod zachęca model do uzupełniania braków własnymi standardowymi wartościami. Szablon kontrolera zaczyna się od deklaracji klasy oraz iniekcji tokenów:
@ApiTags("bookings")
@Controller("bookings")
export class BookingController {
constructor(
@Inject(SERVICE_INTERFACE.BOOKING_SERVICE)
private readonly bookingService: IBookingService,
) {}
Dalej znajduje się kompletny punkt końcowy, który jednocześnie pokazuje kolejność zabezpieczeń, dekorator uprawnień, dekoratory Swagger oraz strukturę zwracanych danych:
@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 };
}
}
Zwróć uwagę na to, czego uczy ten pojedynczy przykład: JwtAuthGuard jest wykonywany przed PermissionsGuard, identyfikator użytkownika pochodzi z typowanej struktury UserRequest, kontroler nie zawiera logiki biznesowej, a funkcja zwraca { data }, aby mógł ją otoczyć TransformInterceptor. Dzięki takiemu podejściu opisany scenariusz pozwala na stworzenie kompletnego punktu końcowego (kontroler, usługa, repozytorium oraz DTO-y połączone z modułem) w mniej niż trzy minuty, zamiast około dwudziestu, jak to miało miejsce przy pierwszej próbie w zespole. Twoje wyniki będą zależeć od rozmiaru szablonów oraz od tego, ile jeszcze przeglądów wykonasz.
Inne umiejętności do rozważenia
Każdy powtarzający się proces pracy, który obejmuje specyficzne dla zespołu konwencje, jest dobrym kandydatem:
- Generator migracji: tworzy migrację Flyway w odpowiedniej folderze schematu, korzystając z Twojego schematu numeracji i nazywania wraz z odpowiadającą zmianą w entytecie.
- Budowniczy modułu: generuje plik modułu, trzy warstwy oraz tokeny DI, już połączone ze sobą.
- Przegląd żądania pull: sprawdza różnice w kodzie pod kątem zapytań typu N+1, wykorzystania uprawnień, naruszeń zasad warstw oraz braku testów.
- Generator testów: tworzy pliki
*.spec.ts, które przestrzegają konwencji mockowania i przygotowywania danych pomocniczych dla usług. - Adapter międzyusługowy: dodaje metodę adaptera IMS wraz z interfejsem odpowiedzi i pomocnikiem do transformacji.
- Dokumentacja przypadków użycia: przekształca wymagania w historie użytkownika z kryteriami akceptacji typu Given/When/Then.
Krok 5: konfiguracja uprawnień
.claude/settings.json określa, jakie polecenia wiersza poleceń asystent może wykonać bez uprzedniego pytania. Rozsądna podstawa pozwala z góry na linting, testy i budowanie, a wyraźnie blokuje polecenia, które niszczą prace lub je publikują:
{
"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 *)"]
}
}
Ustawienia dzielą się na dwa poziomy:
.claude/settings.jsondotyczy całego zespołu i jest komitowany..claude/settings.local.jsonzawiera osobiste modyfikacje, dotyczy tylko ciebie i powinien być oznaczony jako gitignored.
Lista blokad to zabezpieczenie awaryjne, a nie sandbox. Wzory odpowiadają ciągom poleceń, więc należy je traktować jako środek zapobiegający wypadkom, a nie jako granicę bezpieczeństwa.
Praktyki utrzymujące zdrowy stan konfiguracji
Traktuj .claude/ jak kod produkcyjny: rozpowszechnia twoje konwencje wśród wszystkich inżynierów i na każdej sesji, więc zasługuje na przegląd, historię i regularną oczyszczanie.
- Zachowuj krótkość pliku
CLAUDE.md, maksymalnie około 300 wierszy. Wszystko, co się w nim znajduje, konkuruje o przestrzeń roboczą z kodem, który asystent musi przeczytać. Jeśli jakaś informacja można wywnioskować z kodu, lepiej jej pominąć. - Sformułuj zasady jako zakazy. W praktyce „nigdy nie wykonywaj zapytań do bazy danych wewnątrz pętli” znacznie lepiej sprawdza się pod presją niż „wolimy zapytania masowe”.
- Zachowuj szablony w kompetencjach. Powinny się ładować tylko wtedy, gdy uruchamiana jest dana kompetencja, a nie przy każdej sesji.
- Zachowuj wersję
.claude/w git. Nowe zasady trafiają poprzez pull requests, a te przestarzałe są usuwane. - Używaj tabel decyzyjnych tam, gdzie istnieją dwie możliwe opcje, np. przy umieszczaniu DTO, lokalizacji entity lub granicach usług. W plikach Markdown przeznaczonych dla asystenta tabela jest bardziej zrozumiała niż akapity.
.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
Błędy, które po cichu pogarszają jakość wyników
Każdy z nich generuje kod, który wygląda poprawnie, ale narusza ustalone zasady, co sprawia, że są one kosztowne.
- Zapisywanie pliku
CLAUDE.mdjako tutorialu do NestJS. Model już zna tę ramę programistyczną. Dokumentuj tylko to, co jest specyficzne dla Ciebie: nazwy modułów, stałe błędów, strukturę odpowiedzi, system uprawnień. - Pomijanie negatywnych zasad. Najdroższe błędy to te, które wyglądają poprawnie; podanie asystentowi informacji o tym, czego nie robić, eliminuje całe kategorie takich błędów.
- Umieszczanie wszystkiego w pliku korzeniowym. Plik korzeniowy jest ładowany przy każdej sesji, więc specyficzna dla usług głębokość struktury w tym pliku zanieczyszcza kontekst podczas pracy w innych miejscach.
synchronize: false. Model założy, że to TypeORM zarządza schematem. Gdy odpowiedzialny jest Flyway, zmiana entity bez migracji kończy się bez żadnych komunikatów błędu, dlatego należy to jasno określić.Przebieg: jedna prośba, przed i po
Załóżmy, że menedżer produktu prosi o dostęp do rezerwacji, aby pokazać status, który klienci mogliby zobaczyć na swoim pakiecie.
W niekonfigurowanym repozytorium asystent zakłada, że pakiety są zdefiniowane w operator-service, i edytuje tam entytę pakietu, co dotyczy niewłaściwego usługi. Zapisuje ściśle określoną wartość pola statusu i zabezpiecza tę zmianę za pomocą if (user.roles.includes('admin')).
Dzięki konfiguracji z tego przewodnika żądanie jest realizowane krok po kroku:
- Tabela routingu klasyfikuje status widoczny dla konsumenta jako element interakcji z nim, więc praca powinna odbywać się w
api-service. - Zasada międzyusługowa przypomina o tym, że dane pakietu operatora są czytane za pośrednictwem adaptera IMS, więc pole to jest dodawane do funkcji transformacji adaptera zamiast być pobierane z innej bazy danych.
- Zapewnienie działania
/api-developmentgeneruje cztery warstwy kolejno, począwszy od DTO i kończąc na kontrolerze.
@Permissions([PermissionCodes.VIEW_BOOKING_STATUS]) zamiast sprawdzania ról.NotFoundException na podstawie stałej ErrorMessages.Jedno niejasne zdanie prowadzi do zmian zgodnych z konwencjami we czterech plikach, bez potrzeby zadawania dodatkowych pytań wyjaśniających.
Co zmienia się na co dzień
Konfiguracja zajmuje kilka godzin raz. Zmiany, które ma zapewnić:
- Zmiana punktu skupienia przy przeglądaniu. Zamiast sprawdzania każdego utworzonego pliku pod kątem sztywno zdefiniowanych ciągów znaków, błędnych importów i sprawdzania ról, recenzenci skupiają uwagę na logice.
- Szybsze końce punktów dostępu. Łączenie czterech warstw ręcznie zajmowało około dwudziestu minut w opisanym setupie; dzięki tej umiejętności czas ten spada poniżej trzech minut.
console.log są zapobiegane już podczas generowania, zamiast być znajdowane w pull requestach.Część korzyści pojawia się dopiero po kilku miesiącach. Kod generowany w dużych odstępach czasu wygląda spójnie, ponieważ pochodzi z tych samych szablonów i reguł. Recenzenci przestają pisać te same komentarze. Tabele routingu i reguły pełnią rolę dokumentacji, która jest faktycznie wykorzystywana. A zwięzły plik CLAUDE.md w połączeniu z umiejętnościami dostępnymi na żądanie oznacza, że zasoby przeznaczane są na czytanie kodu, a nie na ponowne odkrywanie konwencji.
Częste pytania
Gdzie umieszcza się pliki w monorepo? Użytkownik powinien umieścić wspólny plik CLAUDE.md w katalogu .claude/ u korzenia repozytorium git, aby zajmować się kwestiami wspólnymi dla wszystkich elementów, a osobny plik CLAUDE.md wewnątrz każdej usługi – dla jej specyficznych potrzeb. Plik z korzenia jest zawsze ładowany; plik usługi zostaje załadowany, gdy pracuje się w danej usłudze.
Jak CLAUDE.md różni się od reguł? CLAUDE.md wyjaśnia, jak działa system; reguły określają, co nie może się pojawić, np. zapytania w pętlach lub sprawdzanie nazw ról.
Jak zachowują się umiejętności? Umiejętność taka jak /api-development opiera się na pliku SKILL.md wraz z szablonami i odnośnikami. Jej wywołanie sprawia, że asystent postępuje zgodnie z ustaloną, wieloetapową procedurą, zamiast improwizować.
Czy .claude/ powinno być zapisywane w git? Tak: CLAUDE.md, rules/, skills/ oraz settings.json powinny znajdować się w git, aby zespół mógł korzystać z jednej konfiguracji. Osobiste modyfikacje trafiają do pliku settings.local.json w katalogu gitignored.
Główne wnioski
- W monorepo NestJS poprawność zależy od konwencji, których nie pokazuje żaden pojedynczy plik, więc asystent bez kontekstu będzie zgadywał.
- Rozdziel te informacje według typu:
CLAUDE.mdopisuje, jak działają poszczególne elementy, zasady określają to, co jest zabronione, umiejętności dotyczą procedur powtarzalnych, a ustawienia to, co może działać bez nadzoru. - Tabela decyzji dotyczących odpowiedzialności za usługi to najważniejszy element; napisz ją pierwszą.
- Dokumentuj celowe usunięcia oraz wybory inne niż domyślne, takie jak
synchronize: false, ponieważ to właśnie model w przeciwnym razie próbowałby „naprawić”. - Zasady silnie kierują procesem generowania, ale nie są narzędziem do egzekwowania zasad; używaj narzędzi do sprawdzania kodu, testów oraz procesów CI dla wartości niezmiennej, których nie możesz sobie pozwolić na naruszenie.
- To inwestycja jednorazowa trwająca kilka godzin, która się zwraca przy każdej nowej funkcji i przy każdym nowym pracowniku, pod warunkiem że pliki są sprawdzane i oczyszczane tak samo jak kod.