Enseñar a Claude Code tu Monorepo NestJS: enrutamiento, reglas y habilidades
Enseña a Claude Code un monorepo de NestJS: CLAUDE.md, reglas, habilidades y permisos que ubican el código en el servicio adecuado y lo mantienen alineado con las convenciones del equipo.
Fuentes: Claude Code for NestJS Monorepos: A Practical Setup Guide
Si le haces una solicitud a un asistente de IA no configurado, como “agregar un estado visible para el cliente a una reserva” en un gran monorepo de NestJS, el resultado es predecible: el cambio llega al microservicio incorrecto, el mensaje de error es una cadena de texto predefinida y el acceso está protegido por user.role === 'admin'. El modelo no falla debido a TypeScript; simplemente está adivinando las decisiones que tu equipo tomó hace tiempo y que nunca anotó en un lugar donde pudiera consultarlas. Esta guía muestra cómo reemplazar esa adivinación por un contexto controlado mediante versiones, utilizando los cuatro componentes que ofrece Claude Code: CLAUDE.md, reglas, habilidades y configuraciones de permisos. Al final, tendrás un esquema que podrás guardar en tu repositorio para que cada ingeniero y cada sesión de IA partan de las mismas convenciones.
Si desea consejos generales sobre cómo redactar el archivo de información, consulte nuestro plan de acción para un CLAUDE.md eficaz. Este artículo se centra en los cambios que ocurren cuando la base de código es un backend NestJS multi-servicio.
La versión breve
Para los lectores que prefieren la lista de verificación antes de los argumentos:
- Instale Claude Code (por ejemplo, con
npm install -g @anthropic-ai/claude-code) y autentíquese una vez ejecutandoclaude. - Cree un archivo raíz
.claude/CLAUDE.mdque explique qué servicio posee qué datos, el orden de su pipeline de solicitudes y el formato de respuesta que devuelve cada endpoint. - Agregue un directorio
.claude/rules/con prohibiciones: no se permiten consultas dentro de bucles, ni verificaciones basadas en nombres de roles, ni importaciones relativas.
.claude/skills/ que contenga flujos de trabajo paso a paso y plantillas de código para tareas que repite, como la creación de un endpoint..claude/settings.json para aprobar previamente comandos seguros y bloquear aquellos destructivos..claude/ para que la configuración se comparta y revise como si fuera código.Por qué un monorepo de NestJS confunde a los asistentes de IA
En una gran base de código de NestJS, si el código es correcto a menudo depende de convenciones que ningún archivo individual revela. Decenas de módulos, cientos de DTOs, guardias personalizados, interceptores, filtros de excepciones y adaptadores HTTP generan una verdadera ambigüedad. Cuando nada resuelve esa ambigüedad, el modelo recurre a valores por defecto genéricos razonables, y estos valores por defecto rara vez se ajustan a un equipo específico.
Síntomas típicos de una configuración no establecida:
- Un método de servicio se comunica directamente con la base de datos en lugar de hacerlo a través de la capa de repositorio.
- Los mensajes de error son literales de cadena en lugar de entradas provenientes de un archivo de constantes
ErrorMessages. - Se crea un archivo nuevo en
operator-serviceaunque la lógica de reservas pertenece aapi-service. - La autorización se implementa con
user.role === 'admin'en lugar de utilizarPermissionCodes. - La salida de depuración utiliza
console.logen lugar del registrador estructurado. - Las importaciones acceden a los directorios mediante
../../common/utilsen lugar de usar el alias de rutasrc/.
La causa raíz es la falta de conocimiento interno del equipo
Ninguno de estos es un error del modelo. Durante una sesión, el asistente solo conoce los archivos que ha abierto. No tiene forma de saber que el equipo eliminó deliberadamente RolesGuard, que la opción synchronize de TypeORM debe permanecer en false, o que los servicios se comunican entre sí exclusivamente a través de adaptadores HTTP. Ese conocimiento reside en la mente de los ingenieros y en temas antiguos de solicitudes de integración.
La solución es anotar ese conocimiento en archivos que la herramienta cargue, y asociar cada tipo de conocimiento al mecanismo adecuado:
CLAUDE.mdcontiene la arquitectura, la responsabilidad de los servicios y las convenciones. Se carga automáticamente al inicio de cada sesión.- Rules incluyen prohibiciones e invariantes que deben guiar cada fragmento de código generado.
La estructura del repositorio que asume esta guía
Es útil establecer una estructura concreta antes de escribir cualquier configuración. El backend de ejemplo cuenta con cuatro servicios y un único directorio .claude/ en la raíz del repositorio:
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
Cada servicio es una aplicación autónoma con su propio package.json, tsconfig.json y resultados de compilación. Cabe señalar que uno de ellos, chat-service, está escrito en JavaScript puro con Express y Socket.IO, mientras que job-service no cuenta con servidor HTTP alguno. Esa heterogeneidad es precisamente el tipo de información que un asistente no puede deducir a partir de un único archivo.
Dentro de cada servicio NestJS, los módulos funcionales comparten la misma estructura de tres capas:
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
Los controladores enrutan las solicitudes HTTP y nada más; los servicios contienen la lógica de negocio y la orquestación, mientras que los repositorios albergan las consultas de TypeORM. Si al asistente nunca se le ha explicado esta división, tenderá a confundirla: un servicio que emite consultas o un controlador que llama directamente a un repositorio. Escribir claramente esta estructura en capas es lo que permite respetar los límites entre ellas.
Paso 1: instalar y autenticarse
La instalación consiste en un único paquete global, seguido de un inicio de sesión en el navegador que se realiza una sola vez:
npm install -g @anthropic-ai/claude-code
claude # opens a browser for one-time OAuth
Una vez autenticado, claude puede iniciarse desde cualquier directorio. Cada desarrollador lo instala e inicia sesión en su propia máquina; no existe una clave de equipo compartida que distribuir. Las opciones de instalación cambian con el tiempo, por lo que consulte la documentación actual de Claude Code si la ruta npm ya no es la recomendada.
Un detalle es especialmente importante en los monorepos. La configuración del proyecto se determina con respecto a la raíz del repositorio, por lo que las instrucciones en la carpeta raíz .claude/ son aplicables dondequiera que inicies una sesión. Cuando tu trabajo está limitado a un servicio, inicia Claude desde el interior de ese directorio de servicio, como services/api-service/, para que las búsquedas y comandos se ejecuten contra el paquete correcto.
Paso 2: redactar la documentación CLAUDE.md
CLAUDE.md es el archivo con mayor impacto, ya que es el contexto que el asistente lee antes que nada. Escríbelo de la manera en que informarías a un ingeniero senior en su primer día: asume competencia general y explica solo lo específico de tu sistema. En un monorepo tiene sentido tener dos niveles:
- Un archivo raíz
.claude/CLAUDE.mdpara la responsabilidad por los servicios, las convenciones compartidas y el pipeline de solicitudes.
services/{service}/CLAUDE.md por servicio que incluye patrones de módulo, detalles de autenticación e inyección de dependencias que solo son aplicables allí.Archivo raíz: convierte la asignación de servicios en una decisión, no en una suposición
El error más costoso en un repositorio con múltiples servicios es colocar el código en el lugar incorrecto, ya que pasa la revisión local y solo se detecta posteriormente como una violación de los límites establecidos. El archivo raíz lo evita mediante una tabla de enrutamiento y una lista explícita de patrones prohibidos. Aquí hay un ejemplo de lo que puede contener ese archivo:
## 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
Varias partes de este archivo merecen atención. La sección de propiedad está formulada como una prueba (“¿describe los datos qué es una experiencia o la interacción de un consumidor con ella?”) en lugar de como un listado de directorios, lo que permite al asistente clasificar solicitudes que nunca ha visto. La sección de pipeline enumera los middleware, tuberías, interceptores y filtros en orden de ejecución, de modo que el código generado no duplique lo que ya hace TransformInterceptor. La sección de servicios cruzados indica claramente que api-service nunca accede a la base de datos del operador y debe pasar por los adaptadores en src/common/adapters/.
Lo que diferencia un CLAUDE.md útil de uno deficiente:
- Las reglas de decisión superan a los árboles de archivos. Una regla como “los datos que describen qué es una experiencia deben estar en operator-service” resuelve la ambigüedad sin necesidad de hacer preguntas aclaratorias.
- Defina la estructura de la respuesta. Sin ella, cada controlador envuelve sus propias respuestas y la estructura general se vuelve caótica.
- Especifique qué está prohibido. “Nunca use
console.log” es más fácil de seguir que “use nuestro registrador de eventos”. - Registre las eliminaciones intencionales. Si eliminó
RolesGuardo desactivósynchronize, indíquelo y explique el motivo. De lo contrario, el asistente podría reintroducirlos, precisamente porque parecen ser la opción habitual en NestJS.
Archivos a nivel de servicio: profundidad que solo un servicio necesita
Los detalles que solo son relevantes dentro de un servicio determinado se incluyen en el archivo propio de ese servicio. Para api-service, esto abarca la estructura en capas, la autenticación, los tokens de inyección de dependencias y las tareas en segundo plano:
## 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.
Hay dos puntos aquí que son fáciles de malinterpretar sin orientación. Primero, los proveedores se registran e inyectan a través de tokens de interfaz como SERVICE_INTERFACE.BOOKING_SERVICE, no por clase; por lo tanto, un constructor generado que inyecte directamente BookingService se compilaría, pero rompería el patrón establecido por el equipo. Segundo, las tareas de BullMQ solo se ponen en cola desde el servicio de un módulo, nunca desde un controlador, y los procesadores se encuentran en src/modules/{module}/processors/. Al mantener estos datos fuera del archivo raíz, no consumen contexto cuando se trabaja en chat-service.
Paso 3: agregar reglas para las situaciones que nunca deben ocurrir
Donde CLAUDE.md describe cómo funciona el sistema, las reglas indican qué no debe contener el código generado. Un modelo mental útil es una configuración de ESLint orientada a la salida de IA. Las reglas son archivos en Markdown ubicados dentro de .claude/rules/.
Una forma sencilla de decidir dónde ir algo es:
- La arquitectura, la estructura de módulos, la responsabilidad por los servicios, las convenciones y las referencias a comandos pertenecen a
CLAUDE.md. - Las prohibiciones estrictas, los patrones que nunca deben emitirse y las invariantes de seguridad o rendimiento se encuentran en
rules/.
Tenga en cuenta que las reglas son instrucciones cargadas en el contexto del modelo, no un analizador estático. Hacen que las violaciones sean mucho menos probables, pero para las invariantes realmente importantes aún debe respaldarlas con reglas de lint, pruebas o verificaciones de CI.
Regla: sin consultas N+1
Las consultas N+1 son fáciles de escribir y también fáciles de pasar por alto durante las revisiones, ya que cada línea individual parece inocente. Una regla en .claude/rules/database-queries.md funciona mejor cuando muestra tanto el patrón prohibido como la sustitución. La versión prohibida genera una consulta por cada reserva:
// Wrong — N queries
for (const b of bookings) {
b.user = await this.userRepo.findOne(b.userId);
}
La versión de sustitución carga a todos los usuarios en una sola consulta utilizando el operador In de TypeORM, y luego los guarda en memoria a través de un Map indexado por 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);
});
La ventaja es que la consulta masiva se escribe en el momento de la generación, por lo que el problema de rendimiento nunca llega a una solicitud de integración. Recuerde recopilar primero los userIds de las reservas, y considere eliminar duplicados cuando varias reservas compartan el mismo usuario.
Regla: permisos, nunca nombres de roles
La segunda regla, .claude/rules/permissions.md, prohíbe las verificaciones de roles ad hoc como esta:
// Wrong
if (user.roles.includes('admin')) { ... }
En su lugar, los endpoints declaran el permiso que necesitan y dejan que las guardias lo apliquen:
// Correct
@Permissions([PermissionCodes.MANAGE_BOOKINGS])
@UseGuards(JwtAuthGuard, PermissionsGuard)
La lógica también debe incluirse en el archivo de reglas, ya que ayuda al modelo a generalizar: los roles se fusionan, dividen y renombran a medida que el producto crece, y cada verificación realizada contra un nombre de rol deja de funcionar silenciosamente cuando eso ocurre. Los códigos de permiso son constantes estables que sobreviven a tales reorganizaciones. La regla también debe indicar dónde se encuentran, en este caso src/common/constants/permissions.constant.ts.
Regla: alias de rutas en lugar de importaciones relativas
Otra regla, .claude/rules/imports.md, estandariza las importaciones:
// Wrong
import { UserService } from "../../services/user.service";
// Correct
import { UserService } from "src/modules/user/services/user.service";
El alias funciona porque tsconfig.json establece baseUrl en "./" y moduleNameMapper de Jest mapea el mismo prefijo. Es importante mencionar ambos aspectos: si el asistente solo conociera la parte relacionada con TypeScript, podría concluir que el alias no es seguro en las pruebas.
Más reglas que vale la pena establecer
El mismo formato de “prohibir y mostrar la alternativa” sirve para muchos problemas recurrentes en NestJS. Ejemplos aplicables a sus propios archivos:
- Manejo de errores: lance excepciones de NestJS creadas a partir de las constantes
ErrorMessagesoResponseCodeKeys; nunca codifique mensajes de forma fija ni devuelva objetos de error sin procesar. - Registro de logs: utilice la función auxiliar estructurada
logMessage()con el nivel adecuado;console.logestá prohibido.
EntityManager, de modo que un fallo no pueda dejar los datos parcialmente confirmados.class-validator. Dado que ValidationPipe funciona en modo de lista blanca, las propiedades sin decoradores se eliminan silenciosamente, lo que parece una pérdida de datos en lugar de un error.synchronize permanece en false.@ApiSpec y un DTO de respuesta tipado para que la documentación de la API generada siga siendo precisa.Paso 4: convertir flujos de trabajo repetitivos en habilidades
Las habilidades son comandos específicos de proyecto que agrupan un procedimiento, plantillas y material de referencia. Son la herramienta adecuada para cualquier tarea que involucre varios archivos y se realice con frecuencia: agregar un punto de extremo, escribir una migración, estructurar un módulo o revisar una solicitud de integración. A diferencia de CLAUDE.md, el contenido de una habilidad solo se carga cuando se utiliza, por lo que las plantillas detalladas no consumen recursos en cada sesión.
Anatomía de un directorio de habilidades
Una habilidad para crear puntos de extremo podría organizarse de esta manera, con el flujo de trabajo en SKILL.md, las plantillas en assets/ y el material de referencia en 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
El flujo de trabajo de SKILL.md
El archivo comienza con un título sencillo:
API Development Skill
Luego, el cuerpo indica cuándo invocar la habilidad y describe los pasos en un orden fijo:
## 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.
El orden es intencional. Al verificar primero la tabla de enrutamiento se evita que la habilidad se integre en el servicio incorrecto. Leer las entidades, repositorios y DTOs existentes impide su duplicación. Generar el DTO antes que nada permite definir el contrato, y cada capa posterior se construye sobre él: el repositorio acepta un EntityManager opcional para poder combinar transacciones, el servicio se inyecta mediante un token de interfaz y lanza excepciones estándar con constantes ErrorMessages, mientras que el controlador solo documenta, delega y devuelve { data, meta }. El paso final conecta los proveedores al módulo y los exporta mediante un token.
Una observación práctica: las versiones actuales de Claude Code detectan las habilidades a través del frontmatter en YAML al principio de SKILL.md (un name y un description que indican cuándo usarla). El ejemplo anterior lo omite, así que consulte la documentación actual para conocer el formato exacto al crear el suyo.
Los templates deben ser código real
Los templates en assets/ solo son útiles si se pueden compilar. El pseudocódigo hace que el modelo llene los espacios en blanco con sus propios valores predeterminados. El template del controlador comienza con la declaración de la clase e inyección basada en tokens:
@ApiTags("bookings")
@Controller("bookings")
export class BookingController {
constructor(
@Inject(SERVICE_INTERFACE.BOOKING_SERVICE)
private readonly bookingService: IBookingService,
) {}
Continúa con un endpoint completo que muestra al mismo tiempo el orden de las protecciones, el decorador de permisos, los decoradores de Swagger y la estructura del resultado:
@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 };
}
}
Tenga en cuenta lo que enseña este único método: JwtAuthGuard se ejecuta antes que PermissionsGuard, el ID del usuario proviene de la estructura tipada UserRequest, el controlador no contiene lógica de negocio, y devuelve { data } para que TransformInterceptor lo envuelva. Con una habilidad como esta en práctica, el escenario descrito aquí permite generar un endpoint completo (controlador, servicio, repositorio y DTOs, integrados en el módulo) en menos de tres minutos, en lugar de aproximadamente veinte, siguiendo el estilo del equipo desde el primer intento. Sus propios tiempos dependerán del tamaño de sus plantillas y de cuánta revisión aún realice.
Otras habilidades a considerar
Cualquier flujo de trabajo repetitivo que siga convenciones específicas del equipo es un buen candidato:
- Generador de migraciones: crea una migración Flyway en la carpeta del esquema correcto utilizando su esquema de versionado y nombramiento, junto con el cambio correspondiente en las entidades.
- Generador de estructura de módulos: produce un archivo de módulo, las tres capas y los tokens de inyección de dependencias, ya conectados entre sí.
- Revisión de solicitudes de pull: verifica diferencias en busca de consultas N+1, uso inadecuado de permisos, violaciones en las capas y pruebas faltantes.
- Generador de pruebas: escribe archivos
*.spec.tsque siguen las convenciones de simulación y configuración de entornos de prueba de los servicios. - Adaptador entre servicios: agrega un método del adaptador IMS junto con su interfaz de respuesta y la herramienta de transformación correspondiente.
- Dокументación de casos de uso: convierte un requisito en historias de usuario con criterios de aceptación del tipo Given/When/Then.
Paso 5: configurar permisos
.claude/settings.json determina qué comandos de shell puede ejecutar el asistente sin tener que preguntar primero. Una configuración básica razonable aprueba previamente las tareas de validación, pruebas y compilación, y deniega explícitamente los comandos que destruyen el trabajo o lo publican:
{
"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 *)"]
}
}
Los ajustes existen en dos niveles:
.claude/settings.jsonse aplica a todo el equipo y se guarda en el repositorio..claude/settings.local.jsoncontiene modificaciones personales, solo se aplica a usted y debe marcarse como gitignored.
La lista de denegaciones funciona como red de seguridad, no como entorno aislado. Los patrones coinciden con cadenas de comandos, por lo que deben considerarse como medidas preventivas contra accidentes y no como límites de seguridad.
Prácticas que mantienen la configuración en buen estado
Trate .claude/ como código de producción: distribuye sus convenciones a cada ingeniero y en cada sesión, por lo que merece ser revisado, tener un historial y someterse a actualizaciones periódicas.
- Mantenga
CLAUDE.mdbreve, con un máximo de unas 300 líneas. Todo lo que contenga compite por el espacio de contexto con el código que el asistente debe leer. Si un dato puede deducirse del código, óptimamente no se incluya. - Formule las reglas como prohibiciones. En la práctica, frases como "nunca consulte la base de datos dentro de un bucle" resisten mejor bajo presión que expresiones como "prefiera consultas masivas".
- Mantenga las plantillas dentro de las habilidades. Deben cargarse únicamente cuando se ejecuta la habilidad, y no en cada sesión.
- Versione
.claude/en git. Las nuevas reglas se añaden a través de pull requests; las obsoletas se eliminan. - Utilice tablas de decisión siempre que haya dos opciones plausibles, como en la ubicación de DTO, el emplazamiento de entidades o los límites de servicios. En los archivos Markdown para el asistente, una tabla resulta más clara que los párrafos.
Un diseño completo para la configuración descrita en esta guía:
.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
Errores que degradan silenciosamente la salida
Cada uno de ellos genera código que parece correcto aunque viole sus convenciones, y eso es lo que los hace problemáticos.
- Escribir
CLAUDE.mdcomo un tutorial de NestJS. El modelo ya conoce el framework. Documente solo lo que es específico de usted: nombres de módulos, constantes de errores, el formato de la respuesta, el sistema de permisos. - Omitir reglas negativas. Los errores problemáticos son aquellos que parecen correctos; indicarle al asistente qué no hacer elimina categorías enteras de ellos.
- Poner todo en el archivo raíz. El archivo raíz se carga en cada sesión, por lo que la profundidad específica de los servicios allí presente contamina el contexto cuando trabaja en otra parte.
synchronize: false. El modelo asumirá que TypeORM gestiona el esquema. Con Flyway a cargo, un cambio en la entidad sin una migración fallará silenciosamente, por lo que hay que indicarlo explícitamente.Desglose: una solicitud, antes y después
Supongamos que un gerente de productos solicita que se muestren las reservas para exponer un estado que los consumidores puedan ver en su paquete.
En un repositorio no configurado, el asistente deduce que los paquetes están definidos en operator-service y edita la entidad del paquete allí, lo cual corresponde al servicio incorrecto. Codifica de forma fija la cadena de estado y protege el cambio con if (user.roles.includes('admin')).
Con la configuración de esta guía, la solicitud se resuelve paso a paso:
- La tabla de enrutamiento clasifica un estado visible para el consumidor como parte de la interacción del mismo, por lo que el trabajo debe realizarse en
api-service. - La regla inter-servicio recuerda que los datos del paquete del operador se leen a través de un adaptador IMS, por lo que el campo se agrega a la transformación del adaptador en lugar de acceder a otra base de datos.
- Al ejecutar
/api-developmentse generan las cuatro capas en secuencia, comenzando por el DTO y terminando con el controlador.
@Permissions([PermissionCodes.VIEW_BOOKING_STATUS]) en lugar de una verificación de roles.NotFoundException basado en la constante ErrorMessages.Una sola oración vaga se convierte en cambios que cumplen con las convenciones en cuatro archivos, sin necesidad de hacer preguntas para aclarar nada.
Qué cambia día a día
La configuración lleva unas pocas horas una sola vez. Los cambios que se supone que genera son:
- El enfoque de la revisión cambia. En lugar de verificar cada archivo generado en busca de cadenas de texto hardcodeadas, importaciones incorrectas y verificaciones de roles, los revisores centran su atención en la lógica.
- Los endpoints son más rápidos. Conectar cuatro capas a mano tomaba alrededor de veinte minutos en la configuración descrita; esta habilidad reduce ese tiempo a menos de tres minutos.
console.log no deseadas se previenen durante la generación, en lugar de encontrarse en las solicitudes de integración.Algunos beneficios solo se manifiestan con el paso de los meses. El código generado en intervalos largos parece consistente porque proviene de las mismas plantillas y reglas. Los revisores dejan de escribir los mismos comentarios. Las tablas de enrutamiento y las reglas funcionan como documentación que realmente se utiliza. Además, un CLAUDE.md simplificado junto con habilidades bajo demanda permiten que el tiempo destinado al contexto se invierta en leer el código en lugar de redescubrir las convenciones.
Preguntas frecuentes
¿Dónde se colocan los archivos en un monorepo? Coloca el CLAUDE.md compartido en .claude/ en la raíz de git para los temas transversales, y un CLAUDE.md dentro de cada servicio para sus características específicas. El archivo de la raíz siempre se carga; el archivo del servicio se utiliza cuando trabajas en ese servicio.
¿En qué se diferencia CLAUDE.md de las reglas? CLAUDE.md explica cómo funciona el sistema; las reglas indican qué no debe aparecer nunca, como consultas en bucles o verificaciones de nombres de roles.
¿Cómo se comportan las habilidades? Una habilidad como /api-development cuenta con el soporte de un flujo de trabajo SKILL.md además de plantillas y referencias. Al invocarla, el asistente sigue un procedimiento fijo de varios pasos en lugar de improvisar uno.
¿Debería comprometerse .claude/? Sí: CLAUDE.md, rules/, skills/ y settings.json deben estar todos en git para que el equipo comparta una única configuración. Las modificaciones personales se guardan en settings.local.json, que está excluido de git.
Puntos clave
- En un monorepo de NestJS, la corrección depende de convenciones que no se reflejan en ningún archivo individual, por lo que un asistente sin contexto solo podrá adivinar.
- Divida ese conocimiento por tipo:
CLAUDE.mdpara explicar cómo funcionan las cosas, reglas para lo que está prohibido, procedimientos repetibles en secciones dedicadas a ellas, y configuraciones para aquello que puede ejecutarse sin supervisión. - La tabla de decisiones sobre la responsabilidad por los servicios es el elemento más importante; escríbala primero.
- Documente las eliminaciones intencionadas y las opciones que no son predeterminadas, como
synchronize: false, ya que son precisamente aquello que un modelo de otro modo intentaría “arreglar”. - Las reglas guían en gran medida la generación de código, pero no sirven como mecanismo de cumplimiento; utilice herramientas de linting, pruebas y CI para las invariantes que no puede permitirse romper.
- Este esfuerzo representa una inversión única de unas pocas horas que se compensa en cada funcionalidad y con cada nuevo empleado, siempre y cuando los archivos se revisen y se optimicen al igual que el código.