A guide for teams using Claude Code effectively.
Your CLAUDE.md is the single most important file for Claude Code. It reads this at the start of every conversation.
- Project description — One line. What does this project do?
- Stack — Runtime, framework, database, deployment target
- Commands — dev, test, build, deploy. Claude will run these.
- "Use X, not Y" directives — The most effective format. Claude follows these reliably.
- Project structure — Where things live in the repo.
- Code style — Naming conventions, error format, import patterns.
- Git workflow — Commit message format, branch naming.
- Full API documentation — use context7 MCP for live docs instead
- Tutorials or explanations of why — Claude doesn't need justification, just directives
- Information that changes frequently — put that in code comments
- Secrets or env-specific values — use
.envfiles - Code snippets that might go stale — use
@filereferences instead ("prefer pointers over copies")
Under 150 lines. Claude reads this every time — bloated files dilute the important directives. Move detailed context into .claude/rules/ files (see section 9 below).
Use `Bun.serve()` for HTTP servers. Do NOT use express.This directive format ("Use X. Do NOT use Y.") is what Claude follows most reliably.
CLAUDE.md can reference other files that get loaded into context:
For Bun API details, see @.claude/rules/bun-conventions.md
For agent guide, see @agents.mdImports support:
- Relative paths:
@agents.md,@.claude/rules/testing.md - Home directory:
@~/.claude/my-global-rules.md - Recursive imports up to 5 hops deep
- Ignored inside code spans:
`@anthropic/package`won't import
Break large tasks into steps. Instead of "build the entire auth system", try:
- "Create the user model in src/lib/models/user.ts with email and password fields"
- "Add login and register endpoints in src/routes/auth.ts"
- "Write tests for the auth endpoints"
- "Add JWT token generation in src/lib/auth.ts"
Each step gives Claude clear scope and lets you verify before moving on.
Always point Claude to existing patterns:
"Add a /api/posts route following the same pattern as
the health endpoint in src/index.ts"
This is far more effective than describing the pattern from scratch.
- Context — What problem are you solving? What part of the codebase?
- Reference — Which existing file or pattern should be followed?
- Expectation — What should the output look like?
- Files — Which files to create or modify?
If you want Bun native APIs only, say so. Claude defaults to popular npm packages unless told otherwise.
- New features touching 3+ files
- Database schema changes
- Architecture decisions
- "I'm not sure how to approach this"
- Refactoring across modules
In Plan Mode, Claude explores the codebase, considers alternatives, and presents an approach for your approval before writing code.
- Bug fixes with known cause
- Adding a test
- Small single-file changes
- Running commands (test, deploy)
- Following an established pattern
Every commit runs lint + typecheck + test. If it fails, the commit is blocked.
When the hook blocks you:
- Read the error output
- Fix the issue
- Stage the fix
- Commit again
Do NOT try to skip the hook. The issue is real.
After every file Write or Edit, Biome auto-formats the changed file. This keeps code consistent without any manual formatting steps. The hook:
{
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'FILE=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r \".file_path\"); bunx biome check --write \"$FILE\" 2>/dev/null; exit 0'",
"timeout": 10
}
]
}
]
}For teams that need different behavior per environment:
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'if [ \"$RAILWAY_ENVIRONMENT\" = \"production\" ]; then echo \"BLOCKED: No direct commands in production\" >&2; exit 1; fi'"
}
]
}
]
}Block commits that include console.log:
{
"PreCommit": [
{
"hooks": [
{
"type": "command",
"command": "bash -c '! git diff --cached --diff-filter=ACM | grep -q \"console.log\" || (echo \"Remove console.log before committing\" >&2; exit 1)'"
}
]
}
]
}Claude Code supports three hook handler types:
- command — Run a shell script. Receives JSON on stdin, communicates via exit codes.
- prompt — Send a prompt to a Claude model for yes/no evaluation.
- agent — Spawn a subagent with Read, Grep, Glob tools for multi-turn verification.
The most useful MCP for Bun projects. Bun's API evolves fast and Claude's training data may be outdated.
How to trigger it: Include "use context7" in your prompt:
"What's the current API for Bun.serve() WebSocket handling? Use context7."
When to use it:
- Bun API questions
- Any library where you need current docs
- Verifying if an API still exists or has changed
For problems that need step-by-step thinking:
- Database schema design
- Debugging with multiple possible causes
- Migration planning
- Architecture trade-offs
- One purpose per command —
/qadoes quality checks,/deploy-devdoes deployment - Include safety checks — Deploy commands should verify code quality first
- Use
$ARGUMENTS— Make commands flexible:/test authruns auth tests - List
allowed-tools— Restrict what tools the command can use - Handle failures — Tell Claude what to do when a step fails
Add a .md file to .claude/commands/:
---
allowed-tools: ["Bash", "Read"]
description: "What this command does"
---
Instructions for Claude. Use $ARGUMENTS for user input.The filename becomes the command: my-command.md → /my-command
- Never put secrets in CLAUDE.md, settings.json, or committed files
- Use
.env(gitignored) for local secrets - Use Railway dashboard or GCP Cloud Run console for deployed env vars
- The
.env.examplefile shows required vars without values
.claude/settings.jsonhas an allow/deny list- Production deploys are denied by default — forces use of
/deploy-prodor/deploy-gcp-prodwith safety checks - The PreToolUse hook blocks dangerous shell patterns
- Use
/reviewbefore merging - The security-guidance plugin flags common vulnerabilities
- Watch for: SQL injection, XSS, exposed secrets, missing validation
| File | Committed? | Scope |
|---|---|---|
CLAUDE.md |
Yes | Team conventions |
.claude/settings.json |
Yes | Team plugins, permissions, hooks |
.claude/rules/*.md |
Yes | Team context rules (auto-loaded) |
.claude/settings.local.json |
No | Personal overrides |
.claude/commands/*.md |
Yes | Team slash commands |
.mcp.json |
Yes | Team MCP servers |
agents.md |
Yes | Team agent guide (imported by CLAUDE.md) |
.env |
No | Personal env vars |
.env.example |
Yes | Env var template |
- Clone the repo
cp .env.example .env— fill in valuescp .claude/settings.local.json.example .claude/settings.local.jsonbun install- Read
CLAUDE.mdandagents.md - Run
/devto start the server - Run
/qato see the quality pipeline - Try
/new-feature test-featureto see scaffolding
Treat CLAUDE.md like code:
- When you establish a new pattern, add it to CLAUDE.md or a rules/ file
- When a pattern changes, update the relevant file
- Review CLAUDE.md and rules/ changes in PRs like any other code
- Use the
claude-md-managementplugin to help maintain it
Instead of cramming everything into CLAUDE.md, use .claude/rules/ files. These are auto-loaded by Claude Code without needing @import.
- Auto-loaded — Claude reads all
.claude/rules/*.mdat session start - Path-scoped — Rules can activate only for specific file patterns
- Organized — Separate concerns: Bun conventions, testing, deployment, security
- Focused — Claude gets the right context at the right time
Rules can be conditionally loaded based on which files Claude is working on:
---
paths: ["**/*.test.ts", "**/*.test.tsx"]
---
# Testing Rules
Use bun:test exclusively...This testing rule only loads when Claude is working on test files.
| File | Scope | Contents |
|---|---|---|
bun-conventions.md |
All files | Bun native APIs, HTML imports pattern |
testing.md |
*.test.ts files |
bun:test conventions, co-location |
deployment.md |
All files | Railway + GCP Cloud Run |
security.md |
All files | Secrets, permissions, hooks |
Create a .md file in .claude/rules/:
---
paths: ["src/routes/**"]
---
# API Route Conventions
- All routes return Response.json()
- Error format: { error: string, details?: string }
- Validate input with Zod schemas