AI / GenAI  /  Claude Code

๐Ÿ“„ Claude Code (CLAUDE.md) Guide 3 of 4 16 guides ยท updated 2026

Writing effective CLAUDE.md files โ€” project memory, conventions, permissions, hooks, and the team practices that keep AI coding agents genuinely useful.

Tool Permissions & Allowlists in Claude Code

CLAUDE.md tells Claude Code what your project is and how it works. Itโ€™s a separate, complementary mechanism โ€” permission configuration โ€” that controls what the agent is allowed to actually do without asking first. Understanding the boundary between the two matters: CLAUDE.md is advisory context, permissions are an enforcement layer.


Why This Is a Separate System

A CLAUDE.md instruction like โ€œnever run destructive database commandsโ€ is a strong signal of intent, but itโ€™s still just text the agent reads and reasons about. Permission configuration is different in kind: itโ€™s a structural control over which tool calls require explicit human approval versus which are allowed to proceed automatically, enforced regardless of what any given instruction says. This separation matters because you donโ€™t want the safety boundary for genuinely risky operations to depend entirely on the agent correctly interpreting a paragraph of prose under every possible circumstance.


The Basic Shape

Permissions live in project or user settings (.claude/settings.json or .claude/settings.local.json), not in CLAUDE.md โ€” a deliberate separation, since permissions are a security/access-control concern with different review and change-management needs than project documentation.

{
"permissions": {
"allow": [
"Bash(npm test:*)",
"Bash(npm run lint:*)",
"Read(*)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force:*)"
]
}
}

Commands matching an allow pattern run without interrupting for confirmation; commands matching deny are blocked outright; anything else falls back to prompting for approval each time. This gives a team explicit, reviewable control over the automation/safety tradeoff, rather than leaving it as an implicit judgment call made fresh in every session.


Project vs Personal Settings

Similar to CLAUDE.mdโ€™s project-file-vs-user-file split, permission configuration distinguishes between project-level settings (checked into git, shared with the team โ€” the baseline everyone gets) and personal/local settings (not committed, layered on top for individual preference). A team might commit an allowlist for common, safe commands (npm test, npm run lint) while individual engineers add their own additional trusted commands locally without imposing those on teammates.

.claude/settings.json โ† committed, team-wide baseline
.claude/settings.local.json โ† gitignored, personal additions

Referencing Permissions From CLAUDE.md

While the enforcement lives in settings, itโ€™s often worth a brief pointer in CLAUDE.md so the reasoning is documented alongside the projectโ€™s other context, especially for anything non-obvious about why a particular command is denied:

## Notes on Automation
Database migration commands require manual approval even though other build
commands are allowlisted โ€” a bad migration is expensive to undo, so we keep a
human in the loop for that specific category. See `.claude/settings.json` for
the exact allow/deny configuration.

This isnโ€™t required โ€” the settings file is the actual enforcement โ€” but it helps a human reader understand the reasoning without having to separately go find and parse the JSON.


Common Mistakes

Trying to enforce safety purely through CLAUDE.md instructions. A strongly-worded prohibition in prose is a good signal of intent, but itโ€™s not a hard boundary the way a deny rule is โ€” anything genuinely risky (force pushes, destructive database operations, credential access) deserves an actual permission rule, not just a documentation note.

Over-broad allowlist patterns. Bash(*) defeats the purpose of having a permission system at all โ€” allowlist patterns should be as narrow as the actual safe use case requires (Bash(npm test:*), not blanket shell access).

Committing personal, overly-permissive settings to the shared project file. An individualโ€™s own trust threshold for automation shouldnโ€™t become the whole teamโ€™s default โ€” broadly permissive preferences belong in personal/local settings, not the committed project baseline.

Frequently Asked Questions

Does a CLAUDE.md instruction override a permission deny rule? No โ€” permission rules are the enforcement layer and take precedence; an instruction saying โ€œitโ€™s fine to run Xโ€ doesnโ€™t bypass an explicit deny configured for X.

Should every command be explicitly allowlisted, or is a default-prompt fallback acceptable? A default-prompt fallback for anything not explicitly categorized is a reasonable, safe default โ€” allowlisting is for genuinely routine, low-risk commands where repeated manual confirmation is pure friction, not a requirement to pre-categorize everything.

Can permission configuration be scoped per-directory the way CLAUDE.md can? Settings follow a similar project/personal layering, though the exact scoping mechanics are a separate configuration surface from CLAUDE.mdโ€™s own subdirectory discovery โ€” check current settings documentation for the precise behavior in a specific setup.

Summary

Permissions and CLAUDE.md solve different problems: CLAUDE.md informs, permissions enforce. Genuinely risky operations deserve an explicit deny rule or an approval requirement, not just a prose warning โ€” and routine, safe commands are worth allowlisting explicitly so they stop requiring repeated manual confirmation. Keep the reasoning behind non-obvious permission choices documented in CLAUDE.md even though the actual enforcement lives elsewhere.