CLAUDE.md: buenas prácticas, la guía completa
Qué va en CLAUDE.md, qué recortar, dónde vive cada archivo y cuánto te cuesta. Más la trampa del import que metió 70k tokens en cada sesión de un proyecto de 13 apps.
CLAUDE.md no es documentación. Es la parte de tu context window que ya gastaste antes de escribir una sola palabra.
Claude Code empieza cada sesión sin ningún recuerdo de tu proyecto. CLAUDE.md es la forma de arreglarlo: un archivo Markdown que Claude Code lee al inicio de cada sesión y mantiene en contexto hasta que la sesión termina. Escríbelo bien y Claude deja de repetir los mismos errores. Escríbelo mal y lo pagas en cada request, en tokens y en instrucciones que Claude sigue a medias.
La mayoría de los consejos sobre CLAUDE.md son un template. Esta guía son las decisiones: qué merece una línea, qué se recorta, en qué archivo va cada instrucción y cómo comprobar cuánto estás pagando de verdad. Aprendí la parte cara en una fintech de 13 apps que construí en 70 días, donde un CLAUDE.md que parecía liviano cargaba casi 70.000 tokens en cada sesión.
CLAUDE.md es contexto, no configuración
Si te equivocas en esto, nada más en el archivo funciona. La documentación de Anthropic lo dice sin vueltas: Claude “los trata como contexto, no como configuración obligatoria”. Claude lee tu CLAUDE.md como un ingeniero nuevo lee el documento de onboarding. Casi siempre lo sigue. No siempre. Y cuanto más contiene el archivo, menos se sigue.
Eso divide cada instrucción en dos tipos. La que necesita criterio (“filtra cada query por comercio”) va en CLAUDE.md, porque solo alguien que lee puede aplicarla. La que nunca se puede romper (“nunca ejecutes kubectl apply”) no tiene lugar en un archivo que Claude puede leer en diagonal. Va en un hook o en un linter, donde el chequeo corre aunque Claude no se acuerde de la regla. Más sobre eso abajo.
El CLAUDE.md de la raíz del proyecto también sobrevive a la compactación. Después de un /compact, Claude Code lo vuelve a leer del disco y lo inyecta otra vez. Todo lo demás que dijiste en el chat puede terminar resumido. El archivo se queda.
Dónde vive CLAUDE.md decide cuándo lo pagas
No hay un solo CLAUDE.md. Hay varios, y cargan en momentos distintos. Los archivos de tu directorio de trabajo y de cada carpeta por encima cargan al iniciar y se apilan, desde la raíz del sistema de archivos hasta donde abriste Claude, así que el más cercano se lee al final. Nada sobrescribe nada. Todo se concatena, y si dos archivos se contradicen, la documentación advierte que Claude “puede elegir uno arbitrariamente”.
Todos los lugares donde busca Claude Code
| Archivo | Cuándo carga | Úsalo para |
|---|---|---|
| ~/.claude/CLAUDE.md | Cada sesión, cada proyecto | Tus hábitos: las herramientas que usas, cómo te gustan los commits |
| ./CLAUDE.md o ./.claude/CLAUDE.md | Cada sesión en este repo | Las reglas del equipo. Va al repo junto con el código |
| ./CLAUDE.local.md | Cada sesión en este repo, solo para ti | Tus URLs de sandbox y datos de prueba. Ponlo en el .gitignore |
| subcarpeta/CLAUDE.md | Cuando Claude abre un archivo ahí con Read, Edit o Write | Reglas de un paquete de un monorepo |
| .claude/rules/*.md | Al iniciar, como el archivo del proyecto | Dividir un archivo grande por tema. Mismo costo |
| .claude/rules/*.md con paths: | Cuando Claude abre un archivo que coincide con Read, Edit o Write | Reglas que solo importan para algunos archivos |
| Archivo de política administrada | Cada sesión, para todos en la máquina | Reglas de toda la empresa, definidas por el equipo de TI |
Vuelve a leer la segunda columna. Todo lo que carga al iniciar te cuesta en cada sesión, toque la tarea eso o no. Todo lo que carga bajo demanda solo cuesta cuando el trabajo llega ahí. La buena higiene de CLAUDE.md es, sobre todo, mover líneas del primer grupo al segundo. Un detalle: los archivos bajo demanda cargan cuando Claude abre un archivo con sus herramientas Read, Edit o Write. Un grep por Bash no cuenta, y suele ser la razón de que una regla “no haya cargado”.
El import que parecía un link
En la fintech, mi CLAUDE.md tenía 124 líneas. Un resumen del proyecto, los comandos, once reglas críticas y después un set prolijo de tablas que apuntaban a veinte archivos por tema: roles de auth, schema de la base de datos, design tokens, componentes de UI, convenciones de testing, infraestructura. Parecía un índice.
No era un índice. Cada entrada estaba escrita como @.claude/ui/components.md, y en un CLAUDE.md la @ es un import. La documentación es clara sobre lo que eso significa: “Los archivos importados se expanden y se cargan en el contexto al iniciar, junto con el CLAUDE.md que los referencia.” Los imports pueden anidarse hasta cuatro niveles.
Lo que de verdad cargaba ese archivo de 124 líneas
- 124
- líneasen el propio CLAUDE.md
- 20
- importsescritos como @path en tablas
- ~175 KB
- cargadosal inicio de cada sesión
- 69,8k
- tokensmedidos con /context
Setenta mil tokens es más de un tercio de una context window de 200k, gastados antes del primer mensaje. Lo irónico es que yo ya había escrito la regla contra esto. Mi guía de AGENTS.md dice que hay que apuntar a la documentación detallada en lugar de pegarla en el archivo. Seguí la regla en espíritu y la rompí en la sintaxis.
El arreglo es un carácter. Otra vez la documentación: “El parsing de imports ignora los code spans de Markdown.” Pon la ruta entre backticks y deja de ser un import. Se vuelve texto que Claude puede decidir abrir.
@.claude/ui/components.md
- 01Un import
- 02Expandido en el contexto al iniciar
- 03Se paga en cada sesión, en cada tarea
- 04Se anida hasta cuatro niveles
- 05Siempre visible, así que siempre diluye
`.claude/ui/components.md`
- 01Un puntero
- 02Texto plano en el archivo
- 03Se paga solo si Claude lo abre
- 04Claude decide cuándo es relevante
- 05Se puede pasar por alto, así que no sirve para reglas obligatorias
La última fila importa. Un puntero solo funciona si Claude decide abrirlo. Para una regla que tiene que aplicarse cada vez que Claude toca ciertos archivos, un puntero es demasiado débil y un import es demasiado caro. Justo para eso existen las rules con alcance por path.
Pon cada instrucción donde de verdad funciona
Cuando dejas de pensar en CLAUDE.md como el lugar para todo, la pregunta para cada línea pasa a ser: ¿cuál es el mecanismo más barato que igual hace que esto pase? Hay seis.
Dónde va cada tipo de instrucción
| La instrucción es… | Ponla en | Ejemplo de la fintech |
|---|---|---|
| Válida en todas partes y necesita criterio | CLAUDE.md | Los datos de un comercio nunca llegan a otro |
| Válida solo para algunos archivos | .claude/rules/ con paths: | Design tokens, solo cuando hay un archivo de frontend abierto |
| Un procedimiento de varios pasos | Una skill | Crear una migration, correrla, agregar el test |
| Algo que nunca puede pasar | Un hook o un linter | Nunca ejecutar kubectl apply en infra gestionada por Terraform |
| Válida solo para ti | CLAUDE.local.md o ~/.claude/CLAUDE.md | Tus hostnames HTTPS locales |
| Algo que Claude aprendió de una corrección | Auto memory | Deja que Claude lo escriba. Su índice carga hasta 200 líneas o 25 KB |
Las skills merecen una frase más, porque son el mejor negocio de la lista. El cuerpo de una skill solo carga cuando la invocas o cuando Claude decide que encaja con la tarea. Un procedimiento de deploy de veinte pasos en CLAUDE.md te cuesta en cada sesión. El mismo procedimiento como skill te cuesta el día del deploy. En la fintech, ocho comandos del proyecto estaban en cada sesión como skills, a unos 20 tokens cada uno, hasta que alguien llamaba a uno. La guía de skill vs prompt vs memoria profundiza en esa división.
Las mayúsculas no imponen nada
Mi archivo tenía once reglas con la forma “NUNCA hagas X” y “SIEMPRE haz Y”. Gritarlas no cambió lo que eran: contexto que Claude lee y casi siempre sigue. Tres de ellas ni siquiera necesitaban el criterio de Claude. Necesitaban una máquina que dijera que no.
“NUNCA desactives reglas de ESLint” es una línea de config de ESLint: linterOptions: { noInlineConfig: true }, y todo comentario de disable inline deja de funcionar. “NUNCA uses valores arbitrarios de Tailwind” también es una regla de lint. Este sitio corre exactamente esa regla, y pnpm verify falla con p-[13px]. Y “NUNCA ejecutes kubectl apply” es un hook PreToolUse, que corre antes de que Claude ejecute un comando y puede bloquearlo:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command' | grep -qE 'kubectl.* apply' && { echo 'Infra is managed by Terraform. Use terraform apply.' >&2; exit 2; } || exit 0"
}
]
}
]
}
}
El exit code 2 bloquea la llamada y le devuelve el mensaje a Claude, que entiende el motivo y se pasa a Terraform. Ponlo en .claude/settings.json y la regla se cumple también el día en que Claude lee tu archivo en diagonal. Una regla que se puede imponer se debe imponer. CLAUDE.md es para las que no.
Qué merece una línea en CLAUDE.md
La documentación trae la mejor prueba que vi para saber cuándo agregar una línea. Agrégala cuando Claude comete el mismo error por segunda vez, cuando el code review atrapa algo que Claude debería haber sabido, cuando escribes la misma corrección que escribiste la sesión anterior, o cuando un compañero nuevo necesitaría el mismo contexto. Lo que el archivo no debería guardar es la historia alrededor del código: decisiones, personas, lo que se resolvió en cada reunión. Eso crece sin límite, y su lugar es un LLM wiki que el agente lee cuando lo necesita. Si no pasó nada de eso, la línea es una apuesta.
Qué va en el CLAUDE.md del proyecto
- Obligatorio:Dos o tres líneas sobre qué es el proyectoEl stack y la forma del repo. Nada de pitch, nada de ensayo de arquitectura.
- Obligatorio:Los comandos que Claude debe correrSetup, servidor de desarrollo, el comando que lo verifica todo. Claude no los puede adivinar.
- Obligatorio:Convenciones que un buen ingeniero igual haría malDónde se traducen los errores de la API. Qué paquete se encarga del auth. Todo lo que no es estándar.
- Obligatorio:Reglas que necesitan criterioAislamiento de datos, transacciones, lo que nunca sale del servidor. Escríbelas de forma que se puedan verificar.
- Obligatorio:Punteros entre backticks a la documentación detalladaRutas que Claude abre cuando la tarea las necesita. No @imports.
- Anti-pattern:Reglas que un linter o un hook podría imponerImponlas. Un archivo solo puede pedir.
- Anti-pattern:Referencias largas sobre una parte del códigoMuévelas a una rule con alcance por path, para que carguen junto con los archivos que describen.
- Anti-pattern:Consejos genéricos como 'escribe código limpio'Claude ya lo hace. La línea cuesta tokens y no cambia nada.
Escribe cada línea de forma que puedas verificar si Claude la siguió. Los ejemplos de la propia documentación son buenos: “Usa indentación de 2 espacios” en lugar de “Formatea bien el código”, “Corre npm test antes de hacer commit” en lugar de “Prueba tus cambios”. Si tú no puedes saber si una regla se rompió, Claude tampoco.
No dejes que /init lo escriba por ti
/init lee tu código y arma un borrador de CLAUDE.md. Es un buen punto de partida y un mal archivo final, y ahora hay datos que explican por qué. Evaluating AGENTS.md, un estudio de 2026 con agentes de código sobre issues reales de GitHub, encontró que los archivos de contexto en general no subieron la tasa de éxito y sumaron más de un 20% al costo de inferencia en promedio. Los archivos generados por un LLM rindieron un poco peor que no tener archivo. Los que escribieron los propios desarrolladores del repo rindieron apenas un poco mejor. Lo que ayudó fue lo que no es estándar, las convenciones que un modelo no adivinaría. El resumen del repositorio, justo lo que mejor hace /init, no ayudó.
Así que corre /init y después recorta. La documentación dice que hay que refinarlo “con instrucciones que Claude no descubriría por su cuenta”. Todo lo que Claude podría descubrir leyendo el código es una línea que pagas para repetir. La guía de AGENTS.md tiene la prueba de poda que uso: si quitar una línea no causaría ningún error, quítala.
Un ejemplo de CLAUDE.md: el archivo de la fintech, reescrito
Este es el archivo de 124 líneas como lo escribiría hoy. Mismo proyecto, las mismas reglas que importan, ningún import.
# Payment Platform
PIX payment gateway with on-chain settlement on the Liquid Network.
Turborepo + pnpm. Fastify 5, Drizzle, Zod. Next.js, Tailwind. PostgreSQL, Redis.
## Commands
- `pnpm setup:dev`: Docker, databases, migrations, seed
- `pnpm dev:https`: dev servers behind Caddy
- `pnpm verify`: format, lint, types, tests. Run it before every commit.
## Layout
- `apps/<domain>/api`: Fastify APIs for auth, exchange and payment
- `apps/<domain>/<app>`: Next.js frontends
- `packages/ui`, `packages/i18n`, `packages/auth`: shared code
## Rules that need judgment
- One merchant's data never reaches another merchant. Scope every query by merchant.
- Validate on the server. Never trust the client.
- Writes that touch more than one table run in a transaction.
- APIs return English messages plus a code from `ERROR_CODES`.
Frontends translate with `getErrorMessage(code)`.
## Read when the task needs it
- Local setup: `.claude/setup/local-development.md`
- Git workflow: `.claude/git/workflow.md`
- Infrastructure: `infrastructure/README.md`
Menos de 40 líneas. El resto de los veinte archivos no desapareció. Se mueven a donde cargan junto con el trabajo que los necesita.
Adónde iría el resto de la documentación
- .claude/
- CLAUDE.mdsiempre// el archivo de arriba
- settings.jsonobligatorio// el hook de kubectl
- rules/
- frontend.mdpaths// frontends y packages/ui: tokens, componentes, UX
- database.mdpaths// apps/*/api/src/db/**: schema, migrations, entidades
- auth.mdpaths// APIs y packages/auth: roles, scopes, permisos de rutas
- testing.mdpaths// carpetas de test y e2e: convenciones de testing
- pricing.mdpaths// apps/exchange/**: el motor de precios
- skills/
- new-migration/bajo demanda// el procedimiento, cargado cuando se usa
Una rule con alcance por path es un archivo Markdown normal con un campo más. Esta solo carga cuando Claude lee o edita un archivo dentro de una carpeta de base de datos:
---
paths:
- "apps/*/api/src/db/**"
---
# Database
- Every table: a prefixed ID (`mer_`, `cus_`, `ord_`), `createdAt`, `updatedAt`,
and an index on every foreign key.
- Tables are snake_case plural. Columns are snake_case.
- Relations live in a separate `relations.ts`.
La rule de frontend sigue siendo grande. Solo la referencia de componentes de UI ocupa 10,8k tokens. Pero ahora carga cuando Claude abre un componente de React, y se queda afuera cuando arregla un retry de un webhook. Ese es el trato: el costo no desapareció, se movió al momento en que compra algo.
Mide lo que cargas, no adivines
Mi primer cálculo para la fintech, a partir del tamaño de los archivos, fue de 45.000 tokens. /context en el repo viejo dijo 69.800. El cálculo se quedó corto por un tercio, hacia el lado que te deja bien parado. Mídelo. Claude Code ya trae las herramientas.
Dentro de una sesión de Claude Code
Ver cada archivo de memoria que cargó y cuántos tokens ocupa
/contextAbrir y editar los archivos de memoria que encontró Claude Code
/memoryEncontrar instrucciones desactualizadas, faltantes o contradictorias (v2.1.283+)
/doctor prompt-audit
Corre /context en tu repo principal hoy. Si los archivos de memoria ocupan más de unos pocos miles de tokens, abre el más grande y pregúntate, línea por línea, qué sesión lo necesitó de verdad.
CLAUDE.md vs AGENTS.md: con un archivo alcanza
Si tu repo lo tocan varios agentes, quizás ni necesites un CLAUDE.md. Desde la v2.1.277, Claude Code lee AGENTS.md como instrucciones del proyecto cuando no hay CLAUDE.md ni CLAUDE.local.md en tu carpeta o por encima, y Codex, Cursor, Copilot y otros también lo leen. Ojo con el segundo archivo: agregar un CLAUDE.local.md hace que Claude deje de leer tu AGENTS.md sin avisar. Si existen los dos tipos de archivo, Claude Code lee por defecto solo los CLAUDE.md, salvo que configures Project instructions en claude-md-and-agents-md desde /config.
La configuración limpia es un AGENTS.md compartido, más un CLAUDE.md que empieza con @AGENTS.md solo cuando Claude necesita líneas que los otros agentes no deberían ver. Ese import está bien, porque quieres el archivo completo en cada sesión. La guía de AGENTS.md tiene la tabla completa de qué agente lee qué.
Mantenlo al día o bórralo
Un CLAUDE.md equivocado es peor que ninguno, porque Claude lo sigue con confianza. Cambia el archivo en el mismo pull request que cambia la convención. Si pasas la base de datos de Prisma a Drizzle, la línea sobre Prisma muere en ese commit, y no tres meses después, cuando Claude escriba una query de Prisma.
Cada pocas semanas, corre /context, mira el número y recorta. El mejor CLAUDE.md que tengo es el de este sitio: un AGENTS.md corto en la raíz y unas pocas rules en .claude/rules/ que solo cargan para los archivos que describen. /context le da a su memoria de proyecto 2.000 tokens. La de la fintech era de 69.800. No está completo. Es barato, y es correcto.
CLAUDE.md, respuestas rápidas
¿Dónde va CLAUDE.md?
Para un proyecto, en la raíz del repo como CLAUDE.md o en .claude/CLAUDE.md, versionado junto con el código. Para reglas que aplican a todos tus proyectos, en ~/.claude/CLAUDE.md. Para notas personales sobre un proyecto, en CLAUDE.local.md en la raíz del repo, agregado al .gitignore. Un CLAUDE.md dentro de una subcarpeta solo carga cuando Claude trabaja en archivos de esa carpeta.
¿Qué tan largo debería ser CLAUDE.md?
La documentación de Anthropic recomienda quedarse por debajo de 200 líneas por archivo, porque los archivos más largos ocupan más contexto y reducen la adherencia.
Cuenta también los imports. Un archivo de 124 líneas con veinte @imports no es un archivo de 124 líneas. Corre /context para ver el tamaño real.
¿Claude lee CLAUDE.md cada vez?
Sí, al inicio de cada sesión, y se queda en contexto durante toda la sesión. Después de /compact, Claude Code vuelve a leer del disco el CLAUDE.md de la raíz del proyecto. Los CLAUDE.md en subcarpetas y las rules con alcance por path solo cargan cuando Claude lee o edita un archivo que coincide.
¿CLAUDE.md puede importar otros archivos?
Sí, con @ruta/al/archivo. Los archivos importados cargan al iniciar, así que cuestan lo mismo que pegarlos, y pueden anidarse hasta cuatro niveles. Para mencionar una ruta sin importarla, ponla entre backticks.
¿Por qué Claude ignora mi CLAUDE.md?
Primero verifica que haya cargado: /context lista cada archivo de memoria cargado al iniciar. Después revisa su tamaño y si dos archivos se contradicen, porque Claude puede elegir uno arbitrariamente.
Si una regla nunca se puede romper, deja de pedirla en un archivo. Usa un hook PreToolUse o un linter que la bloquee.
¿Cómo uso CLAUDE.md en un monorepo?
Deja en el archivo de la raíz las reglas que comparten todos los paquetes. Pon las reglas de cada paquete en un CLAUDE.md dentro de su carpeta o en una rule con alcance por path, para que solo carguen cuando Claude trabaja ahí. Si los archivos de otros equipos cargan en tus sesiones, sáltalos con claudeMdExcludes en .claude/settings.local.json.
¿Cuál es la diferencia entre CLAUDE.md y CLAUDE.local.md?
CLAUDE.md es del equipo y va al control de versiones. CLAUDE.local.md es tuyo, vive al lado y va al .gitignore. Los dos cargan al iniciar, y el local se agrega después del compartido.
¿Debo hacer commit de CLAUDE.md?
Haz commit del CLAUDE.md del proyecto. Describe cómo trabajar en el código y debería cambiar en los mismos pull requests que cambian el código. Deja lo personal en CLAUDE.local.md y lo secreto fuera de los dos.
La newsletter
Don’t Code, Specify. Cada semana, agentes de IA en producción de verdad. Sin hype: lo que funcionó y lo que se rompió.
Suscríbete en Substack (se abre en una pestaña nueva)