Most people think using Claude Code is about writing better prompts.
It is not.
The real unlock is structuring your repository so Claude can think like an engineer -- not a chatbot you are explaining things to over and over.
I run a 20-agent AI orchestration platform called Bureau. Four business verticals, six Docker containers, 30+ agents, each with a different LLM and a defined role. Claude Code is how I build it. Not Cursor, not Copilot -- Claude Code, operating inside a codebase that is designed for it.
The difference between Claude Code behaving like a chatbot and Claude Code behaving like a developer who lives inside your codebase comes down to one thing: project structure.
A messy repo gives Claude nothing to work with. It guesses, hallucinates file paths, invents API patterns that don't match yours, and asks you to confirm things it should already know. A structured repo gives Claude the equivalent of a senior engineer's institutional knowledge -- on every session, from the first prompt.
Your project needs four things:
- •The Why -- what the system does and how it is architected
- •The Map -- where things live and how they connect
- •The Rules -- what is allowed, what is forbidden, and what breaks if you do it wrong
- •The Workflows -- how work actually gets done
Here is how to build each one.
1. CLAUDE.md -- Your Repo's Institutional Memory
CLAUDE.md is the single most important file in your project. It is loaded into every Claude Code session -- the first thing Claude reads before it touches your code. Think of it as the onboarding document you would write for a senior engineer joining your team, except this engineer has perfect recall and reads it every single morning.
What CLAUDE.md Actually Is
It is a markdown file at your project root. Claude Code loads it as a user message after the system prompt. That distinction matters -- it is not a system-level instruction with guaranteed compliance. It is more like a strong recommendation from a trusted colleague. This is why concise, specific instructions massively outperform verbose ones. Anthropic's docs recommend under 500 lines. In practice, the community consensus is tighter: 200 lines or fewer per file.
HumanLayer's engineering team keeps theirs under 60 lines. SFEIR Institute found that well-structured CLAUDE.md files reduce instruction repetition by 25% across sessions. And Anthropic's own system prompt consumes roughly 24K tokens of the 200K context window -- every line in your CLAUDE.md is competing with your actual code for attention.
The Three Things It Needs
1. Purpose -- why the system exists
Not a product description. An architectural statement that tells Claude what kind of system it is working inside.
## Project Overview
Bureau -- Multi-agent AI orchestration framework with four business verticals. React SPA frontend talks to a FastAPI backend, which dispatches jobs to Celery workers that call LLM agents through OpenClaw (a Node.js gateway). PostgreSQL + pgvector for persistence and memory, Redis for task brokering.
That is 6 lines. Claude now knows it is working in a multi-service Python/Node/React system with a task queue architecture. Every file it reads, every function it writes, every suggestion it makes will be informed by that context.
2. Map -- how the project is structured
This is where most people go wrong. They either include nothing (Claude guesses), or they paste their entire directory tree (Claude drowns in noise).
The sweet spot is a functional map -- not every file, but every important area with what it does:
## Code Layout
Backend (celery_app/)
- •api.py -- FastAPI app, startup hooks, router registration
- •models.py -- All SQLAlchemy ORM models, enums, init_db(), seed functions
- •tasks.py -- Celery tasks: execute_agent_job, orchestrate_task
- •memory.py -- pgvector cosine similarity retrieval + embedding storage
Routers (celery_app/routers/)
- •bookings.py -- Central workflow (status: enquiry to confirmed)
- •quotes.py, invoices.py -- Financial lifecycle
- •conversations.py -- Multi-channel messaging (WhatsApp/SMS/email)
- •src/pages/ -- Route-level components
- •src/lib/api/ -- Typed API clients with auto-refresh interceptor
- •src/stores/ -- Zustand stores for auth, UI state, SSE
Frontend (frontend/)
Claude does not need to know about every utility file. It needs to know where to look and what pattern each area follows.
3. Rules -- what is allowed and what breaks
This is the section that separates productive Claude Code sessions from debugging sessions. Every codebase has non-obvious constraints -- things that work differently from the default, things that break silently, things that a new engineer would get wrong on their first day.
Document them:
## Critical Gotchas
- •Container builds use COPY not volume mount -- code changes require docker compose build, not just restart
- •Celery worker MUST listen on celery queue (not just custom queues) or chord unlock tasks never fire
- •The context column is JSON not JSONB -- use jsonb_exists() instead of the ? operator
- •Boolean columns use Integer (0/1) not Boolean
These four lines have saved me hours. Claude reads them once, and never makes those mistakes. Without them, Claude would confidently write a query using the wrong operator, and you would spend 30 minutes debugging a query that returns nothing.
The Anti-Patterns
Don't include things Claude can figure out from the code. If you are using standard Python conventions, you don't need to say "use snake_case for functions." Claude already knows.
Don't include things that change frequently. Your CLAUDE.md should be stable. If you are updating it every day, you are putting transient information in a permanent file. Use auto-memory (MEMORY.md) for that.
Don't include long explanations. If something needs 200 lines of context, it belongs in a separate document that Claude can read on demand -- not in a file that is loaded on every session.
Don't use /init uncritically. Claude Code's auto-generation is a starting point, not a finished product. It tends to include information Claude can already infer from your code. Manually curate what actually needs to be there.
The Size Question
Here is the reality: Claude Code handles roughly 150-200 instructions reliably per session. Your CLAUDE.md system prompt consumes some of that budget. Every line that does not earn its place is noise that dilutes the lines that do.
Our Bureau CLAUDE.md is 401 lines -- larger than the recommended 200 -- but it covers a genuinely complex system (6 containers, 30+ agents, 4 verticals, 24 specific gotchas). For most projects, you should be well under 200. If you are over, ask yourself: is this something Claude would get wrong without this line? If not, delete it.
2. .claude/rules/ -- Contextual Rules That Load When You Need Them
This is the feature most people have not discovered yet, and it solves the biggest problem with CLAUDE.md: not everything is relevant all the time.
The .claude/rules/ directory holds markdown files with optional path-scoping. Rules without a paths frontmatter load at session start (just like CLAUDE.md). Rules with paths only load when Claude is working on matching files.
# Router Conventions (scoped to celery_app/routers/**/*.py)Every router in this project follows these patterns:
- Session management: SessionLocal() with try/finally
- Multi-tenancy: Every query filters by organization_id
- Auth: Read = get_current_user, writes = require_role("member")
- Pagination: All list endpoints take limit and offset
- Activity feed: Mutations call publish_activity() after flush()
- 404 pattern: Check existence then check org_id
When Claude opens a router file, these rules load automatically. When it is working on frontend code, they don't exist -- no wasted context.
This is the pattern for scaling Claude Code to large codebases. Instead of cramming everything into root CLAUDE.md, you distribute rules to where they are relevant:
.claude/rules/
api-conventions.md # Always loaded
router-patterns.md # paths: celery_app/routers/**/*.py
frontend-components.md # paths: frontend/src/components/**/.tsx
database-gotchas.md # paths: celery_app/models.py
deployment-rules.md # paths: docker-compose.yml, Dockerfile.
The result: Claude gets exactly the right context at exactly the right time, without paying the token cost of loading everything on every session.
3. .claude/skills/ -- Reusable Expert Modes
Skills are where Claude Code stops being a general-purpose assistant and starts being a specialist. A skill is a reusable prompt with metadata that Claude can invoke -- either when you ask for it (via /skill-name) or automatically when the task matches.
What a Skill Actually Is
A directory in .claude/skills/ with a SKILL.md file:
.claude/skills/
code-review/
SKILL.md
deploy-checklist/
SKILL.md
new-router/
SKILL.md
The SKILL.md file has YAML frontmatter and markdown instructions:
--- name: new-router description: Create a new API router following project conventions allowed-tools: Read, Edit, Write, Grep, Glob
# Create New Router
When creating a new router for this project:
- Read celery_app/routers/bookings.py as reference
- Create the new router file in celery_app/routers/
- Follow all 11 router patterns from CLAUDE.md
- Register the router in api.py
- Add the Pydantic request models at the top
- Include the _serialize helper function
- Verify: pagination, multi-tenancy, activity feed hooks
Now when you say "create a new router for certificates," Claude does not improvise. It loads the skill, reads the reference implementation, and follows the exact patterns your codebase uses. Every router comes out consistent.
Skills vs. CLAUDE.md -- When to Use Which
| Need | Use |
|---|---|
| Always-relevant context | CLAUDE.md |
| File-type-specific rules | .claude/rules/ with paths |
| Repeatable workflows | Skills |
| Hard safety guarantees | Hooks |
Skills shine for workflows that happen repeatedly but are not relevant to every session: code reviews, deployment procedures, data migrations, new module scaffolding. Things you would document in a runbook for a human engineer -- except this runbook executes itself.
4. Hooks -- Guardrails That Never Forget
Skills are probabilistic -- Claude chooses to follow them. Hooks are deterministic -- they execute regardless of what Claude decides.
Hooks are shell commands or HTTP calls that fire at specific lifecycle events. They are configured in your settings files (.claude/settings.json for team-shared, .claude/settings.local.json for personal).
Why Hooks Matter
Models forget. Over a long session, Claude might stop running your formatter, forget to run tests, or edit a file it should not touch. Hooks make these things impossible to skip.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "python -m py_compile $CLAUDE_FILE_PATH"
}
]
}
],
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "echo $CLAUDE_FILE_PATH | grep -qE '(auth|billing)/' && echo 'BLOCKED' && exit 2 || exit 0"
}
]
}
]
}
}
The first hook: after every file edit, Python syntax-checks the file. If the syntax is wrong, Claude sees the error immediately -- before it moves on and makes 10 more edits based on a broken file.
The second hook: before any file edit, check if it is in a protected directory. Exit code 2 blocks the action entirely. Claude cannot edit auth or billing files -- period. Not because it was told not to, but because the system will not let it.
The Hook Lifecycle
Claude Code exposes 22 hook events. The ones that matter most for project structure:
| Event | When | Use Case |
|---|---|---|
| PreToolUse | Before tool executes | Block edits to protected files |
| PostToolUse | After tool succeeds | Run formatters, syntax checks |
| UserPromptSubmit | User sends a message | Inject context, validate task scope |
| SessionStart | Session begins | Load environment, check prerequisites |
| Stop | Claude finishes | Run final validation, generate summary |
The Power Pattern: Hooks + Skills
The most effective Claude Code setups combine hooks and skills:
- •Hook ensures every Python edit is syntax-valid (deterministic, can not be skipped)
- •Skill defines how to write a new router (probabilistic, but consistent when invoked)
- •CLAUDE.md provides the architectural context for both (always loaded)
- •Rules scope domain-specific patterns to relevant files (loaded on demand)
This is a layered system. Each layer handles a different kind of knowledge at a different level of enforcement.
5. docs/ and Local CLAUDE.md -- Progressive Context Loading
Claude Code does not need everything in memory. It needs to know where truth lives.
The docs/ Directory
There is no magic treatment of a docs/ directory -- Claude reads files on demand, like any other directory. But that is exactly the point. A well-organized docs directory becomes Claude's reference library:
docs/
architecture.md # System design overview
SDD.md # Software Design Document
adr/ # Architecture Decision Records
001-celery-over-temporal.md
002-pgvector-over-pinecone.md
003-egress-proxy-pattern.md
runbooks/
deployment.md
incident-response.md
database-migration.md
Reference these from CLAUDE.md:
## Documentation
- •Architecture overview: docs/architecture.md
- •Design decisions: docs/adr/ (read before proposing changes)
- •Deployment procedures: docs/runbooks/deployment.md
Claude does not load these at startup. It reads them when it needs them -- when you ask it to make an architectural change, it reads the ADRs. When you ask it to deploy, it reads the runbook. Progressive context loading, not upfront context dumping.
Local CLAUDE.md for Danger Zones
Some parts of your codebase have hidden complexity that the root CLAUDE.md can not cover without becoming massive. Subdirectory CLAUDE.md files solve this.
These files load lazily -- only when Claude reads files in that directory. This is the key feature. A CLAUDE.md in src/auth/ only loads when Claude is actually working on auth code, not when it is editing a frontend component.
celery_app/
CLAUDE.md # Backend-specific patterns
routers/
CLAUDE.md # Router conventions (lazy load)
services/
CLAUDE.md # Service layer rules
signals/
CLAUDE.md # Signals subsystem gotchas
This pattern -- lazy-loading subdirectory instructions -- is how you scale Claude Code to large codebases without hitting the context ceiling. The root CLAUDE.md stays lean (the 30,000-foot view), and each directory adds its own depth only when Claude is actually working there.
6. Auto-Memory -- Claude's Own Notes
Auto-memory is the feature most people either don't know about or don't configure well. It is Claude Code's ability to write notes to itself that persist across sessions.
How It Works
Claude stores notes in ~/.claude/projects/project-name/memory/MEMORY.md. The first 200 lines are loaded at session start. Additional topic files (like debugging.md or patterns.md) exist in the same directory but load on demand.
Claude writes to memory automatically when:
- •You correct it on something ("No, we use Integer not Boolean for that column")
- •It discovers a pattern it wants to remember
- •You explicitly ask it to remember something
What to Store vs. What Not To
Good for memory:
- •Transformation roadmaps and long-term plans
- •Coupling audits (what needs to change for the next phase)
- •Deployment architecture specifics (especially non-obvious ones)
- •Patterns confirmed across multiple sessions
- •User preferences ("always use bun", "never auto-commit")
Bad for memory:
- •Session-specific context (current task, in-progress work)
- •Information that duplicates CLAUDE.md
- •Speculative conclusions from reading a single file
- •Anything that changes frequently
The Index Pattern
Keep MEMORY.md as a concise index under 200 lines, with links to topic files for depth:
# Project Memory
Architecture
Bureau is being transformed from plumbing-specific to vertical-agnostic.
Three-layer model: Universal kernel + Configurable middleware + Vertical templates.
Deployment -- CRITICAL
Two directories on spark: ~/projects/openclawd_agency/ (SMB mount)
vs ~/openclawd_agency/ (Docker build).
Must rsync before building. Never overwrite .env on spark.
- •patterns.md -- Router patterns, coding conventions
- •personal_agents.md -- Agent data model, family data
See Also
The first 200 lines of MEMORY.md load on every session. Everything else loads when Claude needs it. Structure accordingly -- critical context at the top, deep dives in linked files.
7. Settings -- Permission Boundaries and Team Conventions
Settings files control what Claude can and cannot do. They exist at four scopes:
| File | Scope | Shared? |
|---|---|---|
| Managed policy (OS-level) | Organization | Yes (IT deployed) |
| ~/.claude/settings.json | All your projects | No |
| .claude/settings.json | This project | Yes (git) |
| .claude/settings.local.json | This project, you only | No (gitignored) |
Team-shared conventions (.claude/settings.json -- committed to git):
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "ruff check --fix $CLAUDE_FILE_PATH" }
]
}
]
}
}
Every team member gets the same auto-formatting. No style drift, no "but it works on my machine."
Personal preferences (.claude/settings.local.json -- gitignored):
{
"permissions": {
"allow": [
"Bash(docker compose:)",
"Bash(ssh spark:)"
]
}
}
Your deploy shortcuts. Your machine-specific paths. Your personal permission grants.
8. Putting It All Together
Here is the complete anatomy of a well-structured Claude Code project:
my-project/
CLAUDE.md # Root: purpose, map, gotchas
.claude/
settings.json # Team-shared: hooks, permissions
settings.local.json # Personal: local permissions
rules/
api-conventions.md # Always loaded
router-patterns.md # paths: src/routers/**/*.py
frontend-rules.md # paths: frontend/**/.tsx
skills/
code-review/SKILL.md # /review
new-router/SKILL.md # /new-router
deploy/SKILL.md # /deploy
docs/
architecture.md # Deep reference
adr/ # Decision records
runbooks/ # Operational procedures
src/
auth/
CLAUDE.md # Lazy: auth constraints
api/
CLAUDE.md # Lazy: API patterns
~/.claude/projects/project/memory/
MEMORY.md # Auto-memory index
patterns.md # Confirmed code patterns
debugging.md # Recurring issues and fixes
The Loading Order
Understanding when each piece loads is critical:
- Session start: Root CLAUDE.md + .claude/rules/ (no paths) + MEMORY.md (first 200 lines) + parent directory CLAUDE.md files
- On demand (file access): Subdirectory CLAUDE.md files + .claude/rules/ with matching paths + memory topic files
- On invocation: Skills (manual via /skill-name or auto when description matches)
- Always: Hooks (deterministic, event-driven, cannot be skipped)
The Context Budget
This loading order exists because context is finite. Claude Code's 200K window sounds large, but:
- •System prompt: ~24K tokens
- •CLAUDE.md + rules: 2-8K tokens (depending on size)
- •MEMORY.md: 1-3K tokens
- •Your actual code and conversation: everything else
Quality degrades noticeably around 147-152K tokens. Optimal sessions stay under 80K. This is why progressive loading matters -- don't pay for context you don't need yet.
The Shift Most People Miss
Prompting is temporary. Structure is permanent.
A good prompt helps Claude once. A good CLAUDE.md helps Claude on every session, for every team member, for the life of the project. A well-placed hook prevents a class of mistakes forever. A scoped rule loads exactly when it is needed and costs nothing when it is not.
The developers getting the most out of Claude Code are not writing longer prompts. They are building repositories that make Claude smarter by default -- repos where the structure itself is the instruction.
Once your repository is designed for AI:
- •Claude stops asking questions it should already know the answer to
- •New team members (human or AI) onboard in minutes, not days
- •Mistakes happen once, get documented, and never happen again
- •Every session starts with institutional knowledge, not a blank slate
The model keeps getting smarter. The next version of Claude will be better than this one. But it will still need to know your architecture, your patterns, your gotchas, and your workflows. The projects that have this documented -- structured, scoped, and progressive -- will get disproportionately more value from every model upgrade.
The investment in structure compounds. The investment in prompts does not.
Build the repo. Claude will figure out the rest.
Quick-Start Checklist
If you are starting from zero, do these five things in order:
- Create CLAUDE.md at your project root. Include: what the project does (3-5 lines), where the code lives (functional map, not full tree), how to build/test/deploy (exact commands), and the top 5 things that break non-obviously. Keep it under 200 lines.
- Create .claude/rules/ for file-type-specific patterns. Start with one file for your most opinionated area (API routes, component patterns, database queries). Use paths frontmatter to scope it.
- Create one skill for your most repeated workflow. Code review, new module scaffolding, or deployment -- whatever you explain to Claude most often. Put it in .claude/skills/your-workflow/SKILL.md.
- Add one hook to .claude/settings.json. Start with a PostToolUse hook that runs your linter or syntax checker after every edit. This one hook will prevent more bugs than anything else on this list.
- Let auto-memory work. Don't fight it. When Claude gets something wrong, correct it once -- it will write the correction to memory and never make that mistake again. Check ~/.claude/projects/project-name/memory/MEMORY.md occasionally to make sure it is accurate.
That is it. Five files, maybe an hour of work. The return is every Claude Code session from now on being materially better.
I build Bureau -- a multi-agent AI orchestration platform with 20+ specialized agents across 4 business verticals. Built entirely with Claude Code. The CLAUDE.md alone has saved more engineering hours than any single feature I have shipped.*
