Hierarchical Context Patterns

James Phoenix
James Phoenix

Hierarchical Context with CLAUDE.md Files

Summary

Place CLAUDE.md files at every directory level to provide LLMs with hyper-localized, contextual knowledge. This hierarchical approach ensures AI agents load only relevant patterns and constraints for the code they’re working on, dramatically improving accuracy and reducing context overload.

The Problem

LLMs lack context about project-specific patterns, constraints, and architectural decisions. Without guidance, they generate code that compiles but violates project conventions, design patterns, and domain rules. Loading all documentation in a monolithic file wastes context and dilutes signal with irrelevant information.

The Solution

Distribute knowledge hierarchically across CLAUDE.md files at every directory level. Root CLAUDE.md contains global architecture and principles (lean, 20-50 lines). Domain/package CLAUDE.md files contain domain-specific patterns (50-200 lines). Subdirectory CLAUDE.md files contain implementation details. LLMs load only root + relevant domain context, achieving 70% context reduction with 80-95% relevance.

The Problem

When working with AI coding agents, they lack inherent knowledge about your codebase:

  • What patterns you follow
  • What frameworks you use
  • What design decisions you’ve made
  • What constraints apply to specific domains
  • What anti-patterns to avoid

Without this context, LLMs generate code that:

  • Compiles but violates project conventions
  • Runs but doesn’t match architectural patterns
  • Works but creates technical debt
  • Passes tests but misses domain-specific rules

The Monolithic Documentation Problem

The naive solution is to create a single, comprehensive CLAUDE.md file at the root:

/CLAUDE.md (5,000-10,000 lines)
├── Global architecture
├── API patterns
├── Database patterns
├── Frontend patterns
├── Testing patterns
├── Deployment patterns
├── Security patterns
└── ... (50+ more sections)

Problems:

  1. Context Overload: LLM loads 10,000 lines when it only needs 200
  2. Low Signal-to-Noise: 90%+ of content is irrelevant to current task
  3. Maintenance Burden: Single file becomes unmaintainable
  4. Difficult Discovery: Developers can’t find relevant sections
  5. Stale Information: Sections drift from actual code
  6. Merge Conflicts: Everyone edits the same file

Real-World Example

You’re working on an API endpoint in packages/api/src/routes/campaigns.ts.

Monolithic CLAUDE.md loads:

✅ API patterns (relevant)
✅ Validation patterns (relevant)
✅ Error handling (relevant)
❌ Frontend React patterns (irrelevant)
❌ Database migration patterns (irrelevant)
❌ Temporal workflow patterns (irrelevant)
❌ Email service patterns (irrelevant)
❌ ... (40 more irrelevant sections)

Result: 5% signal, 95% noise

What you actually need:

✅ Global architecture (20 lines)
✅ API-specific patterns (150 lines)
✅ Campaign domain rules (100 lines)

Total: 270 lines, 100% relevant

The Solution: Hierarchical CLAUDE.md Files

Distribute documentation hierarchically to mirror your codebase structure:

/
├── CLAUDE.md (LEAN - 30 lines)
│   ├── Core philosophy
│   ├── Global architecture principles
│   └── Link to domain-specific docs
│
├── packages/
│   ├── api/
│   │   ├── CLAUDE.md (150 lines)
│   │   │   ├── FastAPI patterns
│   │   │   ├── Route handler conventions
│   │   │   ├── Validation approach
│   │   │   └── Error handling
│   │   │
│   │   └── src/
│   │       ├── routes/
│   │       │   ├── campaigns/
│   │       │   │   └── CLAUDE.md (80 lines)
│   │       │   │       ├── Campaign-specific rules
│   │       │   │       ├── Status workflow
│   │       │   │       └── Business constraints
│   │       │   │
│   │       │   └── users/
│   │       │       └── CLAUDE.md (60 lines)
│   │       │           ├── User management patterns
│   │       │           └── Auth integration
│   │       │
│   │       └── services/
│   │           └── CLAUDE.md (100 lines)
│   │               └── Service layer patterns
│   │
│   ├── database/
│   │   └── CLAUDE.md (120 lines)
│   │       ├── Schema patterns
│   │       ├── Migration approach
│   │       └── Query optimization
│   │
│   └── temporal/
│       └── CLAUDE.md (180 lines)
│           ├── Workflow patterns
│           ├── Activity patterns
│           └── Determinism requirements

Loading Strategy

When working on a file, LLMs load:

  1. Root CLAUDE.md (global context)
  2. Relevant domain CLAUDE.md (domain context)
  3. Subdirectory CLAUDE.md (if exists, implementation details)

Example: Working on packages/api/src/routes/campaigns/create.ts

Loaded context:
├── /CLAUDE.md (30 lines) - Global architecture
├── /packages/api/CLAUDE.md (150 lines) - API patterns
└── /packages/api/src/routes/campaigns/CLAUDE.md (80 lines) - Campaign rules

Total: 260 lines, 95%+ relevant

Implementation Guide

Step 1: Create Lean Root CLAUDE.md

Keep the root file minimal – only global architecture and philosophy:

# Project: Social Media Scheduler

## Architecture

**Pattern**: Factory functions, no classes
**Stack**: TypeScript, FastAPI (Python), Supabase, Temporal
**Structure**: Monorepo with packages (api, database, temporal, web)

## Core Principles

1. **Type Safety**: All functions have explicit types
2. **Error Handling**: Return Result types, never throw in business logic
3. **Testing**: Integration tests over unit tests
4. **Documentation**: Self-documenting code + CLAUDE.md per domain

## Finding Domain-Specific Patterns

Each package has its own CLAUDE.md:
- `packages/api/CLAUDE.md` - API patterns
- `packages/database/CLAUDE.md` - Database patterns
- `packages/temporal/CLAUDE.md` - Workflow patterns
- `packages/web/CLAUDE.md` - Frontend patterns

Read the CLAUDE.md in the directory you're working in.

Line count: ~30 lines

Purpose: Orients the LLM to project structure, points to domain docs

Step 2: Create Domain CLAUDE.md Files

Each major package/domain gets its own CLAUDE.md:

Example: packages/api/CLAUDE.md

# API Package

## Route Handler Pattern

All routes follow this structure:

