DEV Community

Evan-dong
Evan-dong

Posted on

10 Claude Code Settings I Wish I'd Configured on Day One

I used Claude Code daily for six months before I admitted the obvious: the defaults were costing me 10–15 hours a month. Not because the tool is bad — because I never configured it. The agent would wander off, forget decisions mid-session, or confidently rewrite code I hadn't asked it to touch, and I'd clean up after it.

This isn't a "what is Claude Code" tutorial. It's the settings I landed on if you already use it and want more out of it. Each section ends with copy-paste config.

1. Cap CLAUDE.md at 8,000 characters

My biggest early mistake was dumping everything into CLAUDE.md. By March it was 40,000 characters. I assumed more context meant better output. Wrong — and the reason is structural: transformers are bad at retaining info in the middle of long documents. Past a certain size the agent stops seeing middle-of-file rules. I asked it to reproduce a rule from line 300 of its own CLAUDE.md; it couldn't.

Hard cap the main file at 8,000 characters. Everything else goes into files loaded on demand:

.claude/
├── CLAUDE.md              # always-on context, ≤ 8,000 chars
├── skills/
│   ├── nestjs.md          # NestJS session rules
│   ├── migration.md       # TS migration rules
│   └── testing.md         # test strategy
└── specs/
    ├── domain-model.md
    ├── api-contracts.md
    └── infra.md
Enter fullscreen mode Exit fullscreen mode

CLAUDE.md holds only what every session needs: stack, hard prohibitions, links to specs and skills. After the refactor: 6,000 chars, more accurate answers, ~35% lower session-init token cost.

2. settings.json — the permissions that change your rhythm

Most devs never open settings.json. Add this and read/test commands stop interrupting you:

