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.md under 200 lines, plus path-scoped rules
  • A committed .claude/settings.json with 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
  • jq installed, 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, CLAUDE.md, skills

Hooks

What it can touch

Auto mode's classifier

Deny rules, sandbox, managed settings

When it is done

A request for evidence, /goal, /code-review

Tests, typecheck, a Stop hook, CI

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.example

Rules for the file itself, from the memory docs:

  1. Be concrete. "Use 2-space indentation" beats "format code properly". "Run npm test before committing" beats "test your changes".
  2. Stay under 200 lines. Imports help you organize but do not reduce context cost, because imported files load with the file.
  3. No contradictions. If two files disagree, Claude may pick one arbitrarily.
  4. Audit it. Run /doctor prompt-audit to 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.
  5. 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 /Library/Application Support/ClaudeCode/CLAUDE.md, Linux and WSL /etc/claude-code/CLAUDE.md, Windows C:\Program Files\ClaudeCode\CLAUDE.md

Everyone in the organization

User

~/.claude/CLAUDE.md

You, in all projects

Project

./CLAUDE.md or ./.claude/CLAUDE.md

The team, through git

Local

./CLAUDE.local.md, added to .gitignore

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 it

Practice 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:

  1. In the prompt: "run the auth tests after, and keep going until they pass."
  2. As a goal: /goal all tests in tests/auth pass and lint is clean sets a condition a separate model checks after each turn. It matters most for long background runs.
  3. As a Stop hook: the harness refuses to let the turn end until your script passes.
  4. By someone who did not write it: /code-review reviews 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 Read deny rule does catch cat .env, because Claude Code recognizes common file-reading commands.
  • It does not catch a script that opens the file.
  • A WebFetch deny rule for a domain does not stop curl to 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 (default)

Unfamiliar repos, first week

Approval fatigue after about ten prompts

acceptEdits

You review diffs in git anyway

Shell commands still prompt

plan

Big or risky changes

No edits until you approve the plan

auto

Daily work on trusted repos

A classifier judges each action, so it can be wrong

bypassPermissions

Disposable containers only

Skips prompts, including for .git and .claude writes

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

.claude/settings.local.json, ~/.claude/settings.json

You alone

.claude/settings.json

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 PreToolUse hook that denies blocks the tool even in bypassPermissions mode.
  • PostToolUse hooks cannot undo an action, because the tool has already run.
  • Stop hooks 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 timeout in seconds.
  • Matchers are case-sensitive. If a hook does not fire, run /hooks and 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/mcp

Connected 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.

  • /clear between unrelated tasks. Every request sends the whole conversation.
  • /compact with a focus, for example /compact keep the API changes and the test commands.
  • /effort for 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 save max for 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-turns and --max-budget-usd with -p cap 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 /doctor prompt-audit

Subdirectory CLAUDE.md seems missing

It loads on demand, not at launch

Ask Claude to read a file in that folder, watch for the Loaded line

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

/hooks, then check names

Hook errors with "jq: command not found"

jq is not installed

Install it, or parse JSON in Python or Node

Stop hook loops

Check never passes

Fix the check, read stop_hook_active

Auto mode keeps pausing

Three blocks in a row, or twenty in total

Add trusted infrastructure to autoMode in user settings

Subagent bills pile up

Many fresh contexts

Use /usage, set model: haiku for simple jobs, ask for short summaries

Teammates see different behavior

Local settings differ

Commit shared rules to .claude/settings.json, policy to managed settings

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 allowManagedHooksOnly and allowManagedPermissionRulesOnly. 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/ with paths
  • /doctor prompt-audit run 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
  • /usage reviewed 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

/agents/claude-code/

Anthropic

Provider

/providers/anthropic/

Skills

Add-on

/add-ons/skills/

Subagents

Add-on

/add-ons/subagents/

Code reviewer subagent

Add-on

/add-ons/code-reviewer-subagent/

Claude Code cheat sheet

Add-on

/add-ons/claude-code-cheat-sheet/

Claude Code MCP servers

Add-on list

/add-ons/claude-code-mcp-servers/

Claude pricing

Pricing

/pricing/claude-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...

Sources

  1. code.claude.com/docs/en/best-practices
  2. code.claude.com/docs/en/memory
  3. code.claude.com/docs/en/permissions
  4. code.claude.com/docs/en/hooks-guide
  5. code.claude.com/docs/en/sub-agents
  6. code.claude.com/docs/en/costs
  7. code.claude.com/docs/en/data-usage

Part of the Wbcom Designs family

The all-in-one WordPress community stack

Also ours: wbcomdesigns.comvapvarun.combrndle.com