Главная / Статьи

Обучение Claude Code вашему Monorepo NestJS: маршрутизация, правила и навыки

Научите Claude Code работе с монорепозиторием NestJS: файл CLAUDE.md, правила, навыки и разрешения, которые помогают размещать код в соответствующем сервисе и соблюдать командные стандарты.

Издатель: REACTAPP.TOP20 мин чтения4492 слов

Источники: Claude Code for NestJS Monorepos: A Practical Setup Guide

Если отправить неконфигурированному ИИ-ассистенту запрос вроде «добавить статус, видимый для клиента, к бронированию» в большом монорепозитории NestJS, результат будет предсказуемым: изменения попадут не в тот микросервис, сообщение об ошибке будет жестко заданной строкой, а доступ контролируется условием user.role === 'admin'. Проблема не в TypeScript, а в том, что модель делает предположения относительно решений, принятых вашей командой давно и нигде не зафиксированных, где они могут быть найдены. В этом руководстве показано, как заменить эти предположения на контекст, управляемый версиями, с использованием четырех основных элементов, предоставляемых Claude Code: CLAUDE.md, правил, навыков и настроек разрешений. К концу вы получите структуру, которую можно сохранить в репозиторий, чтобы каждый инженер и каждая сессия ИИ работали в соответствии с одинаковыми правилами.

Если вам нужны общие рекомендации по составлению самого файла с описанием задачи, ознакомьтесь с нашим пособием по созданию эффективного файла CLAUDE.md. В этой статье рассматриваются изменения, которые происходят, когда база кода представляет собой бэкенд NestJS с несколькими сервисами.

Краткая версия