{
  "permissions": {
    "allow": [
      "Bash(git log:*)",
      "Bash(git diff:*)",
      "Bash(git status:*)",
      "Bash(npm test:*)",
      "Bash(npm run lint:*)",
      "Bash(npm run typecheck:*)"
    ],
    "deny": [
      "Bash(git push:*)",
      "Bash(git reset --hard:*)",
      "Bash(rm -rf:*)"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Interruptions per session went from 15–20 to 2–3. deny outranks allow, so dangerous commands stay blocked. Commit .claude/settings.json and the whole team gets the same guardrails.

3. acceptEdits — enable it selectively, not globally

acceptEdits removes per-edit confirmations. It helps for a specific class of work:

Enable for: type migrations (JS→TS, fixing any), mechanical fixes (renames, import swaps, formatting), test generation against existing logic, docs/comments.

Don't enable for: new features, architectural refactors, auth/payment/data-layer changes, or any task where you can't state the "done" criteria in 10 seconds.

Real example: I enabled it for "clean up unused imports." The agent tidied the imports, then decided to rewrite a few services while it was in there. Valid, worse than our patterns, 30 unplanned minutes of review. My rule: acceptEdits only when automated tests can catch the mistake.

4. Hooks as guardrails — the 3 that earned their keep

Hooks are the most underrated feature. Shell scripts that run before/after tool calls, receiving JSON context on stdin.

Stop hook — log every session:

#!/bin/bash
# ~/.claude/hooks/stop.sh
TASK=$(cat /tmp/claude_current_task 2>/dev/null || echo "no task recorded")
echo "$(date '+%Y-%m-%d %H:%M') | $TASK" >> ~/.claude/session_history.log
Enter fullscreen mode Exit fullscreen mode

A month of this gave me an honest report: ~2h15m/day with the agent, 4–5 sessions. I thought I used it "occasionally." The data disagreed.

PreToolUse hook — block dangerous commands. Return exit 2 to block and pass stderr back to the agent:

#!/bin/bash
# ~/.claude/hooks/pre_tool.sh
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
DANGEROUS="(rm -rf|DROP TABLE|truncate|DELETE FROM.*WHERE 1=1|git reset --hard|kubectl delete)"
if echo "$COMMAND" | grep -qiE "$DANGEROUS"; then
  echo "BLOCKED: potentially dangerous command: $COMMAND" >&2
  exit 2
fi
exit 0
Enter fullscreen mode Exit fullscreen mode

Fired 7 times in 3 months. 5 were reasonable (I ran them manually after review); 2 I was glad it caught.

Notification hook — ping when done:

#!/bin/bash
osascript -e 'display notification "Claude Code finished the task" with title "Claude Code"'
# Linux: notify-send "Claude Code" "Task done"
Enter fullscreen mode Exit fullscreen mode

Wiring them up:

{
  "hooks": {
    "Stop": [{ "matcher": "", "hooks": [{ "type": "command", "command": "~/.claude/hooks/stop.sh" }] }],
    "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "~/.claude/hooks/pre_tool.sh" }] }]
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Multi-model council for architecture decisions

For hard-to-roll-back decisions (module layout, data schema, service contracts), I run the same task across models in parallel:

Opus:   "Design [X]. Priority: reliability and explicit contracts"
Sonnet: "Design [X]. Priority: development speed"
Haiku:  "Design [X]. Minimal complexity, minimal abstraction"
Enter fullscreen mode Exit fullscreen mode

Then a fourth Opus session synthesizes the best of the three against our context. ~20–25 minutes. On my last big refactor it replaced a week of team debate and landed cleaner than a 2–3 person discussion would have.

6. "Effort levels" — match depth to the task

I formalized this as a custom skill with four levels. (/effort is my skill, not built in; the built-in way to deepen reasoning is keywords like think / ultrathink.)

/effort low     fast draft, no plan, just code        → small utilities, throwaway
/effort medium  short plan, then implement            → standard features, fixes
/effort high    detailed plan, then implement         → new components, public API changes
/effort max     analyze → plan → approval → implement → report; stop for OK at each step
                                                       → core changes, auth, schema migrations
Enter fullscreen mode Exit fullscreen mode

Zero full rollbacks on /effort max in six months. Bonus: if /effort max feels like overkill for a task, the task is easier than you thought. The reverse holds too.

7. Spot context rot early and restart

Long sessions degrade. Three tells:

  • The agent re-proposes something you already rejected this session
  • It suggests changing code it just wrote
  • It asks you to "clarify the task" you gave 10 messages ago

Don't push through. Restart with a short handoff:

# Context to continue
**Task:** [one sentence]
**Done:** [file/function]: [what], ...
**Ruled out (discussed):** [option]: [why], ...
**Next step:** [one concrete item]
Enter fullscreen mode Exit fullscreen mode

3–5 minutes to fill out, 5–10x faster than dragging a rotted session. My personal trigger: 3 messages with no forward progress → restart, no debate.

8. The two-correction rule

If you correct the agent twice on the same point in one session, stop. That's a signal the task framing or context is wrong, not that the agent is stubborn. Rebuild the task three ways:

  1. Split smaller. Two failures on X may mean X is too big. Solve X1 then X2.
  2. Give a concrete example of the desired output instead of "do it right."
  3. Write the skeleton (structure, signatures) and let the agent fill it in. "Here's the service interface, implement it" beats "write a service for X."

Estimated 15–20 hours saved over six months.

9. Split settings by environment

Settings merge hierarchically: team .claude/settings.json, personal .claude/settings.local.json (gitignored), user-global ~/.claude/settings.json.

Team (strict, committed):

{ "permissions": { "deny": ["Bash(kubectl delete:*)", "Bash(terraform destroy:*)", "Bash(rm -rf:*)"] } }
Enter fullscreen mode Exit fullscreen mode

Personal (local, gitignored):

{ "permissions": { "allow": ["Bash(docker compose up --build:*)", "Bash(npm run db:reset:*)"] } }
Enter fullscreen mode Exit fullscreen mode

You move fast locally without losing the shared guardrails — deny outranks allow, so a local allow can't override a team deny. You can also switch permission modes at launch (--permission-mode plan) for investigation-only sessions.

10. Skills: lazy-load project-specific context

Skills are markdown files in .claude/skills/, loaded on demand. The point: don't keep project-specific context in the agent's memory full-time.

.claude/skills/
├── nestjs-conventions.md
├── clean-arch.md
├── typescript-strict.md
├── git-workflow.md
├── testing-strategy.md
└── code-review-checklist.md
Enter fullscreen mode Exit fullscreen mode

Each maps to a task type — writing a NestJS module → nestjs-conventions, refactoring → clean-arch, reviewing → code-review-checklist. After moving to skills: main CLAUDE.md from 40K to 6K chars, 30–40% lower token use in standard sessions, and the agent stopped mixing rules from different domains.

Where this leads

Once you're running multiple models in parallel (#5), the next question isn't prompt quality — it's routing and cost: who uses which model, and what does each workflow cost? That part lives outside Claude Code. Routing the CLI through a gateway gives you per-workflow, per-model cost visibility across providers; the EvoLink Claude Code CLI guide documents that setup if your team's at that stage.

Takeaway

Claude Code isn't plug-and-play — it's a platform you configure. Defaults run at "fine"; the right settings move it to "genuinely fast." By my estimate these paid back 50–60 hours in the first six months. If you've got settings that didn't make my list — especially hooks — drop them in the comments.


Claude Code docs


tags: claude-code, ai, developer-tools, productivity

Top comments (0)