Build
Claude Code Best Practices From Real Projects
Checked 2026-10-31
Loading poll...
Good Claude Code practice comes down to one split: tell it what it cannot see, give it a check it can run, and enforce the rules that cannot have exceptions with settings instead of prose. By the end of this guide you will have a short CLAUDE.md, a deny list, a formatter hook, a read-only reviewer subagent and a cost routine, all as files you can commit. Facts checked 31 October 2026.
What you will have at the end
- A project
CLAUDE.mdunder 200 lines, plus path-scoped rules - A committed
.claude/settings.jsonwith allow, ask and deny rules - Two hooks: one that formats edits, one that blocks protected files
- A reviewer subagent that cannot edit anything
- A skill for one team procedure
- A short list of cost and context habits for the whole team
This is the third Claude Code guide here. The first-run guide covers install and basics. The tutorial builds a small app. This one is about team setup, so if you have never used Claude Code, start with the first-run guide.
Prerequisites
- Claude Code installed and in daily use on at least one project
- A repo your team shares, with a test command and a lint or type check
- Permission to commit files under
.claude/to that repo jqinstalled, because the hook examples use it to read JSON
The principle: two kinds of instruction
The best-practices page splits every instruction into two kinds.
Non-deterministic (Claude decides) | Deterministic (Claude Code enforces) | |
|---|---|---|
What to do | Prompt, | Hooks |
What it can touch | Auto mode's classifier | Deny rules, sandbox, managed settings |
When it is done | A request for evidence, | Tests, typecheck, a |
Who has the last word | Claude's summary | Branch protection, a person who approves the merge |
Most of the time you want Claude to decide, because current models are good at judging whether a guideline fits the code in front of them. Use the right column only for rules with zero exceptions. A useful test from the docs: if you keep adding emphasis to a CLAUDE.md line and Claude still misses it, the line belongs in a hook. The reverse holds too. If you have hooks for things that are only preferences, delete them.
Practice 1: Keep CLAUDE.md short and about what Claude cannot see
Claude reads your code well. It finds the file layout, the test framework and the conventions without help. So write down only what it cannot find, or would spend minutes rediscovering each session. The docs give a two-question test: could it find this by reading the code, and would it actually go and look? If both are yes, leave it out.
Good entries:
- The exact commands to confirm work, including how to run a single test file
- Places where the code misleads, such as an old pattern new code must not copy
- Decisions that live outside the repo, such as how deploys work
- Anything you have corrected twice
# Commands
- Install: pnpm install
- Run one test file: pnpm vitest run path/to/file.test.ts
- Typecheck: pnpm tsc --noEmit
# Conventions
- Use the error helpers in src/lib/errors.ts, never throw raw strings
- src/legacy/ is still live but don't copy its patterns into new code
# Gotchas
- Integration tests need DATABASE_URL set, see .env.exampleRules for the file itself, from the memory docs:
- Be concrete. "Use 2-space indentation" beats "format code properly". "Run
npm testbefore committing" beats "test your changes". - Stay under 200 lines. Imports help you organize but do not reduce context cost, because imported files load with the file.
- No contradictions. If two files disagree, Claude may pick one arbitrarily.
- Audit it. Run
/doctor prompt-auditto find instructions written for older models, references to missing files, and contradictions. It reports first and changes nothing until you ask. The docs list it as needing Claude Code v2.1.283 or later. - Start with `/init`. It generates a first version from your code. Then mostly delete.
Where each file lives
Scope | Location | Shared with |
|---|---|---|
Managed policy | macOS | Everyone in the organization |
User |
| You, in all projects |
Project |
| The team, through git |
Local |
| You, in this project |
If another tool in your team already uses AGENTS.md, Claude Code can read it. The memory docs describe when each loads and how to share one file; see our AGENTS.md guide and CLAUDE.md guide.
Scope a rule to one folder
If a rule matters in only one part of the codebase, put it in .claude/rules/ with a paths: filter, so it loads only when Claude works there.
---
paths:
- "src/api/**/*.ts"
---
- Validate request bodies with the zod schemas in src/api/schemas/, don't hand-roll checks
- Every new endpoint needs an entry in openapi.yaml, CI fails without itPractice 2: Give Claude a check, and ask for evidence
Claude stops when the work looks done. Without a pass or fail check, "looks done" is all it has. Put the command in CLAUDE.md and ask for the output, not just the claim. The docs give four levels of strictness:
- In the prompt: "run the auth tests after, and keep going until they pass."
- As a goal:
/goal all tests in tests/auth pass and lint is cleansets a condition a separate model checks after each turn. It matters most for long background runs. - As a Stop hook: the harness refuses to let the turn end until your script passes.
- By someone who did not write it:
/code-reviewreviews the diff in a fresh subagent.
The Stop hook version, in .claude/settings.json:
{
"hooks": {
"Stop": [
{
"hooks": [
{ "type": "command", "command": "pnpm tsc --noEmit 1>&2 || exit 2" }
]
}
]
}
}Exit code 2 blocks the stop, and stderr goes back to Claude as the reason. After eight blocks in a row with no tool call between them, Claude Code overrides the next block and ends the turn. Raise the cap with CLAUDE_CODE_STOP_HOOK_BLOCK_CAP if your check legitimately needs more rounds. Write the hook to read stop_hook_active and exit 0 if it is already true, so it cannot loop.
Practice 3: Use permission rules and the sandbox, not prompts, for "never"
Deny rules hold in every permission mode, including auto and bypassPermissions. That is why secrets belong there.
{
"permissions": {
"allow": ["Bash(pnpm tsc --noEmit)"],
"ask": ["Bash(git push *)"],
"deny": ["Read(./.env)", "Read(./.env.local)", "Read(./secrets/**)"]
}
}Know the edges, as the docs list them:
- A
Readdeny rule does catchcat .env, because Claude Code recognizes common file-reading commands. - It does not catch a script that opens the file.
- A
WebFetchdeny rule for a domain does not stopcurlto that domain unless the sandbox is on.
For an OS-level limit on files and network, turn on the sandbox with /sandbox. It confines Claude's shell commands on macOS, Linux and WSL2, and picks up your Read deny rules and WebFetch(domain:...) rules. Strict sandbox mode, in the /sandbox Overrides tab, turns off the retry-outside-the-sandbox request.
Pick a permission mode on purpose
Mode | Use it when | Watch out for |
|---|---|---|
Manual ( | Unfamiliar repos, first week | Approval fatigue after about ten prompts |
| You review diffs in git anyway | Shell commands still prompt |
| Big or risky changes | No edits until you approve the plan |
| Daily work on trusted repos | A classifier judges each action, so it can be wrong |
| Disposable containers only | Skips prompts, including for |
Auto mode blocks things like curl | bash, production deploys and migrations, force pushes over other people's work, and sensitive data sent to external endpoints. After three blocks in a row, or twenty in total, it pauses and goes back to asking. If the classifier keeps stopping something routine in your setup, add it to autoMode.allow in ~/.claude/settings.json, and keep "$defaults" in every list you set, or your entries replace the built-in rules. The classifier ignores autoMode in project settings, so that block cannot live in the repo.
To stop anyone using the wide modes, set permissions.disableBypassPermissionsMode or permissions.disableAutoMode to "disable". These are most useful in managed settings.
Who a rule applies to
File | Applies to |
|---|---|
| You alone |
| Everyone who uses the repo, once committed |
Managed settings | The organization. No personal file can override them |
Two details: deny rules in a committed project file apply right away, but its allow rules wait until each person accepts the trust dialog for that folder. And if a tool is denied at any level, no other level can allow it.
Practice 4: Use hooks for steps that must always happen
Hooks run your command at fixed points: before or after a tool call, when Claude tries to stop, at session start. Use a command or http hook for real enforcement. A prompt or agent hook asks another model to decide, so it is not deterministic.
Format every edit:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "npx prettier --write --ignore-unknown \"$(jq -r '.tool_input.file_path')\"" }
]
}
]
}
}Block edits to protected files. Save as .claude/hooks/protect-files.sh, run chmod +x on it, and register it as a PreToolUse hook on Edit|Write.
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
FILE_PATH="${FILE_PATH//\\//}"
for pattern in ".env" "package-lock.json" ".git/" "migrations/"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh" }
]
}
]
}
}Facts to keep in mind from the hooks guide:
- A
PreToolUsehook that denies blocks the tool even inbypassPermissionsmode. PostToolUsehooks cannot undo an action, because the tool has already run.Stophooks fire whenever Claude finishes responding, not only at task completion, and not on user interrupts.- Command hooks time out after 10 minutes by default. Override with
timeoutin seconds. - Matchers are case-sensitive. If a hook does not fire, run
/hooksand check the event and matcher. - Test a hook by piping sample JSON into the script and checking
echo $?. - Keep the enforced set small. Every hook will sometimes fire when it should not, and someone has to maintain each. At forty hooks, most are probably preferences.
For more, see our Claude Code hooks guide.
Practice 5: Delegate with subagents, knowing the cost
Claude delegates on its own. Broad searches go to the built-in Explore agent, and background subagents keep your main conversation moving. Subagents pay off for parallel work, for keeping long output out of the main chat, for a reviewer who has not seen how the code was written, and for a helper with fewer tools.
What they cost is mostly tokens, since each sends its own requests against the same usage. A fresh subagent also has to gather context your conversation already has, so it can be slower. And by default it is the same model or a smaller one, so you get parallelism or a fresh view, not a smarter answer.
Project subagents live in .claude/agents/ and are checked into git. This reviewer cannot edit anything, and the limit is enforced by Claude Code, not requested in a prompt:
---
name: reviewer
description: Reviews changed files against the spec they were built from. Use after implementing a feature, before opening the PR.
tools: Read, Grep, Glob
model: haiku
---
You're reviewing code you didn't write. Compare the changed files against the spec you're given,
check that every requirement is implemented and the listed edge cases have tests,
and report only the gaps that affect correctness. Skip style.The model: haiku line is optional. The docs suggest it for simple jobs such as collecting files that import a module. For a reviewer, decide with your own results. Field names are camelCase and must match exactly, because Claude Code silently ignores a field it does not recognize. See subagents and how to write a subagent.
Practice 6: Put team procedures in skills
A skill is a procedure saved at .claude/skills/<name>/SKILL.md. Claude sees only the name and description each session, and reads the full file when the task calls for it, so a long release procedure does not weigh on an unrelated bug fix. For anything with side effects, add disable-model-invocation: true, so it only runs when a person types the command.
---
name: release
description: Cut a new release. Bumps the version, writes the changelog entry, tags it and pushes the tag so CI can publish.
disable-model-invocation: true
---
1. Make sure you're on main with a clean tree. If not, stop and ask.
2. Run `pnpm changeset version` and commit the result.
3. Summarize the PRs merged since the last tag into CHANGELOG.md, one line each, user-facing changes only.
4. Tag `v<new version>` and push the tag. CI publishes from there, never publish from your machine.See skills and how to write a Claude Code skill.
Practice 7: Connect tools sparingly
If a system has a command-line tool, use that. With gh installed and signed in, Claude opens pull requests and reads review comments without help. For systems without a CLI, add an MCP server:
claude mcp add --transport http notion https://mcp.notion.com/mcpConnected servers add their tool names and instructions to context even when unused, so turn off the ones you do not need in /mcp. Add one server you will really use, not five. Compare options in best MCP servers.
Practice 8: Control cost and context
The docs say not to change defaults until a task costs more than it should. When it does, /usage is the first stop. On subscription plans it breaks usage down by skill, subagent, plugin and MCP server, and flags patterns like long context or frequent cache misses once one passes 10% of usage.
/clearbetween unrelated tasks. Every request sends the whole conversation./compactwith a focus, for example/compact keep the API changes and the test commands./effortfor thinking depth. Opus 5.5, Sonnet 5.5 and Haiku 5.5 default to medium. Raise it for bugs in unfamiliar code, lower it for quick renames, and savemaxfor hard problems you want solved without you.- Watch background work: a
/loop, a goal's check after each turn, and Claude's own subagents keep sending requests while you are away. - For non-interactive runs,
--max-turnsand--max-budget-usdwith-pcap the work.
The prompt cache lasts about an hour on a subscription and five minutes on an API key, cloud provider or usage credits. Subagents get five minutes unless you change it.
For team budgets, the costs page reports an enterprise average of about $13 per developer per active day and $150 to $250 per developer per month, with 90% of users under $30 per active day. It advises a small pilot group to set your own baseline.
Practice 9: Steer early
Watch the first minute. Esc stops Claude and keeps everything up to that point. /rewind, or Esc twice on an empty prompt, rolls back code, conversation or both. After two failed corrections, /clear and one better prompt beat a third correction. Checkpoints cover files edited with Claude's own tools, not files a shell command changed, and they work alongside git, not instead of it. For multi-day work, /rename oauth-migration and later claude --resume oauth-migration bring the session back.
What can go wrong
Problem | Cause | Fix |
|---|---|---|
Claude ignores a CLAUDE.md line | File too long, vague, or in conflict | Shorten, rewrite concretely, run |
Subdirectory CLAUDE.md seems missing | It loads on demand, not at launch | Ask Claude to read a file in that folder, watch for the |
Instructions "lost" after compaction | Context was summarized | Put must-keep rules in CLAUDE.md or a hook |
Deny rule did not stop a script | Rules match tool calls, not intent | Add the sandbox for OS-level limits |
Hook does nothing | Wrong event or case-sensitive matcher |
|
Hook errors with "jq: command not found" |
| Install it, or parse JSON in Python or Node |
Stop hook loops | Check never passes | Fix the check, read |
Auto mode keeps pausing | Three blocks in a row, or twenty in total | Add trusted infrastructure to |
Subagent bills pile up | Many fresh contexts | Use |
Teammates see different behavior | Local settings differ | Commit shared rules to |
Privacy and team policy
Decide before rollout, not after.
- Training and retention depend on account type. Consumer plans (Free, Pro, Max, Ultra) depend on a privacy setting you choose. Team, Enterprise, API and third-party platforms are covered by commercial terms, where Anthropic says it does not train on code or prompts unless the customer opts in. Standard retention on commercial terms is 30 days.
- Transcripts are stored in plain text on each developer's machine under
~/.claude/projects/for 30 days by default. Treat them as sensitive. - Managed settings give an admin one place for policy that no personal file can override, including
allowManagedHooksOnlyandallowManagedPermissionRulesOnly. See the admin setup page for how to deploy them. - Secrets go in deny rules and the sandbox, never only in a CLAUDE.md sentence.
Checklist
- CLAUDE.md under 200 lines, with commands, gotchas and decisions only
- Folder-specific rules in
.claude/rules/withpaths /doctor prompt-auditrun once and findings applied- Deny rules for
.env, secrets and other no-go files - Sandbox enabled for shared or sensitive repos
- A Stop or PostToolUse hook for the one check that must always run
- A read-only reviewer subagent in
.claude/agents/ - One release or deploy procedure as a skill with
disable-model-invocation: true - No more than a handful of hooks and MCP servers
/usagereviewed after the first week, with a team baseline noted- Data-use settings and retention agreed with whoever owns compliance
What this guide uses
Item | Type | Page |
|---|---|---|
Claude Code | Agent | |
Anthropic | Provider | |
Skills | Add-on | |
Subagents | Add-on | |
Code reviewer subagent | Add-on | |
Claude Code cheat sheet | Add-on | |
Claude Code MCP servers | Add-on list | |
Claude pricing | Pricing |
Questions
How long should a CLAUDE.md be?
The memory docs target under 200 lines per file. Longer files use more context and reduce adherence. Move rules that matter for one folder into .claude/rules/ with a paths filter.
When should a rule be a hook instead of a CLAUDE.md line?
When it has no exceptions. CLAUDE.md is context the model reads and may weigh. A hook, a deny rule or a managed setting is applied by Claude Code itself every time.
Are deny rules safe in auto mode?
Deny rules hold in every permission mode, including auto and bypassPermissions. Auto mode's classifier runs after deny and ask rules and PreToolUse hooks.
Should every task use subagents?
No. They cost tokens, and a fresh subagent has to gather context your main conversation already has. Use them for parallel work, noisy output, an independent reviewer, or a tool-limited helper.
Loading poll...