Claude Code Best Practices: Taming the Terminal Agent

The agent that can actually touch your codebase

Claude Code is a different beast from the chat windows we compared in the field guide. It reads your repository, writes files, runs commands, and iterates on its own until you tell it to stop.

That power cuts both ways. Handled well, it clears multi-file tasks in minutes. Handled badly, it rewrites your auth flow because you said "fix the login bug." Here's how to get the first outcome, consistently.

The anatomy of a good session

A productive Claude Code session has five layers. Each one catches problems the previous one let through:

┌─────────────────────────────────────────────────────┐
│ 1. CLAUDE.md   → persistent rules and context       │
│ 2. PLAN MODE   → agree on the approach first        │
│ 3. PERMISSIONS → decide what it can touch alone     │
│ 4. THE LOOP    → small tasks, verify, commit        │
│ 5. HOOKS       → automate the boring guardrails     │
└─────────────────────────────────────────────────────┘

Layer 1: CLAUDE.md — teach it your codebase once

CLAUDE.md is a markdown file Claude Code reads automatically at the start of every session. It's the closest thing to onboarding a new hire — and it works exactly as well as you write it.

What belongs in CLAUDE.md:            What doesn't:
──────────────────────────────        ──────────────────────────
Commands (test, lint, typecheck)      Secrets and API keys
Architecture ("X talks to Y via Z")   The whole architecture diagram
Conventions (naming, file layout)     Anything a new dev should
Existing patterns to follow           learn by reading the code
Things to never touch (generated
files, migrations, seed scripts)

A minimal, high-signal version:

# Project: Next.js 13 App Router + MongoDB (Mongoose)

## Commands
- Typecheck: yarn typecheck
- Tests:     yarn test (node --test, do not add a test framework)
- Lint:      yarn lint

## Conventions
- Validation lives in lib/validation with Zod schemas
- API routes: thin handlers, delegate to lib functions
- Never edit generated code or migration files
- MDX content uses tilde fences: ~~~ not backticks

## Gotchas
- MongoDB connection is a singleton — reuse it, don't reconnect
- Node 20+: scripts run with node --experimental-strip-types

The rule: CLAUDE.md is a README for the agent. Keep it under ~100 lines. Every rule you write there is one less mistake it can make.

Layer 2: Plan mode — agree before it writes

Claude Code has a plan mode (Shift+Tab to toggle) where it researches and proposes an approach without changing anything. Use it whenever a task touches more than a couple of files.

You: "Add an unsubscribe endpoint to the newsletter."

[plan mode]
Claude Code reads the codebase and proposes:
1. Add GET /api/unsubscribe/[token] route
2. Store token on the Subscriber model (add field)
3. Link in every broadcast email
4. Update the subscriber admin page

You: "Do 1, 2, 4 — skip broadcasts for now."

[exit plan mode]
Claude Code executes the agreed scope. No surprises.

The point isn't the plan. The point is that you reviewed the approach before any file changed. A wrong plan caught in planning costs a minute. A wrong plan caught after implementation costs a review.

Layer 3: Permission modes — decide what it can do alone

Claude Code asks before it runs anything risky. You choose the default posture:

┌──────────────────┬────────────────────────────────────┐
│ MODE             │ WHAT IT MEANS                      │
├──────────────────┼────────────────────────────────────┤
│ Default          │ Asks before every file write       │
│                  │ and every command                  │
├──────────────────┼────────────────────────────────────┤
│ Accept edits     │ Edits auto-accepted, commands      │
│                  │ still prompt                       │
├──────────────────┼────────────────────────────────────┤
│ Plan mode        │ Read-only until you approve        │
│                  │ a plan                             │
├──────────────────┼────────────────────────────────────┤
│ YOLO mode        │ Everything allowed. Sandbox it,    │
│ (--dangerously-  │ or don't use it on machines        │
│ skip-permissions)│ with real credentials.             │
└──────────────────┴────────────────────────────────────┘

For daily work, default or accept-edits is right. Reserve full autonomy for throwaway environments.

You can also pre-approve safe commands in `.claude/settings.json` so you're not clicking "yes" forty times a day:

{
  "permissions": {
    "allow": [
      "Bash(yarn typecheck:*)",
      "Bash(yarn lint:*)",
      "Bash(yarn test:*)",
      "Bash(yarn prettier:*)",
      "Read(./**)"
    ],
    "deny": ["Read(./.env*)", "Bash(git push:*)"]
  }
}

The rule: Allow fast, safe operations. Deny anything irreversible — pushes, deletes, env files.

Layer 4: The loop — small tasks, verify, commit

This is where Claude Code rewards discipline. The unit of work is the small, verifiable task, not the feature.

