Módulo 7: Remote Control y CLAUDE.md para Equipos
4. CLAUDE.md para Equipos — Estructura, Merge y Enforcement
4. CLAUDE.md para Equipos — Estructura, Merge y Enforcement
Descripción
Hasta ahora, CLAUDE.md ha sido tu documento personal. Lo escribes para tu proyecto, con tus preferencias, y Claude Code lo sigue. Funciona bien cuando eres el único developer. Pero cuando tres personas trabajan en el mismo repo, cada uno con su propio CLAUDE.md (o sin uno), el resultado es caos: archivos con tabs y spaces mezclados, funciones con y sin type hints, tests en diferentes frameworks, imports organizados de tres formas distintas. El código se ve como si lo hubieran escrito tres personas diferentes — porque tres agentes con reglas diferentes lo escribieron.
CLAUDE.md para equipos resuelve esto. Es un documento compartido que se commitea al repositorio y establece las reglas que todos los agentes respetan: convenciones de código, decisiones arquitectónicas, patrones prohibidos, requisitos de testing. Cuando alguien clona el repo, su Claude Code adopta automáticamente las reglas del equipo. No hay reunión de "alineación de estilo." No hay PR con 47 comentarios sobre formatting.
Esta cápsula cubre la estructura de un CLAUDE.md de equipo production-ready, la jerarquía de merge que permite reglas compartidas con overrides personales, el enforcement con hooks que validan compliance, y un template completo que puedes adaptar a tu equipo.
CLAUDE.md como Constitución
La metáfora correcta
No pienses en CLAUDE.md como un archivo de configuración. Un config file dice "usa port 3000." Una constitución dice "estos son nuestros valores, estas son las reglas, estas son las consecuencias de romperlas." Un CLAUDE.md de equipo es una constitución:
Config file: Constitución (CLAUDE.md):
───────────── ─────────────────────────
port: 3000 Usamos TypeScript strict
debug: true No usamos any - nunca
log_level: info Tests unitarios para toda lógica de negocio
Cobertura mínima: 80%
Conventional commits obligatorio
No console.log en producción
Imports ordenados: stdlib > third-party > local
La diferencia: un config file describe parámetros técnicos. Una constitución describe expectativas de comportamiento. CLAUDE.md dice cómo debe comportarse el agente, qué patrones seguir, qué nunca hacer, y qué nivel de calidad se espera.
Por qué funciona como estándar
- Se commitea al repo → Todos lo tienen automáticamente
- Es declarativo → "qué hacer", no "cómo configurar"
- Es evolutivo → Se actualiza con PRs como cualquier otro archivo
- Es enforceable → Hooks pueden validar compliance
- Escala sin reuniones → Nuevo miembro clona, adopta, produce consistente
Estructura de un CLAUDE.md de Equipo
Secciones recomendadas
Un CLAUDE.md de equipo efectivo tiene estas secciones:
# CLAUDE.md — [Nombre del Proyecto]
## Contexto del Proyecto
Breve descripción de qué hace el proyecto, stack tecnológico,
y arquitectura de alto nivel.
## Convenciones de Código
Reglas de estilo, naming, formatting que todo código debe seguir.
## Arquitectura
Decisiones arquitectónicas vigentes. Patrones que usamos y por qué.
## Patrones Prohibidos
Lo que NUNCA debe hacerse. Anti-patterns específicos del proyecto.
## Testing
Requisitos de testing. Frameworks, cobertura, tipos de tests requeridos.
## Git y Workflow
Convenciones de commits, branches, PRs.
## Seguridad
Reglas de seguridad. Qué nunca exponer, cómo manejar secretos.
## Dependencias
Políticas de dependencias. Cuándo agregar, cómo evaluar, qué evitar.
Template production-ready
Este es un CLAUDE.md que puedes adaptar para tu equipo:
# CLAUDE.md — TaskFlow API
## Contexto del Proyecto
TaskFlow es una API REST para gestión de tareas colaborativas.
- Backend: Python 3.12 + FastAPI
- Base de datos: PostgreSQL 16 + SQLAlchemy 2.0 (async)
- Cache: Redis 7
- Auth: JWT con refresh tokens
- Tests: pytest + httpx
- Deploy: Docker + GitHub Actions
## Convenciones de Código
### Python
- Type hints obligatorios en TODAS las funciones (parámetros y retorno)
- Docstrings en formato Google para funciones públicas
- Nombres de variables descriptivos, no abreviaciones (user_repository, NO ur)
- Funciones de máximo 30 líneas. Si son más largas, extraer subfunciones
- Usar pathlib en lugar de os.path
- f-strings para interpolación, no .format() ni %
### Naming
- Archivos: snake_case (user_service.py)
- Clases: PascalCase (UserService)
- Funciones y variables: snake_case (get_user_by_id)
- Constantes: UPPER_SNAKE_CASE (MAX_RETRY_COUNT)
- Endpoints: kebab-case en URLs (/api/v1/user-tasks)
### Imports
Orden estricto, separado por líneas en blanco:
1. stdlib (os, sys, typing, datetime)
2. third-party (fastapi, sqlalchemy, pydantic)
3. local (app.models, app.services)
### Formatting
- ruff como linter y formatter
- Configuración en pyproject.toml (ya incluida en el repo)
- Ejecutar `ruff check --fix .` antes de cada commit
## Arquitectura
### Estructura de directorios
```text
app/
├── api/ # Routers de FastAPI (endpoints)
├── core/ # Configuración, seguridad, excepciones
├── models/ # SQLAlchemy models
├── schemas/ # Pydantic schemas (request/response)
├── services/ # Lógica de negocio
├── repositories/ # Acceso a datos (queries)
└── utils/ # Utilidades compartidas
Capas
- Routers llaman a Services
- Services llaman a Repositories
- Repositories llaman a la DB
- NUNCA un Router accede directamente a la DB
- NUNCA un Repository contiene lógica de negocio
Patrones
- Repository Pattern para acceso a datos
- Dependency Injection via FastAPI Depends()
- Pydantic para validación de entrada Y salida
- Async/await para todas las operaciones de I/O
Patrones Prohibidos
- NO usar
anytype en Python (typing.Any) sin justificación en comentario - NO queries SQL raw — siempre usar SQLAlchemy ORM
- NO print() para logging — usar el logger configurado en app.core.logging
- NO hardcodear URLs, puertos, o credenciales — usar variables de entorno
- NO importar desde tests/ en código de producción
- NO commit de archivos .env, credenciales, o tokens
- NO usar datetime.now() — usar datetime.now(UTC) para consistencia
- NO modificar archivos en alembic/versions/ manualmente — usar alembic revision
Testing
Requisitos
- Cobertura mínima: 80% (enforced en CI)
- Todo endpoint tiene al menos 1 test de happy path y 1 de error
- Toda función de servicio tiene test unitario
- Tests de integración para flujos completos (auth → create → read)
Convenciones
- Archivos de test: test_[module].py en tests/
- Fixtures compartidas en conftest.py
- Factory pattern para crear objetos de test
- No usar datos hardcodeados — usar factories o fixtures
- Cada test es independiente — no depende del orden de ejecución
Frameworks
- pytest como runner
- httpx.AsyncClient para tests de endpoints
- pytest-asyncio para tests async
- factory-boy para factories de modelos
Git y Workflow
Commits
- Conventional Commits: tipo(scope): descripción
- Tipos: feat, fix, refactor, test, docs, chore, ci
- Ejemplo: feat(auth): add JWT refresh token endpoint
- Mensajes en inglés, imperativo, sin punto final
- Máximo 72 caracteres en la primera línea
Branches
- main: producción, siempre deployable
- develop: integración
- feature/xxx: nuevas features
- fix/xxx: bug fixes
- NUNCA force push a main o develop
Pull Requests
- Requiere 1 review mínimo
- CI debe pasar (tests + lint)
- Descripción incluye: qué, por qué, y cómo testear
Seguridad
- NUNCA commitear .env, API keys, tokens, o passwords
- Variables sensibles van en variables de entorno
- Passwords se hashean con bcrypt (nunca en texto plano)
- JWT secrets mínimo 256 bits
- Rate limiting activo en todos los endpoints públicos
- Input validation con Pydantic en TODOS los endpoints
- No exponer stack traces en respuestas de error (usar excepciones custom)
Dependencias
- Evaluar antes de agregar: ¿vale la complejidad extra?
- Preferir stdlib sobre third-party cuando es viable
- Fijar versiones en requirements.txt (==, no >=)
- Auditar dependencias con
pip-auditmensualmente - No instalar paquetes de desarrollo en producción
### Puntos clave del template
1. **Contexto primero** — El agente necesita entender qué es el proyecto antes de las reglas
2. **Convenciones son específicas** — "Type hints obligatorios" no "intenta usar type hints"
3. **Prohibiciones son absolutas** — "NO" en mayúsculas, sin ambigüedad
4. **Testing es enforceable** — Cobertura mínima cuantificable, no "intenta testear"
5. **Seguridad es no negociable** — Las reglas de seguridad no tienen excepciones
---
## Jerarquía de Merge
### Los tres niveles de CLAUDE.md
Claude Code busca y combina CLAUDE.md de múltiples ubicaciones:
Nivel 1: Global (usuario) ~/.claude/CLAUDE.md → Preferencias personales que aplican a TODOS tus proyectos → Ej: "Siempre responde en español", "Prefiero explicaciones concisas"
Nivel 2: Proyecto (root del repo) ./CLAUDE.md → Estándar del equipo para este proyecto → Se commitea al repo → todos lo comparten → Ej: "TypeScript strict", "Conventional commits"
Nivel 3: Subdirectorio (módulo-específico) ./src/api/CLAUDE.md → Reglas específicas para un módulo del proyecto → Ej: "Endpoints siguen RESTful naming", "Validación con Pydantic"
### Regla de precedencia
**Más específico gana.** Si hay conflicto entre niveles:
Global dice: "Usa tabs" Proyecto dice: "Usa spaces (4)" Subdirectorio dice: "Usa spaces (2)"
Resultado cuando trabajas en ./src/api/: → Usa spaces (2) — subdirectorio gana
Resultado cuando trabajas en ./src/models/: → Usa spaces (4) — proyecto gana (no hay subdirectorio CLAUDE.md)
Resultado en otro proyecto sin CLAUDE.md: → Usa tabs — global gana (único nivel disponible)
### Merge en práctica
Claude Code no reemplaza niveles — los **combina**. Las reglas de todos los niveles aplicables se juntan, y solo se overridean las que explícitamente conflictúan:
Global CLAUDE.md:
- Responde en español
- Prefiere explicaciones concisas
- Usa git conventional commits
Proyecto CLAUDE.md:
- TypeScript strict
- Tests con vitest
- No console.log
Cuando Claude Code trabaja en este proyecto, sigue TODO: ✅ Responde en español (global) ✅ Explicaciones concisas (global) ✅ Conventional commits (global) ✅ TypeScript strict (proyecto) ✅ Tests con vitest (proyecto) ✅ No console.log (proyecto)
### CLAUDE.md en subdirectorios
Para proyectos grandes, puedes tener CLAUDE.md en subdirectorios con reglas específicas del módulo:
my-project/ ├── CLAUDE.md ← Reglas del proyecto ├── src/ │ ├── api/ │ │ ├── CLAUDE.md ← Reglas específicas de la API │ │ └── routes.py │ ├── models/ │ │ ├── CLAUDE.md ← Reglas específicas de modelos │ │ └── user.py │ └── utils/ │ └── helpers.py ← Solo reglas del proyecto (no hay subdir CLAUDE.md)
`src/api/CLAUDE.md`:
```markdown
## API-Specific Rules
- Endpoints use kebab-case URLs
- Every endpoint returns a Pydantic ResponseModel
- Error responses use app.core.exceptions, not raw HTTPException
- New endpoints require OpenAPI documentation
src/models/CLAUDE.md:
## Model-Specific Rules
- Every model has created_at and updated_at timestamps
- Soft delete with is_deleted flag, never hard delete
- Relationships use lazy="selectin" for async compatibility
- Migrations go through alembic, never manual schema changes
Enforcement: Hooks que Validan Compliance
El problema de "reglas sin dientes"
Un CLAUDE.md sin enforcement es una sugerencia. Claude Code lo respeta la mayoría del tiempo, pero los LLMs pueden cometer errores. Enforcement con hooks convierte las sugerencias en reglas:
Sin enforcement:
CLAUDE.md dice "usa type hints" →
Claude olvida en 3 de 20 funciones →
Code review lo detecta (quizás)
Con enforcement:
CLAUDE.md dice "usa type hints" →
Hook PostToolUse ejecuta mypy después de cada edición →
Claude recibe el error y corrige inmediatamente →
100% compliance
Hook: Validar type hints después de edición
scripts/hooks/enforce-type-hints.sh:
#!/bin/bash
INPUT=$(cat -)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty')
if [ -z "$FILE" ] || [ ! -f "$FILE" ]; then
exit 0
fi
if [[ "$FILE" != *.py ]]; then
exit 0
fi
if command -v mypy &> /dev/null; then
MYPY_RESULT=$(mypy "$FILE" --ignore-missing-imports --no-error-summary 2>&1)
MYPY_EXIT=$?
if [ $MYPY_EXIT -ne 0 ]; then
MISSING_HINTS=$(echo "$MYPY_RESULT" | grep -c "missing return type\|no type annotation")
if [ "$MISSING_HINTS" -gt 0 ]; then
echo "CLAUDE.md VIOLATION: Missing type hints in $FILE"
echo "$MYPY_RESULT" | grep "missing return type\|no type annotation" | head -5
exit 1
fi
fi
fi
exit 0
Hook: Validar que no se usen patrones prohibidos
scripts/hooks/enforce-forbidden-patterns.sh:
#!/bin/bash
INPUT=$(cat -)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty')
if [ -z "$FILE" ] || [ ! -f "$FILE" ]; then
exit 0
fi
if [[ "$FILE" == *.py ]]; then
if grep -n "print(" "$FILE" | grep -v "^#" | grep -v "# noqa" > /dev/null 2>&1; then
LINES=$(grep -n "print(" "$FILE" | grep -v "^#" | grep -v "# noqa" | head -5)
echo "CLAUDE.md VIOLATION: print() found in $FILE"
echo "Use logger instead. Found:"
echo "$LINES"
exit 1
fi
if grep -n "datetime.now()" "$FILE" | grep -v "now(UTC)" > /dev/null 2>&1; then
echo "CLAUDE.md VIOLATION: datetime.now() without UTC in $FILE"
echo "Use datetime.now(UTC) for timezone consistency"
exit 1
fi
if grep -n "typing.Any" "$FILE" > /dev/null 2>&1; then
ANY_COUNT=$(grep -c "typing.Any\|from typing import.*Any" "$FILE")
JUSTIFIED=$(grep -c "# justified:" "$FILE")
if [ "$ANY_COUNT" -gt "$JUSTIFIED" ]; then
echo "CLAUDE.md VIOLATION: typing.Any without justification in $FILE"
echo "Add '# justified: reason' comment for each use of Any"
exit 1
fi
fi
fi
if [[ "$FILE" == *.ts ]] || [[ "$FILE" == *.tsx ]]; then
if grep -n "console\.log\|console\.warn\|console\.error" "$FILE" | grep -v "// debug" > /dev/null 2>&1; then
echo "CLAUDE.md VIOLATION: console.log found in $FILE"
echo "Use the project logger instead"
exit 1
fi
if grep -n ": any" "$FILE" | grep -v "// justified" > /dev/null 2>&1; then
echo "CLAUDE.md VIOLATION: 'any' type found in $FILE"
exit 1
fi
fi
exit 0
Hook: Validar convenciones de naming
scripts/hooks/enforce-naming.sh:
#!/bin/bash
INPUT=$(cat -)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty')
if [ -z "$FILE" ] || [ ! -f "$FILE" ]; then
exit 0
fi
BASENAME=$(basename "$FILE")
if [[ "$FILE" == *.py ]]; then
if ! echo "$BASENAME" | grep -qE "^[a-z][a-z0-9_]*\.py$"; then
echo "CLAUDE.md VIOLATION: Python filename must be snake_case"
echo "File: $BASENAME"
echo "Expected: snake_case.py"
exit 1
fi
fi
if [[ "$FILE" == *.ts ]] || [[ "$FILE" == *.tsx ]]; then
if echo "$BASENAME" | grep -qE "^[A-Z]"; then
: # PascalCase components are OK in React
elif ! echo "$BASENAME" | grep -qE "^[a-z][a-z0-9-]*\.(ts|tsx)$"; then
echo "CLAUDE.md VIOLATION: TypeScript filename must be kebab-case"
echo "File: $BASENAME"
exit 1
fi
fi
exit 0
settings.json con todos los enforcement hooks
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "./scripts/hooks/enforce-forbidden-patterns.sh"
},
{
"type": "command",
"command": "./scripts/hooks/enforce-naming.sh"
},
{
"type": "command",
"command": "./scripts/hooks/enforce-type-hints.sh"
}
]
}
]
}
}
Onboarding: Nuevos Miembros Adoptan el Estándar Automáticamente
El flujo ideal
Nuevo developer se une al equipo:
1. git clone repo-url → Obtiene CLAUDE.md + settings.json + hook scripts
2. Abre Claude Code → SessionStart hook verifica entorno
3. Escribe código → Hooks validan compliance en tiempo real
4. Hace commit → Conventional commits, lint pasa
5. Crea PR → CI valida cobertura, linting, types
Resultado: código consistente desde el primer commit.
Sin documentos de 20 páginas. Sin reuniones de "cómo configurar."
Checklist de onboarding con CLAUDE.md
Lo que tu repositorio necesita para onboarding automático:
✅ CLAUDE.md en el root del repo (constitución del equipo)
✅ .claude/settings.json con hooks de enforcement
✅ scripts/hooks/ con todos los scripts de validación
✅ pyproject.toml / .eslintrc configurados (linter del proyecto)
✅ .github/CONTRIBUTING.md con link al CLAUDE.md
✅ Todos los scripts de hooks con chmod +x (ejecutables)
Script de verificación de onboarding
scripts/verify-setup.sh:
#!/bin/bash
echo "=== Verificación de Setup de Equipo ==="
ERRORS=0
if [ ! -f "CLAUDE.md" ]; then
echo "❌ CLAUDE.md no encontrado en el root"
ERRORS=$((ERRORS + 1))
else
echo "✅ CLAUDE.md presente"
fi
if [ ! -f ".claude/settings.json" ]; then
echo "❌ .claude/settings.json no encontrado"
ERRORS=$((ERRORS + 1))
else
if jq -e '.hooks' .claude/settings.json > /dev/null 2>&1; then
echo "✅ settings.json con hooks configurados"
else
echo "⚠️ settings.json sin hooks"
fi
fi
if [ -d "scripts/hooks" ]; then
HOOK_COUNT=$(ls scripts/hooks/*.sh 2>/dev/null | wc -l | tr -d ' ')
EXECUTABLE=$(find scripts/hooks/ -name "*.sh" -perm +111 2>/dev/null | wc -l | tr -d ' ')
echo "✅ $HOOK_COUNT hook scripts ($EXECUTABLE ejecutables)"
if [ "$HOOK_COUNT" -ne "$EXECUTABLE" ]; then
echo "⚠️ Algunos scripts no son ejecutables"
echo " Fix: chmod +x scripts/hooks/*.sh"
fi
else
echo "❌ scripts/hooks/ no encontrado"
ERRORS=$((ERRORS + 1))
fi
if command -v ruff &> /dev/null; then
echo "✅ ruff instalado"
elif command -v npx &> /dev/null && npx eslint --version > /dev/null 2>&1; then
echo "✅ eslint instalado"
else
echo "⚠️ No se encontró linter (ruff o eslint)"
fi
if command -v jq &> /dev/null; then
echo "✅ jq instalado"
else
echo "❌ jq no instalado (requerido por hooks)"
ERRORS=$((ERRORS + 1))
fi
echo ""
if [ $ERRORS -eq 0 ]; then
echo "✅ Setup completo. Estás listo para trabajar."
else
echo "❌ $ERRORS problemas encontrados. Revisa los errores arriba."
fi
exit $ERRORS
Mantenimiento del CLAUDE.md
Cuándo actualizar
| Señal | Acción |
|---|---|
| Nuevo framework adoptado | Agregar a "Convenciones de Código" |
| Bug recurrente en PRs | Agregar a "Patrones Prohibidos" |
| Nueva decisión arquitectónica | Agregar a "Arquitectura" |
| Revisión trimestral | Verificar que todo sigue vigente |
| Nuevo miembro reporta confusión | Clarificar la sección ambigua |
Proceso de actualización
1. Crear branch: git checkout -b update/claude-md-conventions
2. Editar CLAUDE.md
3. Actualizar hooks si las reglas nuevas necesitan enforcement
4. Crear PR con descripción del cambio
5. Review del equipo (al menos 2 approvals para cambios de constitución)
6. Merge a main
7. Todos los miembros obtienen los cambios con git pull
Antipatrones de CLAUDE.md
❌ CLAUDE.md de 500+ líneas
→ Demasiado largo. Claude Code puede perder instrucciones.
→ Mantén < 200 líneas. Usa subdirectory CLAUDE.md para detalles.
❌ Reglas vagas: "Escribe buen código"
→ No actionable. ¿Qué es "bueno"?
→ Específico: "Funciones de máximo 30 líneas"
❌ Reglas que nunca se revisan
→ Se acumulan y contradicen.
→ Revisión trimestral con el equipo.
❌ Reglas sin enforcement
→ Se ignoran gradualmente.
→ Si es importante, agrega un hook que lo valide.
❌ No permitir overrides personales
→ Frustra a developers experimentados.
→ Usa la jerarquía: equipo establece mínimos, personal puede ser más estricto.
Troubleshooting
"Claude Code ignora algunas reglas del CLAUDE.md"
Causa: El CLAUDE.md es demasiado largo o las reglas son ambiguas. Los LLMs pueden perder instrucciones en documentos largos.
Solución: Mantén el CLAUDE.md conciso (< 200 líneas). Prioriza las reglas más importantes al inicio del archivo. Usa enforcement hooks para las reglas críticas:
wc -l CLAUDE.md
# Si > 200, refactoriza: mueve detalles a subdirectory CLAUDE.md
"Conflicto entre CLAUDE.md global y de proyecto"
Causa: Tu CLAUDE.md global dice algo que contradice el del proyecto.
Solución: La regla es clara: proyecto gana sobre global. Si tu global dice "usa tabs" pero el proyecto dice "usa spaces", el proyecto prevalece. Si el conflicto es frecuente, ajusta tu global para ser más genérico:
# ~/.claude/CLAUDE.md (global) — BUENO
Responde en español.
Explicaciones concisas.
Conventional commits.
# ~/.claude/CLAUDE.md (global) — MALO
Usa tabs para indentación. ← Esto conflictuará con proyectos que usan spaces
Siempre usa React. ← No aplica a proyectos Python
Tests con Jest. ← No aplica a todos los proyectos
"Los hooks de enforcement son muy lentos"
Causa: Hooks como mypy o eslint sobre archivos grandes tardan varios segundos, y se ejecutan en cada edición.
Solución: Haz los hooks incrementales — solo verifican el archivo editado, no todo el proyecto:
# MAL: verifica todo el proyecto
mypy src/
# BIEN: verifica solo el archivo editado
mypy "$FILE" --ignore-missing-imports
"Un developer necesita una excepción a la regla"
Causa: Hay un caso legítimo donde la regla no aplica.
Solución: Usa comentarios de excepción que los hooks reconocen:
from typing import Any # justified: third-party lib returns untyped data
print("Debug info") # noqa: debugging, remove before merge
Configura los hooks para respetar estos marcadores (como se mostró en enforce-forbidden-patterns.sh).
"El CLAUDE.md de un subdirectorio contradice al del proyecto"
Causa: Reglas conflictivas entre niveles.
Solución: El subdirectorio gana para archivos dentro de él. Si el conflicto es un error, resolverlo en PR. Si es intencional, documentar por qué:
# src/legacy/CLAUDE.md
## Excepciones para código legacy
- Se permite typing.Any sin justificación (código pre-typing era)
- Cobertura mínima: 50% (no 80% como el resto del proyecto)
- Razón: módulo en proceso de migración, no invertir en tests completos
Comparación: CLAUDE.md Rígido vs Flexible con Overrides
| Aspecto | Rígido (solo proyecto) | Flexible (con overrides personales) |
|---|---|---|
| Setup | Simple: un solo CLAUDE.md | Más complejo: múltiples niveles |
| Consistencia | Máxima: todos siguen exactamente las mismas reglas | Alta: misma base, variaciones controladas |
| Satisfacción del equipo | Puede frustrar a developers experimentados | Alta: respeta preferencias personales |
| Onboarding | Muy simple: clonar = listo | Simple: clonar = base, personalizar = opcional |
| Mantenimiento | Bajo: un archivo | Medio: vigilar que overrides no rompan el estándar |
| Enforcement | Directo: hooks validan contra un solo documento | Necesita entender la jerarquía |
| Ideal para | Equipos pequeños, proyectos críticos | Equipos medianos+, diversidad de experiencia |
| Riesgo | Demasiada rigidez → resistencia | Demasiada flexibilidad → inconsistencia |
Recomendación: Empieza con CLAUDE.md de proyecto (rígido). Cuando el equipo crezca o developers experimenten fricción, introduce overrides personales con la jerarquía de merge. Mantén las reglas de seguridad y arquitectura sin override; permite overrides en preferencias de estilo.
Ejercicios
Ejercicio 1: CLAUDE.md mínimo viable (Fácil)
Escribe un CLAUDE.md de equipo con exactamente 5 reglas que cubran: lenguaje, naming, testing, una prohibición, y una convención de git.
Ver solución
# CLAUDE.md — Mi Proyecto
## Convenciones
- Python 3.12 con type hints obligatorios en todas las funciones
- snake_case para archivos y funciones, PascalCase para clases
## Testing
- pytest para todos los tests, cobertura mínima 80%
## Prohibido
- NO usar print() para logging — usar structlog
## Git
- Conventional commits: tipo(scope): descripción en inglés
Cinco reglas claras, específicas, y actionables. Un agente que lee esto sabe exactamente qué hacer.
Ejercicio 2: Jerarquía de merge (Fácil)
Dados estos tres CLAUDE.md, ¿qué reglas sigue Claude Code cuando edita src/api/routes.py?
Global (~/.claude/CLAUDE.md):
- Responde en español
- Usa tabs
Proyecto (./CLAUDE.md):
- Usa spaces (4)
- Tests con pytest
Subdirectorio (./src/api/CLAUDE.md):
- Endpoints retornan ResponseModel
- Usa spaces (2)
Ver solución
Para src/api/routes.py, Claude Code sigue:
- Responde en español ← Global (no conflictúa con nada)
- Usa spaces (2) ← Subdirectorio (overrides proyecto que dice 4, que overrides global que dice tabs)
- Tests con pytest ← Proyecto (no conflictúa)
- Endpoints retornan ResponseModel ← Subdirectorio (regla adicional)
La regla "Usa tabs" del global queda completamente overrideada por "Usa spaces (4)" del proyecto, que a su vez queda overrideada por "Usa spaces (2)" del subdirectorio.
Si Claude edita un archivo en src/models/ (sin CLAUDE.md de subdirectorio), usaría spaces (4) del proyecto.
Ejercicio 3: Hook de enforcement para imports (Medio)
Crea un hook PostToolUse que valide que los imports en archivos Python siguen el orden correcto: stdlib → third-party → local. Reporta una violación (exit 1) si el orden es incorrecto.
Ver solución
scripts/hooks/enforce-import-order.sh:
#!/bin/bash
INPUT=$(cat -)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty')
if [ -z "$FILE" ] || [ ! -f "$FILE" ] || [[ "$FILE" != *.py ]]; then
exit 0
fi
if command -v ruff &> /dev/null; then
IMPORT_CHECK=$(ruff check "$FILE" --select I --no-fix 2>&1)
if [ $? -ne 0 ]; then
echo "CLAUDE.md VIOLATION: Import order incorrect in $FILE"
echo "Expected: stdlib > third-party > local"
echo "$IMPORT_CHECK" | head -5
echo ""
echo "Fix: ruff check --select I --fix $FILE"
exit 1
fi
fi
exit 0
Este hook usa ruff con la regla I (isort) para validar el orden de imports. Si ruff no está disponible, el hook pasa silenciosamente (exit 0).
Ejercicio 4: CLAUDE.md con subdirectorios (Medio)
Diseña una estructura de CLAUDE.md para un proyecto fullstack con frontend/ (React + TypeScript) y backend/ (Python + FastAPI). El CLAUDE.md raíz define reglas comunes, y cada subdirectorio define reglas específicas de su stack.
Ver solución
./CLAUDE.md (raíz):
# CLAUDE.md — FullStack App
## General
- Git conventional commits en inglés
- No hardcodear URLs o credenciales
- Variables sensibles en .env (never commit .env)
## Code Quality
- Cobertura de tests mínima: 80%
- No TODO sin issue asociado: "TODO(#123): descripción"
- Funciones de máximo 30 líneas
./frontend/CLAUDE.md:
## Frontend Rules
- TypeScript strict mode, no `any`
- React functional components only (no class components)
- Styling with Tailwind CSS utility classes
- Tests with Vitest + React Testing Library
- File naming: PascalCase for components, kebab-case for utils
- Imports: react > third-party > @/components > @/utils > relative
./backend/CLAUDE.md:
## Backend Rules
- Python 3.12 with type hints on all functions
- FastAPI with async endpoints
- SQLAlchemy 2.0 async ORM (no raw SQL)
- Tests with pytest + httpx.AsyncClient
- File naming: snake_case
- Logging with structlog (no print())
- Pydantic v2 for all request/response schemas
Ejercicio 5: Sistema de enforcement completo (Difícil)
Crea un settings.json que combine enforcement de CLAUDE.md con approval flows: PostToolUse hooks validan reglas del CLAUDE.md, y PreToolUse hooks bloquean commits que no siguen conventional commits.
Ver solución
.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "./scripts/hooks/validate-commit-message.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "./scripts/hooks/enforce-forbidden-patterns.sh"
},
{
"type": "command",
"command": "./scripts/hooks/enforce-import-order.sh"
}
]
}
]
}
}
scripts/hooks/validate-commit-message.sh:
#!/bin/bash
INPUT=$(cat -)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if ! echo "$COMMAND" | grep -q "git commit"; then
exit 0
fi
COMMIT_MSG=$(echo "$COMMAND" | grep -oP '(?<=-m ")[^"]*' | head -1)
if [ -z "$COMMIT_MSG" ]; then
COMMIT_MSG=$(echo "$COMMAND" | grep -oP "(?<=-m ')[^']*" | head -1)
fi
if [ -z "$COMMIT_MSG" ]; then
exit 0
fi
PATTERN="^(feat|fix|refactor|test|docs|chore|ci|perf|style)\(.+\): .+"
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
echo "CLAUDE.md VIOLATION: Commit message must follow Conventional Commits"
echo "Format: type(scope): description"
echo "Types: feat, fix, refactor, test, docs, chore, ci, perf, style"
echo "Got: $COMMIT_MSG"
exit 2
fi
exit 0
Resumen
- CLAUDE.md para equipos es una constitución, no un config file — define comportamiento, valores, y restricciones que todos los agentes respetan
- Un CLAUDE.md efectivo tiene secciones claras: contexto, convenciones, arquitectura, prohibiciones, testing, git, seguridad
- La jerarquía de merge (global < proyecto < subdirectorio) permite reglas compartidas con espacio para overrides en módulos específicos
- Enforcement con hooks convierte las reglas del CLAUDE.md en validaciones automáticas: PostToolUse verifica compliance después de cada edición
- El onboarding automático es el beneficio más tangible: clonar el repo = adoptar el estándar, sin configuración manual
- Mantén el CLAUDE.md conciso (< 200 líneas), específico (reglas actionables), y actualizado (revisión trimestral)
- Las prohibiciones son absolutas y específicas — "NO usar print()" no "evita print() si puedes"
- Hooks de enforcement respetan marcadores de excepción (
# justified:,# noqa:) para casos legítimos - Subdirectory CLAUDE.md permite reglas específicas por módulo sin sobrecargar el CLAUDE.md raíz
Recursos Adicionales
- Claude Code CLAUDE.md Memory — Documentación oficial de CLAUDE.md, niveles y merge
- Claude Code Settings — Configuración de settings.json para enforcement
- Claude Code Hooks — Hooks PostToolUse para validación de compliance
- Claude Code Best Practices — Buenas prácticas de CLAUDE.md
- Claude Code Tips and Tricks — Tips para CLAUDE.md efectivos
- Conventional Commits — Especificación de conventional commits
- ruff — Python Linter — Linter y formatter para Python usado en enforcement
- Google Python Style Guide — Referencia de estilo que complementa CLAUDE.md
Siguiente cápsula: En la cápsula 05 (Proyecto) construirás un setup completo de equipo integrando todo lo aprendido: un CLAUDE.md production-ready para un equipo de 3 personas, remote control configurado, approval flows para operaciones destructivas, hooks de enforcement, y un checklist de onboarding. El setup completo que llevarás al proyecto integrador del Módulo 8.