```typescript
import { FastifyRequest, FastifyReply } from 'fastify';
import { z } from 'zod';

const RequestSchema = z.object({
  // Define request schema
});

export async function handleCreateCampaign(
  request: FastifyRequest,
  reply: FastifyReply
): Promise<void> {
  // 1. Validate input
  const validation = RequestSchema.safeParse(request.body);
  if (!validation.success) {
    return reply.status(400).send({
      error: 'Validation failed',
      details: validation.error.issues,
    });
  }

  // 2. Call use case
  const result = await createCampaignUseCase(validation.data);

  // 3. Return result
  if (!result.success) {
    return reply.status(400).send({ error: result.error });
  }

  return reply.status(201).send(result.data);
}

Validation Rules

  • Always use Zod schemas
  • Always call .safeParse() (never .parse() – don’t throw)
  • Always return validation errors as 400 with details
  • Never trust request input

Error Handling

  • Use cases return Result<T, E> types
  • Routes check result.success and respond accordingly
  • Never throw exceptions in route handlers
  • Always log errors before returning

Authentication

  • Use requireAuth middleware for protected routes
  • Access user via request.user (typed)
  • Return 401 for missing auth
  • Return 403 for insufficient permissions

Testing

  • Integration tests in __tests__/routes/
  • Test happy path + error cases
  • Mock external services (database, Temporal)
  • Use test fixtures from __tests__/fixtures/

**Line count**: ~150 lines

**Purpose**: Provides API-specific patterns applicable to all routes

### Step 3: Create Subdomain CLAUDE.md Files (Optional)

For complex domains, add CLAUDE.md files at subdirectory level:

**Example**: `packages/api/src/routes/campaigns/CLAUDE.md`

```markdown
# Campaign Routes

## Domain Rules

### Campaign Status Workflow

DRAFT → SCHEDULED → PUBLISHED → COMPLETED
↓ ↓ ↓
ARCHIVED ARCHIVED ARCHIVED


**Allowed transitions**:
- DRAFT → SCHEDULED (when schedule time set)
- DRAFT → ARCHIVED (cancel before scheduling)
- SCHEDULED → PUBLISHED (when schedule time reached)
- SCHEDULED → ARCHIVED (cancel before publish)
- PUBLISHED → COMPLETED (when all posts sent)
- PUBLISHED → ARCHIVED (cancel mid-flight)