❌ "Build the newsletter admin page with subscribers,
    broadcast, analytics, and CSV export"

✅ "Add the /api/admin/newsletter broadcast route.
    Zod-validate the body. Return 401 when not admin.
    Use the Resend batch send already in lib/resend.ts."

The second one gives the agent everything it needs: where, what, and what done looks like. Then verify before moving on:

┌─────────────────────────────────────────┐
│ BEFORE SAYING "NEXT TASK"               │
├─────────────────────────────────────────┤
│ [ ] yarn typecheck passes               │
│ [ ] yarn test passes                    │
│ [ ] You read the diff — every file      │
│ [ ] You ran the feature in the browser  │
│ [ ] Commit as its own unit              │
└─────────────────────────────────────────┘

The rule: One task, one verification, one commit. Never let the agent run for twenty minutes and then try to review everything at once.

Layer 5: Hooks — deterministic guardrails

The agent will eventually forget to format a file or try to touch something it shouldn't. Hooks fix that permanently. They're shell commands Claude Code runs at fixed lifecycle points, configured in `.claude/settings.json`:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write $CLAUDE_FILE_PATHS"
          }
        ]
      }
    ]
  }
}

Common patterns:

PostToolUse (Edit|Write) → auto-format the edited file
PreToolUse  (Bash)       → block rm -rf, force-pushes,
                           anything you'd never type yourself
Stop                     → run the test suite when a task ends
Notification             → desktop ping when it needs input

The rule: Anything you'd otherwise check by hand after every task is a hook waiting to be written.

Context management: the context window is a budget

Claude Code reads a lot: files, diffs, command output, your conversation. All of it shares one context window, and quality degrades as it fills.

Context spenders, ranked:            Recovery moves:
─────────────────────────────        ─────────────────────────
Huge command outputs                 /clear between unrelated tasks
Big files it didn't need             /compact to summarize and continue
Long meandering conversations        /add-dir to add a second repo
Repeated re-reads of the same        instead of pasting its contents
files

Signs it's time to reset:

  • It re-reads files it already read
  • Answers get generic or forget earlier constraints
  • It edits the wrong section of a file

The rule: If the session feels slow or confused, the context is polluted. `/clear` and restate the task with specifics — it's cheaper than debugging a confused agent.

Subagents: heavy research, clean context

Claude Code can spawn subagents — task-scoped helpers with their own context window that report back a summary. Use them for the research-heavy parts of a task:

Main thread: "Find every place we validate request bodies."
   → spawns a subagent
   → subagent greps, reads, summarizes:
     "3 routes use Zod via lib/validation; 2 do
      manual checks (articles seed, notes)."
   → main thread continues with a 3-line answer
     instead of 15 files of raw output

That's the pattern: heavy exploration in the subagent, decisions in your main thread. Your context stays clean for the work that matters.

Cost control: the bill is a feedback loop

Usage scales with context and turns. The cheapest wins first:

1. Small tasks beat big ones   (fewer correction loops)
2. CLAUDE.md beats re-explaining the setup every session
3. /clear aggressively         (old context costs tokens)
4. Read-only questions → plan mode or plain chat, not the agent
5. Model downshift for chores: docstrings, renames, comments

Security: it runs your shell

Claude Code executes commands with your user's permissions. That deserves respect:

┌─────────────────────────────────────────────────────┐
│ NON-NEGOTIABLES                                     │
├─────────────────────────────────────────────────────┤
│ • Never YOLO mode on a machine with prod            │
│   credentials or cloud CLIs logged in               │
│ • Deny .env* reads via permissions                  │
│ • Review every command it proposes — the approval   │
│   dialog is not a formality                         │
│ • Prompt injection applies here too: untrusted      │
│   content (issues, web pages, logs) can contain     │
│   instructions. Treat what it reads as input,       │
│   not as commands.                                  │
└─────────────────────────────────────────────────────┘

This is the same principle from the prompt injection article: the model's context is an attack surface. An agent with shell access just makes the stakes concrete.

The 8-step workflow (your cheat sheet)

1. Write CLAUDE.md once        → every session benefits
2. Pre-approve safe commands   → stop clicking "yes"
3. Plan mode for big tasks     → review before code exists
4. Small task in               → one file, one feature slice
5. Verify: typecheck + tests   → run them, don't trust them
6. Read the full diff          → you own every line
7. Commit                      → checkpoint before next task
8. /clear between topics       → keep context clean

The agent is genuinely capable. The practices above aren't about limiting it — they're about steering it. Point it at small, verifiable slices with good context, and it does its best work.

Further reading: Claude Code Documentation, Claude Code Hooks Guide, Claude Code Best Practices (Anthropic Engineering), and OWASP Top 10 for LLM Applications.