DEV Community

brian austin
brian austin

Posted on

Claude Code CLAUDE.md vs settings.json: which one controls what (and why it matters)

Claude Code CLAUDE.md vs settings.json: which one controls what (and why it matters)

If you've been using Claude Code for more than a week, you've probably edited both CLAUDE.md and .claude/settings.json — and at some point wondered: which file should this go in?

They look similar. They both configure Claude's behavior. But they control completely different things, and mixing them up leads to frustrating results.

Here's the complete breakdown.


The mental model

CLAUDE.md = Claude's brain

settings.json = Claude's permissions

CLAUDE.md is natural language. You write instructions, context, constraints, and preferences. Claude reads it like a colleague reads a briefing doc.

settings.json is machine config. It controls what tools Claude is allowed to use, what commands it can run, what it's blocked from doing.


What CLAUDE.md controls

# Project context
This is a Next.js 14 app using Prisma + PostgreSQL.

# Architecture decisions
- All API routes live in /app/api/
- Database models are in /prisma/schema.prisma
- Never create new /pages/ routes — we're on App Router

# Code style
- TypeScript strict mode always on
- Prefer async/await over .then() chains
- Use Zod for all input validation

# Response format
- Give me code first, explanation after
- Skip obvious comments — I can read the code
- When suggesting a fix, show the before AND after
Enter fullscreen mode Exit fullscreen mode

Things that belong in CLAUDE.md:

  • Tech stack and architecture
  • Coding conventions and style preferences
  • Business domain context (what the app does, who uses it)
  • Response format preferences
  • Things to always/never do
  • Commands to run for common tasks

What settings.json controls

{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git add:*)",
      "Bash(git commit:*)",
      "Bash(cat:*)",
      "Bash(grep:*)",
      "Bash(find:*)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(git push:*)",
      "Bash(curl:*)",
      "WebFetch"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Things that belong in settings.json:

  • Which bash commands Claude can run without asking
  • Which tools are blocked entirely
  • Auto-approve settings for trusted operations
  • Model preferences (if you're on Pro)

The key difference: CLAUDE.md is read, settings.json is enforced

Claude can choose to ignore something in CLAUDE.md if it thinks it knows better. (It shouldn't, but it can.) Settings.json is a hard fence — Claude literally cannot execute a command that's in the deny list.

This is why:

  • Style preferences go in CLAUDE.md — you want Claude to understand and apply them, not be mechanically blocked
  • Safety constraints go in settings.json — you want actual enforcement, not suggestions

Real-world example: preventing accidental deploys

Wrong approach — putting it in CLAUDE.md:

# IMPORTANT
Never run `git push` or deployment commands without asking me first.
Enter fullscreen mode Exit fullscreen mode

Claude will try to follow this. But if it's confident a push is needed, it might do it anyway.

Right approach — settings.json:

{
  "permissions": {
    "deny": [
      "Bash(git push:*)",
      "Bash(npm run deploy:*)",
      "Bash(vercel:*)"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Now it's impossible, not just discouraged.


Real-world example: project conventions

Wrong approach — trying to encode this in settings.json: You can't. Settings.json doesn't support natural language rules.

Right approach — CLAUDE.md:

# Database conventions
- All new tables need a `created_at` and `updated_at` column
- Foreign key names: {table}_{column}_fkey
- Always use UUIDs for primary keys, never auto-increment integers
- Migrations live in /prisma/migrations/ �� never edit them by hand
Enter fullscreen mode Exit fullscreen mode

Claude reads this, understands it, and applies it when writing schema changes.


The inheritance question

Both files support inheritance — but differently.

CLAUDE.md inheritance (directory-based):

~/.claude/CLAUDE.md          # global rules for all projects
/project/CLAUDE.md           # project rules
/project/src/api/CLAUDE.md   # subdirectory rules
Enter fullscreen mode Exit fullscreen mode

All three are read and merged, with deeper files taking precedence.

settings.json inheritance (also directory-based):

~/.claude/settings.json      # global permissions
/project/.claude/settings.json  # project permissions
Enter fullscreen mode Exit fullscreen mode

Permissions merge — if global denies git push but project allows it, the project setting wins.


My combined setup

Here's what my two files look like on a typical project:

CLAUDE.md:

# Stack
Node.js 20, Express 5, PostgreSQL 16, Redis

# Rules
- Tests live in /test/ mirroring /src/ structure
- All routes must have input validation (Zod)
- Log with winston, never console.log in production code
- Environment variables always via process.env, never hardcoded

# Workflow  
- After any schema change, remind me to run migrations
- When creating a new API endpoint, scaffold test file too
- Prefer small focused functions over large ones

# Response style
- Code first, then brief explanation
- If you see a related issue while fixing something, mention it but don't fix it unless I ask
Enter fullscreen mode Exit fullscreen mode

.claude/settings.json:

{
  "permissions": {
    "allow": [
      "Bash(npm run test:*)",
      "Bash(npm run lint:*)",
      "Bash(npm install:*)",
      "Bash(git status:)",
      "Bash(git diff:*)",
      "Bash(cat:*)",
      "Bash(ls:*)",
      "Bash(grep:*)"
    ],
    "deny": [
      "Bash(git push:*)",
      "Bash(rm -rf:*)",
      "Bash(DROP TABLE:*)"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Quick reference

What you want File
Coding style rules CLAUDE.md
Architecture context CLAUDE.md
Domain knowledge CLAUDE.md
Response format preferences CLAUDE.md
Block dangerous commands settings.json
Auto-approve safe commands settings.json
Prevent git push settings.json
Project tech stack info CLAUDE.md
Tool restrictions settings.json

One more thing: the API alternative

If you're using Claude Code heavily, you've probably noticed the rate limits. One thing that helps: routing Claude Code through a direct API endpoint instead of Anthropic's consumer tier.

The ANTHROPIC_BASE_URL environment variable lets you point Claude Code at any compatible endpoint:

export ANTHROPIC_BASE_URL=https://simplylouie.com/api
Enter fullscreen mode Exit fullscreen mode

SimplyLouie runs a flat-rate Claude API proxy at $2/month — same claude-3-5-sonnet-20241022 model, no per-token billing, no rate limit anxiety. If you're doing serious Claude Code work, the math tends to favor flat-rate.


The CLAUDE.md vs settings.json confusion trips up a lot of Claude Code users. Once you have the mental model — brain vs fence — the right file for each thing becomes obvious.

What's in your CLAUDE.md that probably shouldn't be? Drop it in the comments.

Top comments (0)