**Forbidden transitions**:
- ❌ COMPLETED → any other status (final state)
- ❌ ARCHIVED → any other status (final state)
- ❌ PUBLISHED → DRAFT (can't un-publish)

### Business Constraints

- **Max posts per campaign**: 50
- **Min schedule delay**: 5 minutes from now
- **Max schedule delay**: 90 days from now
- **Supported platforms**: Instagram, Twitter, LinkedIn
- **Image requirements**: 1080x1080 (Instagram), 1200x675 (Twitter, LinkedIn)

### Validation

```typescript
import { CampaignStatus } from '@repo/types';

function validateStatusTransition(
  from: CampaignStatus,
  to: CampaignStatus
): Result<void, string> {
  const allowed = {
    DRAFT: ['SCHEDULED', 'ARCHIVED'],
    SCHEDULED: ['PUBLISHED', 'ARCHIVED'],
    PUBLISHED: ['COMPLETED', 'ARCHIVED'],
    COMPLETED: [],
    ARCHIVED: [],
  };

  if (!allowed[from].includes(to)) {
    return {
      success: false,
      error: `Cannot transition from ${from} to ${to}`,
    };
  }

  return { success: true };
}

Common Patterns

  • Always validate status transitions
  • Always check user owns campaign before modifying
  • Always verify post count ≤ 50
  • Always validate schedule time constraints
  • Never allow direct status updates (use transition endpoints)

**Line count**: ~80 lines

**Purpose**: Campaign-specific business rules and constraints

### Step 4: Link Files Together

Each CLAUDE.md points to related files:

**Root CLAUDE.md**:
```markdown
## Finding Domain-Specific Patterns

Read the CLAUDE.md in the package you're working in:
- `packages/api/CLAUDE.md`
- `packages/database/CLAUDE.md`

Domain CLAUDE.md (packages/api/CLAUDE.md):

## Domain-Specific Rules

Some routes have additional domain rules in subdirectories:
- `src/routes/campaigns/CLAUDE.md` - Campaign workflow and constraints
- `src/routes/users/CLAUDE.md` - User management patterns

Benefits

1. Hyper-Localized Context

LLMs load only relevant documentation:

Monolithic approach:
- Load: 10,000 lines
- Relevant: 500 lines (5%)
- Noise: 9,500 lines (95%)

Hierarchical approach:
- Load: 260 lines (root + domain + subdomain)
- Relevant: 250 lines (96%)
- Noise: 10 lines (4%)

Context reduction: 97.4%
Relevance improvement: 19x better

2. Better AI Accuracy

Focused context = better code generation:

Monolithic CLAUDE.md:

  • LLM must filter 10K lines to find API patterns
  • Gets confused by irrelevant patterns (frontend, workflows)
  • Misses domain-specific rules buried in noise
  • Result: Code compiles but violates conventions

Hierarchical CLAUDE.md:

  • LLM reads 260 lines of relevant patterns
  • Sees exactly what applies to current task
  • Catches domain rules (campaign status workflow)
  • Result: Code follows all conventions

3. Maintainability

Smaller files are easier to maintain:

  • Focused editing: Edit only relevant domain
  • Fewer conflicts: Developers work in different domains
  • Easier reviews: PRs touch small, focused files
  • Faster updates: Find and update specific patterns quickly
  • Clear ownership: Domain teams own their CLAUDE.md

4. Developer Discovery

Developers can find patterns easily:

# What patterns apply to campaigns?
cat packages/api/src/routes/campaigns/CLAUDE.md

# What are global architecture rules?
cat CLAUDE.md

# How do we handle auth?
grep -r "authentication" **/CLAUDE.md

5. Living Documentation

Colocated documentation stays up-to-date:

  • Lives next to code (harder to drift)
  • Updated in same PR as code changes
  • Reviewed alongside code
  • Specific to actual implementation

Best Practices

1. Keep Root CLAUDE.md Lean

Target: 20-50 lines maximum

Include:

  • Project name and purpose
  • High-level architecture (stack, patterns)
  • Core principles (3-5 max)
  • Links to domain CLAUDE.md files

Exclude:

  • Implementation details
  • Framework-specific patterns
  • Domain-specific rules
  • Code examples (unless illustrating core principle)

2. Domain Files: 50-200 Lines

Target: 50-200 lines

Include:

  • Patterns specific to this domain
  • Framework usage (how we use FastAPI, React, etc.)
  • Common anti-patterns to avoid
  • Links to subdomain CLAUDE.md files (if any)
  • Code examples showing patterns

Exclude:

  • Subdomain-specific business rules
  • Extremely detailed implementation guides
  • Copy-pasted framework documentation

3. Subdomain Files: 50-150 Lines

Target: 50-150 lines

Include:

  • Business rules and constraints
  • Domain-specific workflows
  • Validation rules
  • State transitions
  • Common gotchas

Exclude:

  • General patterns (those go in parent CLAUDE.md)
  • Framework usage (those go in domain CLAUDE.md)

4. Use Clear Structure

Maintain consistent structure across all CLAUDE.md files:

# [Domain Name]

## Overview
[Brief description of this domain]

## Patterns
[Common patterns with code examples]

## Rules
[Must-follow rules and constraints]

## Anti-Patterns
[What NOT to do, with examples]

## Testing
[Testing approach for this domain]

## Related
[Links to related CLAUDE.md files]

5. Include Code Examples

Show, don’t just tell:

❌ Bad:
"Use factory functions instead of classes"

✅ Good:
"Use factory functions instead of classes:

```typescript
// ✅ Do this
export const createUserService = (deps: Dependencies) => {
  return {
    getUser: async (id: string) => { ... },
    createUser: async (data: UserInsert) => { ... },
  };
};

// ❌ Don't do this
export class UserService {
  getUser(id: string) { ... }
  createUser(data: UserInsert) { ... }
}


### 6. Update in Same PR as Code

When changing patterns:

1. Update code
2. Update CLAUDE.md in **same PR**
3. Reviewer checks both code and docs

This prevents documentation drift.

### 7. Use Grep-Friendly Names

Make patterns easy to find:

```markdown
## Route Handler Pattern

## Validation Pattern

## Error Handling Pattern

Developers and LLMs can grep for “Pattern” to find all patterns.

8. Link Related Concepts

Connect related CLAUDE.md files:

## Related

- See `packages/database/CLAUDE.md` for schema patterns
- See `packages/temporal/CLAUDE.md` for background job patterns
- See root `CLAUDE.md` for global architecture

Real-World Example

Before: Monolithic CLAUDE.md

File: /CLAUDE.md (8,500 lines)

Structure:

# Project Documentation

## Architecture (200 lines)
## API Patterns (800 lines)
## Database Patterns (600 lines)
## Frontend Patterns (1,200 lines)
## Temporal Workflows (900 lines)
## Testing Patterns (400 lines)
## Campaign Domain (300 lines)
## User Domain (250 lines)
## Analytics Domain (350 lines)
## Email Service (400 lines)
## ... (40 more sections)

When working on campaign endpoint:

  • Load: 8,500 lines
  • Relevant: ~300 lines (campaign domain) + 200 (API patterns) = 500 lines
  • Noise: 8,000 lines (94% irrelevant)

Problems:

  • LLM context wasted on frontend, workflows, analytics
  • Hard to find campaign-specific rules in 8,500 lines
  • Every PR touches same file (merge conflicts)
  • Developers don’t read it (too large)

After: Hierarchical CLAUDE.md

Structure:

/CLAUDE.md (40 lines)
packages/api/CLAUDE.md (180 lines)
packages/api/src/routes/campaigns/CLAUDE.md (90 lines)

Root (/CLAUDE.md – 40 lines):

Udemy Bestseller

Learn Prompt Engineering

My O'Reilly book adapted for hands-on learning. Build production-ready prompts with practical exercises.

4.5/5 rating
306,000+ learners
View Course
# Social Media Scheduler

## Architecture

Factory functions, TypeScript, FastAPI, Supabase, Temporal.

Monorepo structure: packages (api, database, temporal, web).

## Core Principles

1. Type safety: explicit types everywhere
2. Error handling: Result types, no throwing
3. Integration tests over unit tests
4. Colocated documentation: CLAUDE.md per package

## Domain Docs

- `packages/api/CLAUDE.md` - API patterns
- `packages/database/CLAUDE.md` - Database patterns
- `packages/temporal/CLAUDE.md` - Workflow patterns
- `packages/web/CLAUDE.md` - Frontend patterns

Domain (packages/api/CLAUDE.md – 180 lines):

# API Package

## Route Handler Pattern

[150 lines of FastAPI patterns, validation, error handling, auth]

## Domain-Specific Rules

- `src/routes/campaigns/CLAUDE.md` - Campaign workflow
- `src/routes/users/CLAUDE.md` - User management

Subdomain (packages/api/src/routes/campaigns/CLAUDE.md – 90 lines):

# Campaign Routes

## Status Workflow

DRAFT → SCHEDULED → PUBLISHED → COMPLETED

[90 lines of campaign-specific business rules, constraints, validation]

When working on campaign endpoint:

  • Load: 40 (root) + 180 (API) + 90 (campaigns) = 310 lines
  • Relevant: ~300 lines (97% relevant)
  • Noise: ~10 lines (3% irrelevant)

Improvements:

  • 96% less context loaded (8,500 → 310 lines)
  • 24x better signal-to-noise ratio (5% → 97%)
  • No merge conflicts (different teams edit different files)
  • Easy discovery (cat the file you’re working in)

Common Pitfalls

❌ Pitfall 1: Duplicating Content

Problem: Copying patterns from root to domain files

Solution: Root has only global patterns, domains extend/specialize

❌ Bad:

/CLAUDE.md:
"Use factory functions, no classes"

packages/api/CLAUDE.md:
"Use factory functions, no classes" (duplicate!)

✅ Good:

/CLAUDE.md:
"Use factory functions, no classes"

packages/api/CLAUDE.md:
"Route handlers follow factory pattern (see root CLAUDE.md):

export const createRouteHandler = (deps: Dependencies) => {
  return async (request, reply) => { ... };
};"

❌ Pitfall 2: Making Root CLAUDE.md Too Large

Problem: Root grows to 500+ lines

Solution: Move domain-specific content to domain files

Trigger: If root > 100 lines, audit and move content

❌ Pitfall 3: Orphaned CLAUDE.md Files

Problem: CLAUDE.md exists but code deleted

Solution: Delete CLAUDE.md when deleting directories

# In package.json scripts
"clean": "find . -type f -name 'CLAUDE.md' -exec check-orphaned {} \;"

❌ Pitfall 4: Inconsistent Structure

Problem: Every CLAUDE.md has different sections

Solution: Use template for new CLAUDE.md files

# [Domain Name]

## Overview

## Patterns

## Rules

## Anti-Patterns

## Testing

## Related

❌ Pitfall 5: Not Linking Files

Problem: LLMs don’t know related CLAUDE.md files exist

Solution: Always include “Related” section with links

Measuring Success

Metrics to Track

  1. Context Loaded:

    • Before: Total lines in monolithic CLAUDE.md
    • After: Lines in root + domain + subdomain
    • Target: 70-90% reduction
  2. Relevance Ratio:

    • Relevant lines / Total lines loaded
    • Target: >80% relevance
  3. File Sizes:

    • Root: <50 lines
    • Domain: 50-200 lines
    • Subdomain: 50-150 lines
  4. AI Accuracy:

    • % of generated code following conventions
    • Target: >90% compliance
  5. Developer Usage:

    • Survey: “Do you read CLAUDE.md files?”
    • Target: >80% say yes

Success Criteria

Good hierarchical structure:

  • ✅ Root CLAUDE.md <50 lines
  • ✅ Context loaded per task <300 lines
  • ✅ Relevance ratio >80%
  • ✅ AI-generated code follows conventions 90%+ of time
  • ✅ Developers actually read and update files

Needs improvement:

  • ❌ Root CLAUDE.md >100 lines
  • ❌ Context loaded per task >500 lines
  • ❌ Relevance ratio <50%
  • ❌ AI-generated code violates conventions frequently
  • ❌ Developers ignore CLAUDE.md files

Integration with Other Patterns

Combine with Prompt Caching

Hierarchical CLAUDE.md files work perfectly with prompt caching:

// Cached context (stable)
const stableContext = `
${await readFile('CLAUDE.md')}  // Root (rarely changes)
${await readFile('packages/api/CLAUDE.md')}  // Domain (changes occasionally)
`;

// Dynamic request (not cached)
const dynamicRequest = `
${await readFile('packages/api/src/routes/campaigns/CLAUDE.md')}  // Subdomain
Implement createCampaign endpoint
`;

See: Prompt Caching Strategy

Combine with Semantic Naming

Semantic file names make CLAUDE.md files discoverable:

packages/
├── api-fastify-routes/
│   └── CLAUDE.md (API route patterns)
├── database-supabase-client/
│   └── CLAUDE.md (Database patterns)
├── temporal-workflows/
│   └── CLAUDE.md (Workflow patterns)

See: Semantic Naming for Retrieval

Combine with Custom ESLint Rules

CLAUDE.md documents patterns, ESLint enforces them:

CLAUDE.md:

## Factory Pattern

ALWAYS use factory functions, NEVER classes.

ESLint (.eslintrc.js):

rules: {
  'no-classes': 'error',  // Enforces factory pattern
}

See: Custom ESLint Rules for Determinism

Combine with Test-Based Regression Patching

When bugs occur:

  1. Write test that catches bug
  2. Fix bug
  3. Update CLAUDE.md with pattern to prevent recurrence

Example:

Bug: Campaign status transition validation missing

Fix:

  1. Add test: test_campaign_status_transition_validation()
  2. Fix code: Add validation logic
  3. Update packages/api/src/routes/campaigns/CLAUDE.md:
## Status Transitions

ALWAYS validate status transitions using validateStatusTransition().

NEVER allow direct status updates.

See: Test-Based Regression Patching

Conclusion

Hierarchical CLAUDE.md files solve the context overload problem by distributing knowledge across the codebase:

Key Benefits:

  1. 70-90% context reduction: Load only relevant documentation
  2. 80-95% relevance: LLMs see only applicable patterns
  3. Better AI accuracy: Focused context = better code generation
  4. Maintainability: Small, focused files are easier to maintain
  5. Developer discovery: Easy to find relevant patterns
  6. Living documentation: Colocated docs stay up-to-date

Structure:

  • Root CLAUDE.md (20-50 lines): Global architecture, core principles
  • Domain CLAUDE.md (50-200 lines): Framework patterns, domain conventions
  • Subdomain CLAUDE.md (50-150 lines): Business rules, specific constraints

Loading strategy: Root + relevant domain + subdomain = 200-400 lines of highly relevant context

The result: LLMs generate code that follows all project conventions, architectural patterns, and domain rules – not by chance, but by design.

Related Concepts

References


Hierarchical Context Files: CLAUDE.md at Every Directory Level

Summary

AI coding agents struggle with massive monolithic documentation that mixes irrelevant context with domain-specific patterns. Hierarchical context files solve this by placing CLAUDE.md files at every directory level, creating a tree of increasingly specific context. Root files define global architecture, domain directories specify patterns for APIs/databases/workflows, and subdirectories add implementation details. LLMs load context bottom-up, getting exactly what they need without noise.

The Problem

Monolithic root CLAUDE.md files become massive (5,000-10,000+ lines) as projects grow, mixing global architecture with domain-specific patterns for APIs, databases, UI components, and workflows. When LLMs load this entire file for every request, they get 90%+ irrelevant context, leading to hallucinated patterns, ignored constraints, and inconsistent implementations. Developers face the same problem: finding relevant documentation requires searching thousands of lines or asking teammates.

The Solution

Create a hierarchy of CLAUDE.md files mirroring your codebase structure. Root CLAUDE.md stays lean (30-50 lines) with global architecture. Each domain directory (api/, database/, workflows/) gets its own CLAUDE.md with domain-specific patterns. Subdirectories add implementation details. LLMs load context hierarchically (root → domain → subdomain), receiving 200-500 lines of highly relevant context instead of 10,000 lines of mixed content. This achieves 80-95% context relevance vs 5-10% with monolithic files.

The Problem: Context Dilution at Scale

When you ask Claude Code to “implement a new Temporal workflow,” it loads your root CLAUDE.md file to understand project patterns. But what happens when that file looks like this?

# Root CLAUDE.md (10,247 lines)

## Project Architecture
[500 lines about monorepo structure]

## API Patterns
[1,200 lines about tRPC endpoints, DTOs, validation]

## Database Patterns  
[1,500 lines about Supabase migrations, RLS policies]

## UI Patterns
[2,000 lines about React components, state management]

## Workflow Patterns (buried at line 8,734)
[800 lines about Temporal workflows, activities]

## CLI Patterns
[600 lines about command-line tools]

## Shared Utilities
[400 lines about shared helper functions]

The Real Impact

LLM perspective:

  • Loads 10,247 lines of context
  • Only 800 lines (7.8%) are relevant to Temporal workflows
  • 9,447 lines (92.2%) are noise about APIs, databases, UI
  • LLM attention diluted across irrelevant patterns
  • Frequently ignores or hallucinates Temporal patterns

Developer perspective:

  • Opens CLAUDE.md to check Temporal patterns
  • Scrolls through 8,734 lines to find relevant section
  • Takes 5-10 minutes to locate information
  • Often gives up and asks teammates instead

Code quality impact:

  • LLM generates workflows using API patterns (wrong domain)
  • Inconsistent implementations across team
  • 30-40% first-try correctness
  • 3-5 iterations to get correct code

Why This Happens

Projects evolve organically. What starts as a simple 100-line CLAUDE.md grows as you add:

  1. New domains: API, database, workflows, UI
  2. Domain patterns: tRPC conventions, migration rules, workflow patterns
  3. Examples: Working code showing correct implementations
  4. Anti-patterns: Common mistakes to avoid
  5. Rationale: Why certain decisions were made

Before long, you have a 10,000-line monolith mixing global architecture with highly specific domain patterns.

The Solution: Hierarchical Context Files

The Core Principle

“Context should be as close as possible to the code it describes, and as specific as necessary.”

Instead of one massive root CLAUDE.md, create a hierarchy of context files that mirror your codebase structure:

project/
├── CLAUDE.md                           # Global: 30-50 lines
├── packages/
│   ├── api/
│   │   ├── CLAUDE.md                   # API domain: 200-300 lines
│   │   └── src/
│   │       ├── routes/
│   │       │   ├── CLAUDE.md           # Route patterns: 100-150 lines
│   │       │   ├── users/
│   │       │   │   └── CLAUDE.md       # User routes: 50-100 lines
│   │       │   └── campaigns/
│   │       │       └── CLAUDE.md       # Campaign routes: 50-100 lines
│   │       └── middleware/
│   │           └── CLAUDE.md           # Middleware: 80-120 lines
│   ├── database/
│   │   ├── CLAUDE.md                   # Database domain: 250-350 lines
│   │   └── src/
│   │       ├── migrations/
│   │       │   └── CLAUDE.md           # Migration rules: 150-200 lines
│   │       └── dtos/
│   │           └── CLAUDE.md           # DTO patterns: 100-150 lines
│   ├── workflows/
│   │   ├── CLAUDE.md                   # Temporal domain: 300-400 lines
│   │   └── src/
│   │       ├── workflows/
│   │       │   └── CLAUDE.md           # Workflow patterns: 150-200 lines
│   │       └── activities/
│   │           └── CLAUDE.md           # Activity patterns: 100-150 lines
│   └── ui/
│       ├── CLAUDE.md                   # UI domain: 250-350 lines
│       └── src/
│           └── components/
│               └── CLAUDE.md           # Component patterns: 150-200 lines

How Context Loading Works

When working on a file like packages/workflows/src/workflows/send-email.ts, the LLM loads context hierarchically from bottom-up:

  1. Most specific: packages/workflows/src/workflows/CLAUDE.md (150 lines)

    • Workflow-specific patterns
    • Naming conventions
    • Error handling for workflows
  2. Domain level: packages/workflows/CLAUDE.md (300 lines)

    • Temporal SDK patterns
    • Activity usage
    • Determinism requirements
  3. Global level: CLAUDE.md (40 lines)

    • Monorepo structure
    • Tech stack
    • SOLID principles

Total context: ~490 lines of highly relevant information

Relevance: 95%+ of loaded context applies to Temporal workflows

Compare to monolithic:

  • Monolithic: 10,247 lines loaded, 800 relevant (7.8%)
  • Hierarchical: 490 lines loaded, 465 relevant (95%)

Result: LLM generates correct workflow patterns on first try.

Implementation Guide

Step 1: Identify Your Domains

Audit your codebase to find logical domains with distinct patterns:

# Example project structure
packages/
├── api/          # Domain: HTTP endpoints, tRPC, validation
├── database/     # Domain: Migrations, RLS, queries  
├── workflows/    # Domain: Temporal orchestration
├── ui/           # Domain: React components, state
├── cli/          # Domain: Command-line tools
└── shared/       # Domain: Shared utilities

Key question: “Does code in this directory need different patterns than other directories?”

  • API routes: DTO validation, error codes, tRPC patterns → Yes, separate domain
  • Database migrations: Schema changes, RLS policies, backwards compatibility → Yes, separate domain
  • Temporal workflows: Determinism, activities, error handling → Yes, separate domain
  • React components: Props, hooks, state management → Yes, separate domain

If yes → Create domain-specific CLAUDE.md

Step 2: Extract Domain Context from Root

Identify sections in your root CLAUDE.md that only apply to specific domains:

Before:

# Root CLAUDE.md (10,247 lines)

## API Patterns
- Validate all DTOs with Zod schemas
- Return only 200/400/401/403/404/500 status codes
- Use tRPC for type safety
[1,200 lines of API-specific patterns]

## Temporal Workflow Patterns
- All external calls must use activities
- Workflows must be deterministic
- Use proxyActivities pattern
[800 lines of Temporal-specific patterns]

## Database Migration Patterns
- Never delete columns, only deprecate
- Always include RLS policies
- Use snake_case for SQL
[1,500 lines of database-specific patterns]

After:

# Root CLAUDE.md (40 lines)

## Project Architecture

Monorepo using Turborepo with domain-driven structure:
- `packages/api`: tRPC API endpoints
- `packages/database`: Supabase database layer
- `packages/workflows`: Temporal orchestration
- `packages/ui`: Next.js frontend

**See each package's CLAUDE.md for domain-specific patterns.**

## Global Principles

- TypeScript strict mode enabled
- All code must have integration tests
- Follow SOLID principles
- Prefer functional patterns over classes
- 200 lines max per file (enforced by linter)
# packages/api/CLAUDE.md (280 lines)

## API Architecture

This package implements the tRPC API layer.

## DTO Validation Patterns

- Every endpoint MUST validate input with Zod schemas
- DTOs defined in `packages/database/src/dtos/`
- Return types must be explicit (never `any`)

## Error Handling

- Return only these status codes: 200/400/401/403/404/500
- Use TRPCError for all errors
- Include error codes for client handling

[270+ more lines of API-specific patterns]
# packages/workflows/CLAUDE.md (320 lines)

## Temporal Architecture

This package implements Temporal workflows and activities.

## Workflow Patterns

- **Determinism**: Workflows MUST be deterministic
- **External calls**: Use activities, never direct calls
- **Randomness**: Use `workflow.uuid()`, not `Math.random()`

## Activity Patterns

- All activities MUST have `startToCloseTimeout`
- Idempotent: Activities can be retried safely
- Error handling: Throw `ApplicationFailure` for business errors

[310+ more lines of Temporal-specific patterns]
# packages/database/CLAUDE.md (340 lines)

## Database Architecture

This package manages Supabase database schema and migrations.

## Migration Patterns

- **Never delete columns**: Mark as deprecated instead
- **Always add RLS policies**: No table without RLS
- **Use snake_case**: For SQL, camelCase for TypeScript

## RLS Policy Patterns

- User-scoped: `auth.uid() = user_id`
- Tenant-scoped: `auth.uid() IN (SELECT user_id FROM tenant_users WHERE tenant_id = tenants.id)`
- Public read: `SELECT true`

[330+ more lines of database-specific patterns]

Result:

  • Root: 40 lines (was 10,247)
  • Domains: 300-400 lines each (highly focused)
  • Total: Same information, better organized

Step 3: Create Subdomain Context for Complex Domains

When domains have distinct subdomains, add another level:

packages/api/
├── CLAUDE.md                    # API-level: tRPC setup, global middleware (280 lines)
└── src/
    ├── routes/
    │   ├── CLAUDE.md            # Route-level: organization, naming (120 lines)
    │   ├── users/
    │   │   └── CLAUDE.md        # User routes: auth, profiles (80 lines)
    │   └── campaigns/
    │       └── CLAUDE.md        # Campaign routes: CRUD, permissions (90 lines)
    └── middleware/
        └── CLAUDE.md            # Middleware: auth, logging, errors (100 lines)

When to add subdomain CLAUDE.md:

Only when patterns diverge significantly between subdirectories.

Example: User routes vs Campaign routes

# packages/api/src/routes/users/CLAUDE.md (80 lines)

## User Route Patterns

User routes handle authentication and profile management.

## Authentication

- POST /users/register: Email + password, returns user + session
- POST /users/login: Email + password, returns session
- POST /users/logout: Invalidates session

## Password Rules

- Minimum 12 characters
- Must include: uppercase, lowercase, number, symbol
- Use bcrypt with cost factor 12
- Check against leaked password database

[70+ more lines of user-specific patterns]
# packages/api/src/routes/campaigns/CLAUDE.md (90 lines)

## Campaign Route Patterns

Campaign routes handle marketing campaign CRUD operations.

## Authorization

- All campaign endpoints require authentication
- Check user has permission for tenant
- Campaign access: Owner or tenant member with `campaigns:read`

## Campaign State Machine

- DRAFT → SCHEDULED → RUNNING → COMPLETED
- DRAFT → ARCHIVED
- RUNNING → PAUSED → RUNNING
- Only DRAFT campaigns can be edited

[80+ more lines of campaign-specific patterns]

Why separate files?

  • User patterns (auth, passwords) ≠ Campaign patterns (state machines, permissions)
  • When working on user routes, campaign patterns are noise
  • When working on campaign routes, auth patterns are noise

Step 4: Standardize Section Structure

Use consistent headers across all CLAUDE.md files for predictability:

# packages/[domain]/CLAUDE.md

## Architecture
[What this domain does, how it fits into project]

## Patterns
[Domain-specific implementation patterns]

## Conventions
[Naming, organization, file structure]

## Anti-Patterns
[Common mistakes, what NOT to do]

## Examples
[Working code showing correct patterns]

## Related Context
[Links to other relevant CLAUDE.md files]

Example:

# packages/workflows/CLAUDE.md

## Architecture

Temporal orchestrates long-running workflows. This package contains:
- `src/workflows/`: Workflow definitions (deterministic logic)
- `src/activities/`: Activities (side effects like API calls)
- `src/worker.ts`: Worker that executes workflows

## Patterns

### Workflow Determinism

Workflows MUST be deterministic (replaying history must produce same result).

**DO**:
- Use `workflow.uuid()` for randomness
- Use `workflow.now()` for current time
- Use `proxyActivities()` for external calls

**DON'T**:
- Use `Math.random()` (non-deterministic)
- Use `Date.now()` (non-deterministic)
- Make HTTP calls directly (non-deterministic)

### Activity Pattern

```typescript
// src/workflows/send-email-workflow.ts
import { proxyActivities } from '@temporalio/workflow';
import type * as activities from '../activities';

const { sendEmail } = proxyActivities<typeof activities>({
  startToCloseTimeout: '1 minute',
});

export async function sendEmailWorkflow(email: string) {
  await sendEmail({ to: email, template: 'welcome' });
}

Conventions

  • Workflow files: [name]-workflow.ts
  • Activity files: [name]-activity.ts
  • Workflow IDs: ${workflowName}-${entityId} (e.g., sendEmail-user123)

Anti-Patterns

❌ External Calls in Workflows

// WRONG: Direct HTTP call
export async function workflow() {
  await fetch('https://api.example.com'); // Non-deterministic!
}

✅ External Calls in Activities

// CORRECT: HTTP call in activity
export async function fetchDataActivity() {
  return await fetch('https://api.example.com');
}

export async function workflow() {
  const data = await fetchData(); // Calls activity
}

Examples

See src/workflows/send-welcome-email-workflow.ts for complete example.

Related Context

  • Root CLAUDE.md: Global architecture
  • packages/workflows/src/workflows/CLAUDE.md: Workflow-specific patterns
  • packages/workflows/src/activities/CLAUDE.md: Activity-specific patterns

### Step 5: Link Between Levels

Create explicit navigation between hierarchy levels:

```markdown
# Root CLAUDE.md

## Domain-Specific Patterns

See domain CLAUDE.md files:
- `packages/api/CLAUDE.md`: API patterns
- `packages/database/CLAUDE.md`: Database patterns
- `packages/workflows/CLAUDE.md`: Temporal patterns
- `packages/ui/CLAUDE.md`: React patterns
# packages/workflows/CLAUDE.md

## Related Context

**Parent**: See `CLAUDE.md` (root) for global architecture

**Children**:
- `src/workflows/CLAUDE.md`: Workflow-specific patterns
- `src/activities/CLAUDE.md`: Activity-specific patterns

**Siblings**:
- `packages/api/CLAUDE.md`: For HTTP endpoints that trigger workflows
- `packages/database/CLAUDE.md`: For DTOs used in workflows

Real-World Example

Scenario: Implementing a New Temporal Workflow

Task: “Create a workflow to send welcome emails when users sign up.”

Before: Monolithic CLAUDE.md

// Context loaded:
// - Root CLAUDE.md: 10,247 lines
//   - Lines 1-500: Architecture
//   - Lines 501-1,700: API patterns (irrelevant)
//   - Lines 1,701-3,200: Database patterns (irrelevant)
//   - Lines 3,201-5,200: UI patterns (irrelevant)
//   - Lines 5,201-6,000: Temporal patterns (RELEVANT)
//   - Lines 6,001-10,247: Other patterns (irrelevant)
//
// Relevance: 800 / 10,247 = 7.8%

// LLM generates (mixing API and workflow patterns):
export async function sendWelcomeEmail(userId: string) {
  // ❌ Wrong: Using API error handling pattern
  if (!userId) {
    throw new TRPCError({ code: 'BAD_REQUEST' });
  }
  
  // ❌ Wrong: Direct external call (non-deterministic)
  await sendgrid.send({
    to: await getUser(userId).email,
    template: 'welcome',
  });
}

// Developer reviews: "This is wrong, it's using API patterns in a workflow"
// Ask LLM to fix...
// 3-4 iterations later, finally correct

After: Hierarchical CLAUDE.md

// Context loaded (hierarchically):
// 1. Root CLAUDE.md: 40 lines (global architecture)
// 2. packages/workflows/CLAUDE.md: 320 lines (Temporal patterns)
// 3. packages/workflows/src/workflows/CLAUDE.md: 150 lines (workflow specifics)
//
// Total: 510 lines
// Relevance: 485 / 510 = 95%

// LLM generates (using correct Temporal patterns):
import { proxyActivities } from '@temporalio/workflow';
import type * as activities from '../activities';

const { sendEmail, getUserEmail } = proxyActivities<typeof activities>({
  startToCloseTimeout: '1 minute',
});

export async function sendWelcomeEmailWorkflow(
  userId: string
): Promise<void> {
  // ✅ Correct: Input validation (from workflow patterns)
  if (!userId) {
    throw new ApplicationFailure('userId required');
  }
  
  // ✅ Correct: External calls via activities (deterministic)
  const email = await getUserEmail(userId);
  
  await sendEmail({
    to: email,
    template: 'welcome',
    data: { userId },
  });
}

// Developer reviews: "Perfect, uses correct Temporal patterns"
// Ships on first try ✓

Impact:

  • Iterations: 3-4 → 1
  • Time: 30-45 min → 5-10 min
  • Code quality: Mixed patterns → Consistent Temporal patterns

Integration with AI Tools

Claude Code

Claude Code automatically loads CLAUDE.md files hierarchically:

# When working on:
packages/workflows/src/workflows/send-email-workflow.ts

# Claude Code loads:
1. /CLAUDE.md
2. /packages/workflows/CLAUDE.md
3. /packages/workflows/src/workflows/CLAUDE.md

# No configuration needed - just create the files

Cursor

Cursor’s .cursorrules files work the same way:

project/
├── .cursorrules               # Global
├── packages/
│   ├── api/
│   │   └── .cursorrules       # API-specific
│   └── workflows/
│       └── .cursorrules       # Temporal-specific

Aider

Aider loads context via explicit configuration:

# packages/workflows/.aider.conf.yml

read:
  - ../../CLAUDE.md
  - CLAUDE.md
  - src/workflows/CLAUDE.md

Custom Tooling

Implement hierarchical loading in your own tools:

import fs from 'fs';
import path from 'path';

function loadHierarchicalContext(filePath: string): string[] {
  const contexts: string[] = [];
  let currentDir = path.dirname(filePath);
  const root = process.cwd();
  
  // Walk up from file to root
  while (currentDir.startsWith(root)) {
    const claudeFile = path.join(currentDir, 'CLAUDE.md');
    
    if (fs.existsSync(claudeFile)) {
      // Prepend (root context loaded first)
      contexts.unshift(fs.readFileSync(claudeFile, 'utf-8'));
    }
    
    if (currentDir === root) break;
    currentDir = path.dirname(currentDir);
  }
  
  return contexts;
}

// Usage:
const contextFiles = loadHierarchicalContext(
  'packages/workflows/src/workflows/send-email.ts'
);

// Returns:
// [
//   '/CLAUDE.md',
//   '/packages/workflows/CLAUDE.md',
//   '/packages/workflows/src/workflows/CLAUDE.md',
// ]

const fullContext = contextFiles.join('\n\n---\n\n');

Best Practices

1. Keep Root CLAUDE.md Lean (<50 lines)

Root file should be navigation, not implementation:

# Root CLAUDE.md

## Project Architecture

Monorepo structure:
- `packages/api`: tRPC endpoints (see packages/api/CLAUDE.md)
- `packages/database`: Supabase layer (see packages/database/CLAUDE.md)
- `packages/workflows`: Temporal (see packages/workflows/CLAUDE.md)

## Global Principles

- TypeScript strict mode
- Integration tests required
- SOLID principles
- 200 lines max per file

Don’t include domain-specific patterns in root.

2. Add Hierarchy Levels Only When Patterns Diverge

Don’t create deep hierarchies prematurely:

 Too deep (premature):
packages/api/src/routes/users/handlers/create/validation/CLAUDE.md

 Start simple:
packages/api/CLAUDE.md

 Add when patterns diverge:
packages/api/src/routes/users/CLAUDE.md (only when users/ has unique patterns)

Rule of thumb: Max 3-4 levels

3. Use Consistent Section Headers

Standardize headers for predictability:

## Architecture
## Patterns
## Conventions
## Anti-Patterns
## Examples
## Related Context

4. Include Concrete Examples

Abstract rules are hard to apply. Show code:

❌ Abstract:
- Workflows must be deterministic

✅ Concrete:
- Workflows must be deterministic

  **DON'T**:
  ```typescript
  export async function workflow() {
    const id = Math.random(); // Non-deterministic!
  }

DO:

export async function workflow() {
  const id = workflow.uuid(); // Deterministic ✓
}

### 5. Link Between Hierarchy Levels

Make navigation explicit:

```markdown
# packages/workflows/CLAUDE.md

## Related Context

**Parent**: CLAUDE.md (root)
**Children**: src/workflows/CLAUDE.md, src/activities/CLAUDE.md
**Siblings**: packages/api/CLAUDE.md (for triggering workflows)

6. Update Context When Code Changes

Treat CLAUDE.md as first-class code:

# Git hook: pre-commit
#!/bin/bash

for file in $(git diff --cached --name-only); do
  if <a href="/posts/file-ts/">$file == *.ts</a>; then
    dir=$(dirname "$file")
    if [ -f "$dir/CLAUDE.md" ]; then
      echo "⚠️  Code changed in $dir - review $dir/CLAUDE.md"
    fi
  fi
done

Measuring Success

Key Metrics

1. Context Relevance

Metric: % of loaded context lines that apply to current task

interface ContextMetrics {
  totalLinesLoaded: number;
  relevantLines: number;
  relevanceRate: number;
}

// Monolithic:
const monolithic: ContextMetrics = {
  totalLinesLoaded: 10247,
  relevantLines: 800,
  relevanceRate: 0.078, // 7.8%
};

// Hierarchical:
const hierarchical: ContextMetrics = {
  totalLinesLoaded: 510,
  relevantLines: 485,
  relevanceRate: 0.950, // 95%
};

Target: >80% relevance

2. First-Try Correctness

Metric: % of LLM-generated code that’s correct without iteration

Monolithic: 30-40% first-try correct
Hierarchical: 70-85% first-try correct

Target: >80%

3. Time to Find Context

Metric: How long to locate relevant documentation

Monolithic: 5-10 minutes (search 10K lines)
Hierarchical: <30 seconds (same directory as code)

Target: <1 minute

4. Iteration Count

Metric: Avg iterations needed for correct code

Monolithic: 3-5 iterations
Hierarchical: 1-2 iterations

Target: <2 iterations

Tracking Context Effectiveness

interface GenerationMetrics {
  file: string;
  contextLoaded: string[];
  linesLoaded: number;
  relevantLines: number;
  firstTryCorrect: boolean;
  iterations: number;
}

const metrics: GenerationMetrics = {
  file: 'packages/workflows/src/workflows/send-email.ts',
  contextLoaded: [
    'CLAUDE.md',
    'packages/workflows/CLAUDE.md',
    'packages/workflows/src/workflows/CLAUDE.md',
  ],
  linesLoaded: 510,
  relevantLines: 485,
  firstTryCorrect: true,
  iterations: 1,
};

const relevanceRate = metrics.relevantLines / metrics.linesLoaded;
console.log(`Context relevance: ${(relevanceRate * 100).toFixed(1)}%`);
// Output: Context relevance: 95.1%

Common Pitfalls

❌ Pitfall 1: Too Many Hierarchy Levels

Problem: Over-engineering structure

 Too deep:
api/src/routes/users/handlers/create/validation/email/CLAUDE.md

Solution: Stop at 3-4 levels max

 Appropriate depth:
- CLAUDE.md (root)
- packages/api/CLAUDE.md (domain)
- packages/api/src/routes/CLAUDE.md (subdomain)
- packages/api/src/routes/users/CLAUDE.md (feature)

❌ Pitfall 2: Duplicating Content Across Levels

Problem: Same patterns in multiple files

# packages/api/CLAUDE.md
## DTO Validation
[200 lines]

# packages/database/CLAUDE.md  
## DTO Validation
[Same 200 lines duplicated]

Solution: Extract to shared file

# packages/shared/CLAUDE.md
## DTO Validation (Shared)
[200 lines - single source of truth]

# packages/api/CLAUDE.md
See packages/shared/CLAUDE.md for DTO validation.

# packages/database/CLAUDE.md
See packages/shared/CLAUDE.md for DTO validation.

❌ Pitfall 3: No Examples

Problem: Abstract rules without concrete code

❌ Abstract only:
- Workflows must be deterministic

Solution: Always include code examples

✅ With examples:
- Workflows must be deterministic

  ```typescript
  // ❌ Wrong:
  const id = Math.random();
  
  // ✅ Correct:
  const id = workflow.uuid();

### ❌ Pitfall 4: Stale Context

**Problem**: Code changes but CLAUDE.md doesn't

**Solution**: Add git hooks to remind updates

```bash
#!/bin/bash
# .git/hooks/pre-commit

for file in $(git diff --cached --name-only | grep "\.ts$"); do
  dir=$(dirname "$file")
  [ -f "$dir/CLAUDE.md" ] && echo "⚠️  Review $dir/CLAUDE.md"
done

Advanced: Auto-Generated Summaries

Generate navigation summaries:

#!/bin/bash
# scripts/generate-context-map.sh

echo "# Context Map" > CONTEXT_MAP.md
echo "" >> CONTEXT_MAP.md

find . -name "CLAUDE.md" | sort | while read file; do
  dir=$(dirname "$file")
  echo "## $dir" >> CONTEXT_MAP.md
  
  # Extract H2 headers
  grep "^## " "$file" | sed 's/^## /- /' >> CONTEXT_MAP.md
  echo "" >> CONTEXT_MAP.md
done

Output:

# Context Map

## .
- Project Architecture
- Global Principles

## ./packages/api
- API Architecture
- DTO Validation Patterns
- Error Handling

## ./packages/workflows
- Temporal Architecture
- Workflow Patterns
- Activity Patterns

Conclusion

Hierarchical context files solve the context dilution problem by organizing documentation to mirror code structure:

Before (Monolithic):

  • 10,000+ line root CLAUDE.md
  • 5-10% context relevance
  • 30-40% first-try correctness
  • 5-10 minutes to find patterns
  • 3-5 iterations per task

After (Hierarchical):

  • 30-50 line root + 200-400 line domain files
  • 80-95% context relevance
  • 70-85% first-try correctness
  • <30 seconds to find patterns
  • 1-2 iterations per task

Implementation Steps:

  1. Identify domains with distinct patterns (API, database, workflows, UI)
  2. Extract domain context from root CLAUDE.md to domain-specific files
  3. Create subdomain files when patterns diverge significantly
  4. Standardize structure (Architecture, Patterns, Conventions, Examples)
  5. Link between levels (parent, children, siblings)
  6. Keep root lean (<50 lines, mostly navigation)

Result: LLMs and developers get exactly the context they need, nothing more, nothing less.

Related Concepts

References

Topics
Agent GuidanceBest PracticesClaude MdCodebase OrganizationCollocationContext ManagementDocumentationEslint ConfigHierarchical KnowledgeHyper Localized

More Insights

Cover Image for Thought Leaders

Thought Leaders

People to follow for compound engineering, context engineering, and AI agent development.

James Phoenix
James Phoenix
Cover Image for Systems Thinking & Observability

Systems Thinking & Observability

Software should be treated as a measurable dynamical system, not as a collection of features.

James Phoenix
James Phoenix