Estructurando proyectos de Claude Code para que los agentes se mantengan enfocados en repositorios grandes
Seis patrones para grandes bases de código: archivos CLAUDE.md en capas, habilidades, subagentes, reglas de denegación, árboles de trabajo dispersos y complementos de language-server que mantienen el contexto del agente limpio.
Fuentes: How I Structure Claude Code Projects So Agents Don’t Get Lost in Large Codebases
Imagínese una solicitud sencilla a Claude Code: corregir un error de tiempo de espera de sesión en el middleware de autenticación. La solución real consta de tres líneas. Sin embargo, antes de llegar a ellas, el agente recorre el sistema de registro, el historial de migraciones, la mitad de los puntos finales de la API y, por razones que nadie puede explicar, las configuraciones de Terraform. Al leer el archivo número 147, el desarrollador ya presiona Escape.
El modelo no es el culpable en una situación así; el culpable es la estructura del proyecto. Cada convención, comando de compilación y decisión arquitectónica se había acumulado en un único archivo CLAUDE.md de 340 líneas, que el agente leía fielmente al iniciar antes de dedicar el resto de su tiempo a explorar código no relacionado con la autenticación.
Eso es lo que sucede cuando a un agente se le trata como a un empleado nuevo que debe leer todo el manual de la empresa antes de corregir un error tipográfico. Necesita el contexto adecuado en el momento oportuno y no todo de una sola vez. A continuación se presentan seis patrones que solucionan este problema, junto con una forma de determinar cuándo vale la pena utilizar cada uno.
Por qué el contexto se agota rápidamente
La ventana de contexto de Claude Code, de unos 200,000 tokens en el modelo utilizado aquí, parece enorme hasta que se enumeran los factores que la reducen: cada mensaje que envías, cada archivo que se lee, toda la salida de los comandos, cada instrucción CLAUDE.md y cualquier nota de memoria guardada automáticamente. Un token equivale aproximadamente a cuatro caracteres.
En una base de código grande, 50,000 tokens pueden desaparecer antes de que se escriba una sola línea de código. Un archivo CLAUDE.md raíz de 300 líneas cuesta aproximadamente 1,200 tokens, uno de 500 líneas requiere otros 2,000, y explorar con grep o find añade aún más. Para cuando el agente comprende la tarea, ya puede haberse agotado la mitad del tiempo disponible. A medida que este se llena, el rendimiento disminuye: las instrucciones anteriores se olvidan, los archivos se vuelven a leer y aparecen errores que no ocurrirían con un contexto limpio. Las ventanas más grandes no son la solución; lo importante es mantener fuera del alcance del agente el material irrelevante.
Patrón 1: archivos CLAUDE.md en capas
El primer error es incluir todo en el archivo raíz: comandos de compilación para la API, patrones de componentes frontales, reglas de migración, listas de verificación para despliegues y convenciones de pruebas, todo junto.
Claude Code lee los archivos CLAUDE.md desde el directorio de trabajo hasta la raíz del repositorio al iniciar una sesión. Si se inicia en un subdirectorio, recoge el archivo de esa carpeta además de todos los de los directorios padre hasta llegar a la raíz. Por lo tanto, divida el contenido según el ámbito:
monorepo/
CLAUDE.md # repository-wide rules only
packages/
api/
CLAUDE.md # API-specific conventions
.claude/skills/
src/
web/
CLAUDE.md # frontend-specific conventions
.claude/skills/
src/
shared/
CLAUDE.md # shared library conventions
src/
El archivo raíz contiene únicamente la estructura del repositorio y las convenciones de commits, alrededor de 40 líneas, y cada paquete incluye sus propios detalles específicos del stack. Al iniciar el agente en packages/api/, este carga el archivo raíz además del correspondiente a la API, sin tener en cuenta nunca las convenciones del frontend. Al cambiar a packages/web/, la misma lógica proporciona una información de contexto diferente.
Un archivo raíz podría tener el siguiente contenido:
# Repository Structure
This is a monorepo with three packages under packages/:
- packages/api: Node.js REST API with Express, TypeScript, PostgreSQL
- packages/web: React frontend with Vite, TypeScript, TailwindCSS
- packages/shared: shared TypeScript utilities
Run commands from the package directory, not the monorepo root.
Each package has its own tsconfig.json, package.json, and test suite.
# Commit Conventions
- Prefix commits with the package name: "api: fix session timeout"
- One commit per logical change
- Run tests before committing
Y el paquete de API solo agrega lo que es importante dentro de ese paquete:
# API Package
This is the REST API server.
## Commands
- Run tests: `npm test` (uses Vitest)
- Run dev server: `npm run dev` (port 3001)
- Database migrations: `npm run migrate`
- Environment variables: copy `.env.example` to `.env`
## Code Patterns
API routes are in src/routes/. Each route file exports an Express router.
Database queries use Knex in src/db/. Never write raw SQL in route handlers.
## Testing
Tests are in src/__tests__/ mirroring the src/ directory.
Use supertest for HTTP assertions, not raw fetch.
Always wrap database tests in a transaction that rolls back.
En este ejemplo, el efecto es que se cargan 80 líneas de contexto en lugar de 340, y la corrección de autenticación implica leer 12 archivos en vez de 147.
Patrón 2: habilidades para conocimiento bajo demanda
Incluso una configuración en capas puede volverse excesiva. Los patrones de pruebas, las listas de verificación de despliegue y los manuales de depuración son procedimientos y no hechos; el agente no los necesita al iniciar, solo cuando la tarea lo requiere. Esa es la función de las habilidades.
Las habilidades se encuentran en .claude/skills/ y se cargan cuando Claude las considera relevantes, o cuando se invocan explícitamente mediante una orden con barra diagonal. La descripción de una habilidad indica cuándo usarla y su contenido real alberga la información correspondiente. Aquí, las indicaciones de pruebas pasan del archivo de API a una habilidad:
# .claude/skills/api-testing/SKILL.md
---
name: api-testing
description: Testing patterns for the API package. Use when writing or modifying tests in packages/api/.
---
## Test Structure
Tests are in `src/__tests__/` mirroring the `src/` directory structure.
Each route file has a corresponding `.test.ts` file.
## Running Tests
- All tests: `npm test`
- Single file: `npm test -- src/__tests__/routes/users.test.ts`
- Watch mode: `npm test -- --watch`
## Test Utilities
- `src/__tests__/helpers/db.ts`: provides `setupTestDb()` and `teardownTestDb()`
- `src/__tests__/helpers/auth.ts`: provides `createTestUser()` and `getAuthToken()`
## Patterns
- Use `supertest` for HTTP assertions, not raw fetch
- Always wrap database tests in a transaction that rolls back
- Mock external services in `src/__tests__/mocks/`
Pida pruebas en el middleware de autenticación y el agente cargará la habilidad de pruebas y seguirá sus patrones. Pídale que corrija el error de tiempo de espera y la habilidad permanecerá sin cargarse, ya que las pruebas no son relevantes.
Las habilidades también son adecuadas para flujos de trabajo repetibles. Una habilidad llamada con /fix-pr-comments podría leer los comentarios de revisión en una solicitud de pull de GitHub, determinar los cambios solicitados, editar el código, ejecutar pruebas y hacer el push. Todo el procedimiento está contenido en la habilidad y no en un CLAUDE.md que consume contexto en cada sesión.
Regla general: los datos necesarios en cada sesión se incluyen en CLAUDE.md, mientras que los procedimientos y el material de referencia se convierten en habilidades.
Patrón 3: subagentes para exploración aislada
Incluso con capas y habilidades, la exploración resulta excesiva. La pregunta “¿Cómo funciona la autenticación?” puede convertirse en 40 lecturas de archivos a medida que el agente sigue los pasos de inicio de sesión, almacenamiento de sesiones, actualización de tokens y cierre de sesión, y todo ese texto permanece en la conversación principal después de responderse la pregunta, contaminando el resto de la sesión.
Un subagente funciona en su propia ventana de contexto, realiza la exploración y solo envía un resumen. Las lecturas de archivos y los resultados de grep quedan en su contexto. El hilo principal recibe el resumen, lo que mantiene la conversación limpia. Una solicitud de investigación podría verse así:
Use a subagent to investigate how our authentication system handles
session timeout and token refresh. Report back what files are involved
and how the flow works.
El subagente lee lo que necesita, sigue el flujo y presenta un informe, de modo que la conversación principal recibe una explicación clara de unas 200 palabras en lugar del contenido de 40 archivos. Los subagentes también ayudan con la revisión después de una implementación:
Use a subagent to review the session timeout fix for edge cases
and consistency with our existing auth patterns.
El subagente encargado de la revisión ve la diferencia de forma directa, sin el razonamiento que generó el cambio.
Una precaución: los resúmenes pierden detalles. Si necesita números de línea exactos o extractos de código, es posible que el agente principal tenga que volver a leer los archivos que el subagente ya ha visto. Mientras tiene lugar la exploración en otro lugar, los subagentes se ocupan del hilo principal de implementación.
Patrón 4: mantener los archivos generados y adquiridos sin leer
Por defecto, todo lo que está listado en .gitignore se omite, lo que hace que node_modules/, dist/ y build/ queden fuera de los resultados de búsqueda. Sin embargo, algunos códigos generados o adquiridos sí se guardan: definiciones de protobuf compiladas, SDKs adquiridos y tipos GraphQL generados. El agente abrirá gustosamente estos archivos si la búsqueda los muestra, aunque nunca debería ser necesario.
Las reglas de denegación de permisos impiden que se lean los patrones de archivos seleccionados. La estructura general se muestra a continuación, y la sintaxis exacta debe verificarse en la documentación correspondiente a su versión de Claude Code:
# packages/api/.claude/settings.json
{
"permissions": {
"deny": [
"Read(./**/dist/**)",
"Read(./**/build/**)",
"Read(./**/*.generated.*)",
"Read(./vendor/**)"
]
}
}
Cuando una búsqueda de “GraphQL resolvers” encuentra ahora un archivo de esquema generado, el resultado aparece, pero la herramienta de lectura responde con “permiso denegado”, y el agente suele preguntar dónde se encuentra la fuente real. Un efecto secundario importante: las mismas reglas impiden las lecturas que se solicitan manualmente en la ventana de chat. Si se le pide que abra src/schema.generated.ts, se le rechaza la solicitud; hay que eliminar temporalmente esa regla. La ventaja es que el ruido generado no se incluye en el contexto y el agente se orienta hacia la fuente real.
Patrón 5: árboles de trabajo escasos
Claude Code puede crear árboles de trabajo de git para tareas paralelas o aisladas, y por defecto cada uno descarga todo el repositorio. En un monorepo eso es un desperdicio, ya que una corrección en la API no necesita el frontend.
El mecanismo sparse-checkout de Git escribe únicamente los directorios seleccionados en el disco y deja el resto en el índice; la configuración worktree.sparsePaths se basa en este principio. Verifique la clave de configuración en la documentación correspondiente a su versión:
# packages/api/.claude/settings.json
{
"worktree": {
"sparsePaths": [
".claude",
"packages/api",
"packages/shared"
]
}
}
Los worktrees contienen únicamente .claude/, packages/api/ y packages/shared/, además de archivos raíz como package.json y tsconfig.base.json. Son más rápidos de crear y ocupan menos espacio en disco. Si el agente intenta leer un archivo que no está desempaquetado, git genera un error y el agente suele preguntar qué directorios existen. Sparse-checkout requiere git 2.25 o una versión más reciente, lo cual se puede confirmar con git --version.
Patrón 6: inteligencia de código en lugar de escaneo de archivos
Encontrar dónde está definida o utilizada una función en una gran base de código implica muchas llamadas a grep y lecturas de archivos, ya que el agente examina los resultados, abre los candidatos posibles y sigue buscando.
Los complementos de inteligencia de código conectan al agente con un servidor de lenguaje. En lugar de escanear, pregunta dónde está definido refreshAuthToken y recibe de inmediato el archivo y el número de línea. Para proyectos de TypeScript, el complemento oficial LSP se instala de esta manera, aunque la sintaxis de instalación varía según la versión, así que consulte la documentación:
/plugin install typescript-lsp@claude-plugins-official
Pregunte dónde se utiliza refreshAuthToken y el agente consulta al servidor de lenguaje y recibe como respuesta:
src/middleware/auth.ts:47
src/routes/users.ts:103
src/__tests__/auth.test.ts:22
No fue necesario leer ningún archivo, por lo que las respuestas son más rápidas y el contexto se mantiene limpio. El complemento también detecta errores de tipo después de las ediciones sin ejecutar tsc, de modo que los errores se manifiestan de inmediato. Funciona bien junto con las reglas de denegación: estas bloquean el ruido, y la inteligencia de código reduce la necesidad de analizar lo que queda.
Los seis juntos
Con cada patrón aplicado, la corrección original del error se realiza más o menos de la siguiente manera:
- Se inicia Claude desde
packages/api/. - Se carga
CLAUDE.mdraíz (40 líneas) y el archivo de la API (50 líneas). - Se le pide que corrija el error de tiempo de espera de sesión en el middleware de autenticación.
- Se envía un subagente para investigar el flujo de autenticación, el cual lee aproximadamente 20 archivos en su propio contexto.
- El subagente informa que la lógica del tiempo de espera se encuentra en
src/middleware/auth.tsen la línea 47 y que se utiliza el operador de comparación incorrecto. - El agente principal lee ese archivo, corrige el error, escribe una prueba y la ejecuta, modificando unos cuatro archivos en el contexto principal.
En esta ilustración, la situación anterior y posterior se presentan como 147 lecturas de archivos, tres intentos fallidos y 35 minutos, frente a unas cuatro lecturas, un intento exitoso y 6 minutos. Considérenlo como la anécdota de un desarrollador y no como un punto de referencia, pero lo importante es la dirección: la estructura eliminó los obstáculos entre la pregunta y la respuesta.
Elegir lo que necesitas
No todo proyecto necesita los seis. Un único repositorio con 50 archivos no requiere archivos en capas ni árboles de trabajo dispersos. Una vez que la base de código supera lo que una persona puede recordar, estos elementos comienzan a ser útiles:
- Archivos
CLAUDE.mden capas son útiles cuando se desplaza más allá de secciones irrelevantes en el archivo raíz, o cuando los compañeros de equipo preguntan por qué el archivo de API contiene reglas de frontend. - Habilidades son adecuadas cuando una sección de
CLAUDE.mdse ha convertido en un procedimiento o lista de verificación, o cuando se siguen pegando las mismas instrucciones en el chat. - Subagentes son útiles cuando una investigación termina indicando “lea estos 30 archivos”, o cuando se desea una revisión sin prejuicios.
- Reglas de denegación son apropiadas cuando el agente sigue abriendo código generado o adquirido a pesar de la existencia de
.gitignore. - Árboles de trabajo escasos son adecuados cuando un monorepo tarda más de 10 segundos en ser desplegado, o cuando se crean muchos árboles de trabajo en paralelo.
El objetivo no es aplicar todo de forma ciega. Se trata de darse cuenta cuando el contexto se vuelve caótico y elegir el patrón que lo limpia. Otras estructuras también pueden funcionar para grandes bases de código, pero estos seis convierten a un agente que lee todo en uno que lee los archivos adecuados, escribe la corrección y se detiene.