Для читателей, которые хотят сначала увидеть список проверок, а затем обоснование:

  1. Установите Claude Code (например, с помощью команды npm install -g @anthropic-ai/claude-code) и один раз авторизуйтесь, запустив команду claude.
  2. Создайте корневой файл .claude/CLAUDE.md, в котором будет описано, какой сервис отвечает за какие данные, порядок обработки запросов и структура ответа, возвращаемого каждым эндпоинтом.
  3. Создайте каталог .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 содержит информацию об архитектуре, ответственных за сервисы и принятых конвенциях. Он автоматически загружается в начале каждой сессии.
    • Правила содержат запреты и неизменные условия, которые должны определять структуру каждого сгенерированного фрагмента кода.
  • Skills позволяют использовать многоэтапные рабочие процессы и шаблоны, причем они подключаются только тогда, когда появляется соответствующая задача.
  • Структура репозитория, предполагаемая в этом руководстве

    Перед написанием любой конфигурации полезно определить конкретную структуру. В примере бэкенда имеются четыре сервиса и один каталог .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 — это файл с наибольшим влиянием, поскольку именно его контекст ассистент читает прежде всего. Пишите его так, как бы вы информировали старшего инженера в первый день его работы: исходя из предположения о его общей квалификации, объясняйте только то, что связано непосредственно с вашей системой. В монорепозитории целесообразно использовать два уровня:

    1. Корневой файл .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/.

    Имейте в виду, что правила — это инструкции, загружаемые в контекст модели, а не статический анализатор. Они значительно снижают вероятность нарушений, но для критически важных параметров всё равно следует использовать дополнительные правила проверки, тесты или инструменты 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, ключом к которой служит идентификатор пользователя:

    // 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, чтобы сбой не привел к частичной сохранности данных.
  • Проверка DTO: каждый запрос-DTO требует декораторов class-validator. Поскольку ValidationPipe работает в режиме белого списка, свойства без декораторов молча удаляются, что выглядит как потеря данных, а не ошибка.
  • Соответствие сущностей и миграций: любые изменения сущностей требуют соответствующей миграции Flyway, причем параметр synchronize остается равным false.
  • Покрытие Swagger: каждый эндпоинт сопровождается аннотацией @ApiSpec и типизированным DTO ответа, чтобы генерируемая документация API оставалась точной.
  • Шаг 4: превращение повторяющихся рабочих процессов в навыки

    Навыки — это проект-специфичные команды вида slash, которые объединяют процедуры, шаблоны и справочные материалы. Они являются подходящим инструментом для любых задач, требующих работы с несколькими файлами и выполняемых часто: добавление конечной точки, написание скриптов миграции, создание структуры модуля, проверка pull request. В отличие от 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 содержит личные настройки, применяемые только к вам, и должен быть отмечен как gitignored.

    Список запрещённых команд служит защитным механизмом, а не средой изоляции. Паттерны соответствуют строкам команд, поэтому их следует рассматривать как средство предотвращения случайных действий, а не как границу безопасности.

    Практики, сохраняющие стабильность настроек

    Относитесь к папке .claude/ как к коду, предназначенному для производственного использования: она распространяет ваши стандарты среди всех инженеров и при каждой сессии, поэтому она требует ревизии, ведения истории изменений и очистки ненужных элементов.

    • Сохраняйте файл CLAUDE.md кратким — не более 300 строк. Все, что в нем находится, конкурирует за внимание с кодом, который должен прочитать ассистент. Если какой-либо факт можно вывести из кода, лучше его не указывать.
    • Формулируйте правила как запреты. На практике формулировка «никогда не выполняйте запросы к базе данных внутри цикла» гораздо лучше справляется с задачей при давлении, чем формулировка «предпочитайте массовые запросы».
    • Храните шаблоны внутри навыков. Они должны загружаться только тогда, когда запускается соответствующий навык, а не при каждой сессии.
    • Ведите версионирование папки .claude/ в git. Новые правила добавляются через 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
    

    Ошибки, которые тихо ухудшают качество вывода

    Каждая из этих ошибок приводит к коду, который на первый взгляд выглядит нормально, но нарушает установленные правила, и именно поэтому они так дорогостоящи.

    1. Запись CLAUDE.md как учебного материала для NestJS. Модель уже знает эту фреймворк-среду. Документируйте только то, что характерно именно для вас: названия модулей, константы ошибок, структуру ответа, систему разрешений.
    2. Игнорирование отрицательных правил. Самые дорогостоящие ошибки — это те, которые кажутся правильными; указание ассистенту того, чего делать не следует, позволяет исключить целые категории таких ошибок.
    3. Размещение всего в корневом файле. Корневой файл загружается при каждой сессии, поэтому детали, связанные с конкретными сервисами, загрязняют контекст при работе в других частях проекта.
  • Игнорирование таблицы маршрутизации. Неправильное размещение сервисов происходит из-за неоднозначности, а не незнания, и таблица делает принятие решений детерминированным.
  • Отсутствие документации к шаблону адаптера. В сервисах, связанных через HTTP, ассистент иначе попытается выполнить импорты между сервисами или запросы к базе данных другого сервиса.
  • Отсутствие упоминания synchronize: false. Модель будет предполагать, что TypeORM управляет схемой. При использовании Flyway изменение сущности без соответствующей миграции происходит бесшумно, поэтому необходимо явно указать это.
  • Пошаговое рассмотрение: один запрос, до и после

    Предположим, менеджер продукта просит добавить возможность отображения статуса бронирования, чтобы потребители могли видеть его на своих пакетах.

    В неконфигурированном репозитории ассистент считает, что пакеты определены в operator-service, и редактирует сущность пакета там, что является ошибкой, поскольку речь идет о неверном сервисе. Он закодирует строку статуса в виде константы и защищает изменения с помощью условия if (user.roles.includes('admin')).

    С использованием конфигурации из этого руководства запрос обрабатывается пошагово:

    1. Таблица маршрутизации относит статус, видимый для потребителя, к части взаимодействия с ним, поэтому данная работа должна выполняться в api-service.
    2. Правило межсервисного взаимодействия напоминает ассистенту, что данные пакетов оператора считываются через адаптер IMS, поэтому поле добавляется в функцию преобразования адаптера, а не обращается к другой базе данных.
    3. Запуск команды /api-development генерирует четыре слоя последовательно, начиная с DTO и заканчивая контроллером.
  • Правило разрешений генерирует @Permissions([PermissionCodes.VIEW_BOOKING_STATUS]) вместо проверки ролей.
  • Правило обработки ошибок создаёт объект NotFoundException, сформированный на основе константы ErrorMessages.
  • Одно неопределённое предложение приводит к соответствию стандартам в четырёх файлах без необходимости задавать уточняющие вопросы.

    Что меняется ежедневно

    Настройка занимает несколько часов один раз. Изменения, которые она должна внести:

    • Смещается фокус проверки. Вместо поиска жёстко заданных строк, неверных импортов и проверок ролей в каждом сгенерированном файле ревизоры сосредотачиваются на логике.
    • Конечные точки работают быстрее. В описанной схеме ручная связка четырёх слоев занимала около двадцати минут; благодаря данной навыку это время сокращается до трёх минут.
  • Размещение файлов происходит правильно с первого раза. Таблица маршрутизации направляет файлы в соответствующий сервис, что позволяет избежать проблем при последующей проверке.
  • Распространённые ошибки устраняются на этапе создания кода. Проблемы, связанные с N+1 запросами и ненужными вызовами console.log, предотвращаются в процессе генерации кода, а не обнаруживаются в pull-запросах.
  • Процесс адаптации сотрудников улучшается. Новые инженеры больше не должны догадываться о границах модулей, поскольку соответствующие правила зафиксированы и применяются к их первому функционалу, созданному с использованием ИИ.
  • Некоторые преимущества проявляются только со временем — спустя несколько месяцев. Код, сгенерированный в разное время, выглядит единообразным, поскольку создается на основе одинаковых шаблонов и правил. Ревьюеры перестают писать одинаковые комментарии. Таблицы маршрутизации и правила служат документацией, которая фактически используется в работе. А лаконичный CLAUDE.md в сочетании с непосредственным получением необходимых навыков позволяет направлять ресурсы на чтение кода, а не на повторное изучение стандартов.

    Часто задаваемые вопросы

    Куда помещать файлы в монорепозитории? Общий CLAUDE.md следует разместить в папке .claude/ у корня проекта для решения общих вопросов, а отдельный CLAUDE.md — внутри каждого сервиса для описания его специфики. Файл из корня всегда загружается; файл сервиса подключается при работе над конкретным сервисом.

    В чем разница между CLAUDE.md и правилами? CLAUDE.md объясняет, как работает система; правила указывают на то, что никогда не должно появляться, например запросы в циклах или проверки имён ролей.

    Как ведут себя навыки? Навык, такой как /api-development, поддерживается с помощью файла SKILL.md, шаблонов и ссылок. При его использовании ассистент следует фиксированной многократной процедуре вместо того, чтобы действовать импровизированно.

    Следует ли добавлять папку .claude/ в git? Да: CLAUDE.md, rules/, skills/ и settings.json должны находиться в git, чтобы команда использовала единое настройки. Личные изменения сохраняются в файле settings.local.json, который находится в папке gitignored.

    Основные выводы

    • В монорепозитории NestJS корректность работы зависит от конвенций, которые не отражены в отдельных файлах, поэтому ассистент без контекста будет только догадываться.
    • Разделите эту информацию по категориям: CLAUDE.md — для описания принципов работы, правила — для указания того, что запрещено, инструкции — для повторяющихся процедур, настройки — для того, что может работать автономно.
    • Таблица решений относительно ответственности за сервисы является наиболее важным элементом; напишите её первой.
    • Документируйте преднамеренное удаление элементов и нестандартные выборы, такие как synchronize: false, поскольку именно это модель в противном случае попыталась бы «исправить».
    • Правила сильно влияют на процесс генерации кода, но не обеспечивают его строгую проверку; используйте инструменты lint, тестирование и CI для обеспечения соблюдения неизменных требований.
    • Эти усилия представляют собой однократные инвестиции в несколько часов, которые окупаются при разработке каждой новой функции и при найме новых сотрудников, при условии регулярного анализа и оптимизации файлов, как это делается с кодом.