Навчання Claude Code вашого Monorepo NestJS: маршрутизація, правила та навички
Навчіть Claude Code роботі з monorepo NestJS: CLAUDE.md, правила, навички та дозволи, які розміщують код у відповідному сервісі та забезпечують дотримання командних стандартів.
Джерела: Claude Code for NestJS Monorepos: A Practical Setup Guide
Якщо надати неналаштованому AI-асистенту запит на кшталт „додати статус, видимий для клієнта, до бронювання“ у великому monorepo NestJS, результат буде передбачуваним: зміни потраплять у неправильний мікросервіс, повідомлення про помилку буде жорстко закодованим рядком, а доступ контролюватиметься за допомогою user.role === 'admin'. Модель не зазнає проблем у TypeScript; вона лише припускає рішення, які ваша команда ухвалила давно та ніде не зафіксувала, де їх можна знайти. Цей посібник показує, як замінити ці припущення на контекст, який контролюється версіями, використовуючи чотири основні елементи, які пропонує Claude Code: CLAUDE.md, правила, навички та налаштування дозволів. До кінця ви отримаєте структуру, яку зможете зберегти у своєму репозиторії, щоб кожен інженер та кожна сесія AI починали роботу з однакових правил.
Якщо ви потребуєте загальних порад щодо створення самого файлу інструкцій, ознайомтесь з нашим посібником щодо створення ефективного CLAUDE.md. Ця стаття присвятована змінам, які виникають, коли базою коду є бекенд NestJS з кількома сервісами.
Коротка версія
Для читачів, які хочуть спочатку переглянути список перевірок, а потім — пояснення:
- Встановіть Claude Code (наприклад, за допомогою
npm install -g @anthropic-ai/claude-code) та автентифікуйтеся один раз, запустившиclaude. - Створіть кореневий файл
.claude/CLAUDE.md, у якому буде пояснено, який сервіс володіє якими даними, послідовність обробки запитів та формат відповідей, яку повертає кожен ендпоїнт. - Додайте каталог
.claude/rules/із правилами заборони: жодних запитів всередині циклів, жодних перевірок за назвами ролей, жодних відносних імпортів.
.claude/skills/, який міститиме поетапні алгоритми роботи та шаблони коду для завдань, які ви виконуєте постійно, наприклад створення кінцевої точки..claude/settings.json, щоб заздалегідь схвалювати безпечні команди та блокувати шкідливі..claude/, щоб конфігурація могла бути поширена та переглянута, як код.Чому монорепозиторій NestJS заплутує штучні інтелект-асистенти
У великій кодовій базі NestJS правильність коду часто залежить від конвенцій, які не відображаються в окремому файлі. Десятки модулів, сотні DTO, власні захисники, інтерцептори, фільтри винятків та адаптери HTTP створюють справжню неоднозначність. Коли ця неоднозначність не усувається, модель вдається до логічних стандартних налаштувань, які рідко підходять конкретній команді.
Типові ознаки неконфігурованої налаштування:
- Метод сервісу взаємодіє безпосередньо з базою даних, а не через шар репозиторію.
- Повідомлення про помилки є рядковими літералами, а не записами з файлу констант
ErrorMessages. - У проекті
operator-serviceстворюється новий файл, хоча логіка бронювання належить доapi-service. - Авторизація реалізована за допомогою
user.role === 'admin', замість використанняPermissionCodes. - Для виведення інформації під час дебагування використовується
console.log, а не структурований логгер. - Імпорти здійснюються через шляхи типу
../../common/utils, замість використання псевдоніма шляхуsrc/.
Основною причиною є відсутність внутрішніх знань команди
Жоден з цих випадків не є помилкою моделі. Під час сеансу асистент знає лише ті файли, які він відкрив. У нього немає можливості дізнатися, що команда навмисно видала RolesGuard, що параметр synchronize у TypeORM має залишатися false, або що сервіси спілкуються між собою виключно через HTTP-адаптери. Ці знання знаходяться у головах інженерів та у старих тредах pull request.
Рішенням є записати ці знання у файли, які завантажує інструмент, та прив’язати кожен тип знань до відповідного механізму:
CLAUDE.mdмістить інформацію про архітектуру, власників сервісів та стандарти. Він автоматично завантажується на початку кожного сеансу.- Правила містять заборони та незмінні умови, які мають впливати на кожен фрагмент генерованого коду.
Структура репозиторію, яку припускає цей посібник
Доцільно визначити конкретну структуру перед тим, як писати будь-яку конфігурацію. У прикладному бекенді є чотири сервіси та один каталог .claude/ у корені репозиторію:
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
Кожен сервіс — це самостійний додаток із власним файлом package.json, tsconfig.json та результатами компіляції. Зауважимо, що один з них, chat-service, — це звичайний JavaScript на базі Express та Socket.IO, а job-service взагалі не має HTTP-сервера. Саме така різноманітність — це те, що асистент не може вивести з одного файлу.
У кожному сервісі NestJS функціональні модулі мають однакову трьохшарову структуру:
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
Контролери обробляють HTTP-запити та нічого більше, сервіси містять бізнес-логіку та механізми координації, а репозиторії — запити TypeORM. Якщо асистента ніколи не інформували про такий розподіл, він може помилково припустити, що запити виконує сервіс або що контролер безпосередньо викликає репозиторій. Саме запис цих шарів дозволяє дотримуватися встановлених меж.
Крок 1: встановлення та автентифікація
Встановлення полягає у завантаженні одного глобального пакета, після чого потрібен одноразовий вхід у браузері:
npm install -g @anthropic-ai/claude-code
claude # opens a browser for one-time OAuth
Після автентифікації claude можна запустити з будь-якої директорії. Кожен розробник встановлює програму та увійшов у своєму комп’ютері; не існує спільного ключа команди для розповсюдження. Варіанти встановлення з часом змінюються, тому перевірте актуальну документацію Claude Code, якщо шлях через npm більше не є рекомендованим.
Одна деталь має особливе значення саме для монорепозиторіїв. Конфігурація проекту визначається відносно кореня репозиторію, тому інструкції, що знаходяться у кореневому каталозі .claude/, застосовуються кожного разу, коли ви починаєте сесію. Якщо ваша робота обмежується однією службою, запускайте Claude зсередини каталогу цієї служби, наприклад services/api-service/, щоб пошуки та команди виконувалися з відповідним пакетом.
Крок 2: написати документ CLAUDE.md
CLAUDE.md — це файл із найбільшим впливом, оскільки саме його контекст асистент читає перед усім іншим. Напишіть його так, ніби ви пояснюєте старшому інженеру все на перший день роботи: припускайте загальну кваліфікацію та пояснюйте лише те, що є специфічним для вашої системи. У монорепозиторії доцільно мати два рівні:
- Кореневий файл
.claude/CLAUDE.md, який містить інформацію про власників служб, спільні конвенції та процес обробки запитів.
services/{service}/CLAUDE.md для кожного сервісу — з деталями щодо шаблонів модулів, автентифікації та вставки залежностей, які стосуються лише цього сервісу.Кореневий файл: перетворіть визначення власника сервісу на обґрунтоване рішення, а не припущення
Найбільш дорогою помилкою у репозиторії з кількома сервісами є розміщення коду не у правильному місці, оскільки він проходить локальну перевірку та лише пізніше виявляється як порушення правил. Кореневий файл запобігає цьому за допомогою таблиці маршрутизації та чіткого списку заборонених шаблонів. Ось приклад того, що може міститися у цьому файлі:
## 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
Кілька частин цього файлу потребують уваги. Розділ про власність сформульований у вигляді тесту („Чи описують дані те, що таке досвід, чи взаємодію споживача з ним?“), а не як список каталогів, що дозволяє асистенту класифікувати запити, яких він раніше не бачив. У розділі про потокову обробку виконання перелічуються мідлвери, трубки, інтерцептори та фільтри у порядку виконання, щоб згенерований код не дублював те, що вже робить TransformInterceptor. У розділі про міжсервісну взаємодію чітко зазначено, що api-service ніколи не торкається бази даних оператора та повинен працювати через адаптери у каталозі src/common/adapters/.
Що відрізняє корисний файл CLAUDE.md від слабкого:
- Правила прийняття рішень кращі за ієрархію файлів. Правило на кшталт „Дані, що описують сутність досвіду, мають знаходитися у розділі operator-service“, усуває неоднозначності без необхідності постановки додаткових запитань.
- Визначте форму відповіді. Без цього кожен контролер обгортає свою відповідь окремо, що призводить до хаосу у структурі даних.
- Чітко вкажіть, що заборонено. Фраза „Ніколи не використовувати
console.log“ легше дотримуватися, ніж „Використовуйте наш логгер“. - Фіксуйте навмисне видалення елементів. Якщо ви видалили
RolesGuardабо вимкнулиsynchronize, зазначте це та поясніть причини. Інакше асистент може їх знову додати, саме тому що вони виглядають як стандартний варіант у NestJS.
Файли на рівні сервісу: глибина, необхідна лише одному сервісу
Деталі, які мають значення лише всередині одного сервісу, потрапляють у власний файл цього сервісу. Для api-service це стосується шарування, автентифікації, токенів DI та фонових завдань:
## 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.
Два моменти тут легко можна сплутати без належних вказівок. По-перше, постачальники реєструються та вставляються через токени інтерфейсу, такі як SERVICE_INTERFACE.BOOKING_SERVICE, а не за класом, тому генерований конструктор, який безпосередньо вставляє BookingService, буде скомпільований, але порушить усталену схему команди. По-друге, завдання BullMQ додаються до черги лише з сервісу модуля, ніколи не з контролера, а обробники знаходяться у директорії src/modules/{module}/processors/. Якщо не включати ці факти до кореневого файлу, вони не будуть впливати на контекст під час роботи з chat-service.
Крок 3: додайте правила для ситуацій, які ніколи не повинні трапитися
У файлі CLAUDE.md описується, як працює система, тоді як правила визначають, чого не повинен містити генерований код. Корисною моделлю для розуміння є конфігурація ESLint, налаштована для вихідних даних ШІ. Правила — це файли Markdown у каталозі .claude/rules/.
Простий спосіб вирішити, куди щось потрапляє:
- Архітектура, структура модулів, власники сервісів, конвенції та посилання на команди мають бути у
CLAUDE.md. - Жорсткі заборони, шаблони, які ніколи не повинні з’являтися, а також інваріанти безпеки чи продуктивності — у каталозі
rules/.
Пам’ятайте, що правила — це інструкції, які завантажуються до контексту моделі, а не інструменти статичного аналізу. Вони значно зменшують ймовірність порушень, але для справді важливих інваріантів слід додатково підкріплювати їх правилами lint, тестами чи перевірками CI.
Правило: заборона на запити типу N+1
N+1 запити легко писати, але їх легко пропустити під час перевірки, оскільки кожен окремий рядок здається невинним. Правило у .claude/rules/database-queries.md працює найкраще, коли воно показує як заборонений шаблон, так і його заміну. У забороненій версії виконується по одному запиту на кожне бронювання:
// Wrong — N queries
for (const b of bookings) {
b.user = await this.userRepo.findOne(b.userId);
}
У заміненій версії всі користувачі завантажуються за допомогою одного запиту з використанням оператора In у TypeORM, а потім зберігаються в пам’яті через Map, ключем до якого є 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);
});
Перевага полягає у тому, що запит для обробки великої кількості даних пишеться під час генерації, тож проблема з продуктивністю ніколи не потрапляє до pull request. Не забудьте спочатку зібрати userIds з бронювань та розглянути можливість їх дедуплікації, якщо багато бронювань належать одному користувачеві.
Правило: дозволи, а не назви ролей
Друге правило, .claude/rules/permissions.md, забороняє такі спонтанні перевірки ролей:
// Wrong
if (user.roles.includes('admin')) { ... }
Натомість кінцеві точки оголошують необхідні дозволи та дозволяють механізмам контролю їх застосовувати:
// Correct
@Permissions([PermissionCodes.MANAGE_BOOKINGS])
@UseGuards(JwtAuthGuard, PermissionsGuard)
Обґрунтування також має бути у файлі правил, адже це допомагає моделі узагальнювати інформацію: з ростом продукту ролі об’єднуються, діляться та перейменовуються, і кожна перевірка, написана за назвою ролі, мовчки припиняє працювати під час таких змін. Коди дозволів є стабільними константами, які залишаються незмінними під час такої реорганізації. У правилах також слід вказати, де вони знаходяться — у цьому випадку src/common/constants/permissions.constant.ts.
Правило: використання псевдонімів шляхів замість відносних імпортів
Третє правило, .claude/rules/imports.md, стандартизує процес імпорту:
// Wrong
import { UserService } from "../../services/user.service";
// Correct
import { UserService } from "src/modules/user/services/user.service";
Еліас працює тому, що у файлі tsconfig.json значення baseUrl встановлено як "./", а функція moduleNameMapper у Jest мапує той самий префікс. Важливо згадувати про обидва аспекти: якби асистент знав лише про сторону TypeScript, він міг би дійти висновку, що еліас є небезпечним у тестах.
Ще правила, які варто сформулювати
Той самий формат „заборона та показ альтернативи“ підходить для багатьох поширених проблем у NestJS. Пропозиції щодо власних файлів:
- Обробка помилок: кидайте винятки NestJS, створені на основі констант
ErrorMessagesчиResponseCodeKeys; ніколи не використовуйте жорстко закодовані повідомлення чи не повертайте сирі об’єкти помилок. - Логування: використовуйте структурований помічник
logMessage()з відповідним рівнем;console.logзаборонений.
EntityManager, щоб у разі збою дані не залишилися наполовину збереженими.class-validator. Оскільки ValidationPipe працює у режимі списку дозволених елементів, властивості без декораторів мовчки ігноруються, що виглядає як втрата даних, а не помилка.synchronize залишається false.@ApiSpec та DTO з типовими даними відповіді, щоб генерована документація API залишалася точною.Крок 4: перетворення повторюваних робочих процесів на навички
Навички — це проєктоспецифічні команди у форматі „слеш“, які об’єднують процедури, шаблони та довідковий матеріал. Вони є ідеальним інструментом для будь-якої багатофайлової задачі, яку ви часто виконуєте: додавання кінцевої точки, написання скриптів міграції, створення структури модуля чи перевірка запиту на з’єднання. На відміну від CLAUDE.md, контент навички завантажується лише тоді, коли вона використовується, тож детальні шаблони не займають багато місця в пам’яті під час кожної сесії.
Структура каталогу навичок
Навичка для створення кінцевої точки може бути організована так: процес роботи описаний у файлі SKILL.md, шаблони знаходяться у каталозі assets/, а довідковий матеріал — у 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
Процес роботи у файлі SKILL.md
Файл починається з простого заголовка:
API Development Skill
Далі у тексті вказується, коли потрібно викликати навичку, та описуються кроки у фіксованому порядку:
## 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.
Порядок виконання дій є навмисним. Спочатку перевірка таблиці маршрутизації запобігає тому, щоб компонент почав працювати з неправильним сервісом. Читання існуючих ентитетів, репозиторіїв та DTO уникає їх дублювання. Створення DTO перед усім іншим вирішує проблеми з контрактом, і кожен наступний шар будується на його основі: репозиторій приймає необов’язковий параметр EntityManager, щоб можна було об’єднувати транзакції, сервіс інжектується через токен інтерфейсу та кидає стандартні винятки з константами ErrorMessages, а контролер лише документує, делегує функції та повертає { data, meta }. Останнім кроком є підключення постачальників до модуля та їх експорт через токен.
Одна практична порада: поточні версії Claude Code виявляють навички за допомогою YAML-фронтматеріалу у верхній частині файлу SKILL.md (з полями name та description, які вказують, коли його використовувати). У наведеному вище прикладі цього немає, тож перевірте актуальну документацію щодо точного формату під час створення власних файлів.
Шаблони мають бути справжнім кодом
Шаблони у каталозі assets/ є корисними лише тоді, коли вони можуть бути скомпільовані. Псевдокод змушує модель заповнювати прогалини своїми стандартними значеннями. Шаблон контролера починається з оголошення класу та вставки параметрів на основі токенів:
@ApiTags("bookings")
@Controller("bookings")
export class BookingController {
constructor(
@Inject(SERVICE_INTERFACE.BOOKING_SERVICE)
private readonly bookingService: IBookingService,
) {}
Далі йде повний ендпоїнт, який одночасно демонструє порядок захисту, декоратори дозволів, декоратори Swagger та формат повернення даних:
@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 };
}
}
Зверніть увагу на те, чого навчає цей єдиний метод: JwtAuthGuard виконується перед PermissionsGuard, ідентифікатор користувача береться з об’єкта типу UserRequest, контролер не містить бізнес-логіки, і він повертає { data }, щоб його обгорнув TransformInterceptor. Завдяки такій навичці сценарій, описаний тут, дозволяє створити повний ендпоінт (контролер, сервіс, репозиторій та DTO-об’єкти, інтегровані в модуль) менш ніж за три хвилини, замість приблизно двадцяти, як це було у команди під час першої спроби. Ваші власні показники залежатимуть від розміру шаблонів та кількості перевірок, які ви ще проводите.
Інші навички, які варто розглянути
Будь-який повторюваний робочий процес, який використовує специфічні для команди конвенції, є гарною кандидатурою:
- Генератор міграцій: створює міграцію Flyway у відповідній папці схеми, використовуючи вашу схему версіонування та найменувань, разом із відповідними змінами ентитетів.
- Скелетоутворювач модулів: генерує файл модуля, три шари та токени DI, які вже підключені між собою.
- Перевірка запиту на підтягування коду: перевіряє відмінності на наявність запитів типу N+1, використання дозволів, порушень правил шарів та відсутності тестів.
- Генератор тестів: створює файли
*.spec.ts, які дотримуються конвенцій мокування та налаштувань сервісів. - Адаптер між сервісами: додає метод адаптера IMS разом із його інтерфейсом відповіді та допоміжним засобом для трансформації даних.
- Документування сценаріїв використання: перетворює вимоги на історії користувача з критеріями прийнятності у форматі Given/When/Then.
Крок 5: налаштування дозволів
.claude/settings.json визначає, які команди оболонки асистент може виконувати без попереднього запиту. Розумний базовий набір правил попередньо схвалює перевірку коду, тести та процеси збірки, а також прямо забороняє команди, які можуть знищити результати роботи чи оприлюднити їх:
{
"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 *)"]
}
}
Налаштування існують у двох рівнях:
.claude/settings.jsonзастосовується до всієї команди та зберігається у версійному керуванні..claude/settings.local.jsonмістить особисті зміни, застосовується лише до вас та має бути виключений з обробки git.
Список заборон є запобіжним заходом, а не санкчуарієм. Шаблони відповідають рядкам команд, тож сприймайте їх як засіб захисту від випадковостей, а не як межу безпеки.
Практики, які підтримують належний стан налаштувань
Ставіться до папки .claude/ як до коду для продакшну: вона поширює ваші стандарти серед кожного інженера та під час кожної сесії, тому вона потребує перевірки, ведення історії змін та оптимізації.
- Тримайте
CLAUDE.mdкоротким — не більше 300 рядків. Усе, що там є, конкурує за простір із кодом, який асистент має прочитати. Якщо факт можна вивести з коду, краще його не включати. - Формулюйте правила як заборони. На практиці формулювання «ніколи не здійснюйте запит до бази даних усередині циклу» краще витримує тиск, ніж «віддавайте перевагу масовим запитам».
- Зберігайте шаблони у навичках. Вони мають завантажуватися лише під час виконання навички, а не при кожній сесії.
- Ведіть версіонування у git під директорією
.claude/. Нові правила додаються через pull requests; застарілі — видаляються. - Використовуйте таблиці прийняття рішень там, де існують два можливих варіанти, наприклад, щодо розміщення DTO, місця розташування ентитета чи меж послуг. У Markdown-файлах для асистента таблиця зрозуміліша, ніж абзаци.
Повна структура для налаштування, описаного в цьому посібнику:
.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
Помилки, які тихо погіршують результати
Кожна з цих помилок призводить до коду, який виглядає нормально, але порушує ваші правила, і саме це робить їх такими шкідливими.
- Запис
CLAUDE.mdяк посібника з NestJS. Модель вже знає цю фреймворк-систему. Документуйте лише те, що є унікальним для вас: назви модулів, константи помилок, структуру відповідей, систему дозволів. - Пропуск негативних правил. Найбільш шкідливими є помилки, які виглядають правильними; вказавши асистенту, що не робити, можна усунути цілі категорії таких помилок.
- Розміщення всього в кореневому файлі. Кореневий файл завантажується під час кожної сесії, тому специфічна для сервісу інформація там забруднює контекст під час роботи в інших місцях.
synchronize: false. Модель буде вважати, що TypeORM керує схемою. Коли використовується Flyway, зміна ентитета без відповідної міграції проходить без проблем, тому необхідно це чітко зазначити.Покроковий огляд: один запит, до та після
Припустимо, менеджер продукту просить про створення бронювань для відображення статусу, який користувачі можуть бачити на своїх пакетах.
У неконфігурованому репозиторії асистент вважає, що пакети визначені у operator-service, і редагує об’єкт пакета там, що є неправильним сервісом. Він закодовує рядок статусу як константу та захищає зміни за допомогою if (user.roles.includes('admin')).
За допомогою конфігурації з цього посібника запит обробляється крок за кроком:
- Таблиця маршрутизації класифікує статус, видимий для користувача, як частину його взаємодії, тому ця робота має виконуватися у
api-service. - Правило міжсервісної взаємодії нагадує про те, що дані пакета оператора читаються через адаптер IMS, тому поле додається до функції перетворення адаптера, а не до іншої бази даних.
- Запуск
/api-developmentгенерує чотири шари послідовно, починаючи з DTO та закінчуючи контролером.
@Permissions([PermissionCodes.VIEW_BOOKING_STATUS]) замість перевірки ролей.NotFoundException, створений на основі константи ErrorMessages.Одне розпливчасте речення перетворюється на зміни, що відповідають стандартам, у чотирьох файлах, без необхідності поставляти додаткові уточнювальні запитання.
Що змінюється щодня
Налаштування займає кілька годин один раз. Зміни, які воно має забезпечити:
- Змінюється фокус перевірки. Замість того, щоб перевіряти кожен створений файл на жорстко закодовані рядки, неправильні імпорти та перевірки ролей, перевіряючі зосереджують свою увагу на логіці.
- Кінцеві точки працюють швидше. Підключення чотирьох шарів вручну у описаному налаштуванні займало близько двадцяти хвилин; завдяки цій навичці цей процес скорочується до менше трьох хвилин.
console.log запобігаються під час генерації, а не виявляються у pull-запитах.Деякі переваги проявляються лише через кілька місяців. Код, створений у великих проміжках часу, виглядає послідовним, оскільки походить з одних і тих самих шаблонів та правил. Рецензенти перестають писати однакові коментарі. Таблиці маршрутизації та правила виступають у ролі документації, яка фактично використовується. А легкий CLAUDE.md разом із навичками, які потрібні саме за потребою, означає, що ресурси йдуть на читання вашого коду, а не на повторне вивчення стандартів.
Поширені запитання
Куди кладуться файли в монорепозиторії? Спільний CLAUDE.md розміщують у директорії .claude/ біля кореня git для обробки загальних питань, а CLAUDE.md у кожній службі — для її конкретних потреб. Файл з кореня завжди завантажується; файл служби використовується під час роботи з цією службою.
У чому різниця між CLAUDE.md та правилами? CLAUDE.md пояснює, як працює система; правила визначають, що ніколи не повинно з’являтися, наприклад запити в циклах чи перевірки імен ролей.
Як поводяться навички? Навичка, така як /api-development, підтримується за допомогою файлу SKILL.md, шаблонів та посилань. Її використання змушує асистента дотримуватися фіксованої багатокрокової процедури замість того, щоб імпровізувати.
Чи варто комітувати папку .claude/? Так: CLAUDE.md, rules/, skills/ та settings.json — усе це має знаходитися в git, щоб команда могла користуватися однією конфігурацією. Особисті зміни потрібно поміщати до файла settings.local.json, який включений до списку gitignored.
Основні висновки
- У монорепозиторії NestJS правильність залежить від конвенцій, які не відображаються в окремому файлі, тож асистент без контексту буде лише здогадуватися.
- Розділіть ці знання за типами:
CLAUDE.md— для пояснення того, як все працює, правила — щодо того, що заборонено, інструкції — для повторюваних процедур, налаштування — щодо того, що може працювати без нагляду. - Таблиця рішень щодо власності на сервіси є найважливішим елементом; напишіть її першою.
- Документуйте навмисне видалення елементів та нестандартні варіанти, такі як
synchronize: false, адже саме це модель інакше б „виправила“. - Правила сильно впливають на процес генерації, але не є засобом контролю; використовуйте інструменти lint, тести та CI для забезпечення незмінності елементів, які не можна допустити до пошкодження.
- Ці зусилля — це одноразова інвестиція у кілька годин, яка окупається за рахунок кожної функції та кожного нового співробітника, за умови що файли постійно переглядаються та оптимізуються, як і код.