Paths Subjects Questions Quizzes Pricing Search

Claude Code Best Practices: Reliable, Cheap, High-Leverage Sessions

The operating habits that separate a session you babysit from a session you can walk away from

Claude Code Best Practices: Reliable, Cheap, High-Leverage Sessions

The earlier subjects in this track taught the mechanisms: the agentic loop and permission modes (Claude Code Fundamentals), durable instructions and hooks (CLAUDE.md and Context Configuration), plan mode and autonomous loops (Plan Mode and Autonomous Workflows), and MCP servers plus subagent fan-out (MCP Servers and Subagent Orchestration). This subject is about operating the tool well: the habits that make a session finish correctly on the first pass, cost a fraction of what a careless session costs, and stay recoverable when it goes sideways.

Almost every practice below is a consequence of one constraint that Anthropic's own guidance puts at the top: the context window fills up fast, and performance degrades as it fills. Every message, every file Claude reads, every command's output lands in that window and is re-sent on every subsequent turn. Once you internalise that, most of the "tips" stop being folklore and start being obvious: keep the window lean, give Claude a check it can run so you aren't the verification loop, plan before editing so a wrong assumption doesn't spread across ten files, and correct early because a polluted context gets worse, not better.

The interview framing is real, too. "How do you use AI coding tools well?" is now a routine senior-engineer question, and the strong answer is not "I ask it nicely" — it is a concrete account of context hygiene, verification loops, permission scoping, and cost discipline. That is what this subject gives you.


The Mental Model: An Agent, a Toolbelt, and a Finite Window

Claude Code is a model running a loop over tools (read, search, edit, run commands, spawn subagents), inside a fixed-size context window. Three consequences follow, and every later section is a corollary of one of them:

┌────────────────────────────────────────────────────────────────┐
 CONTEXT WINDOW (finite; re-sent on EVERY turn)                  
  system prompt  CLAUDE.md  auto memory  tool schemas         
  ─────────────────────────────────────────────────────────────  
  your prompt  tool call  tool output  tool call  output    
  (every file read, every test log, every grep result stays)     
└────────────────────────────────────────────────────────────────┘
                                           
         you manage what goes in            Claude decides the next
         (/clear, /compact, subagents,      action from what it sees;
          scoped prompts, hooks that        if it can't run a check,
          pre-filter output)                "looks done" is its only signal
  1. You own the context budget. Claude does not prune its own window; you do — by clearing between tasks, compacting with instructions, delegating noisy work to subagents, and writing prompts that don't trigger a 200-file exploration.
  2. You own verification. Per the docs: "Claude stops when the work looks done. Without a check it can run, 'looks done' is the only signal available, and you become the verification loop." Give it a test, a build, a linter, a screenshot diff — anything that returns pass/fail into the conversation.
  3. Cost is a function of context × turns. Each tool call is another request carrying the whole conversation (at cached rates, but still). A lean 20K-token context over 40 tool calls processes ~0.8M input tokens; the same task with a bloated 100K context processes ~4M. Same work, five times the tokens, and worse output.

Everything below is how to act on those three facts.


Context Management

Start narrow, and clear between tasks

The single highest-leverage habit is /clear between unrelated tasks. The docs' "kitchen sink session" anti-pattern — start one task, ask something unrelated, go back to the first — leaves the window full of irrelevant material that dilutes attention on every later turn. /clear starts a fresh conversation (and resets the /usage session totals). If you want the old thread back later, /rename it first, then /resume.

Scope investigations narrowly. "Investigate the auth system" invites Claude to read hundreds of files; "read src/auth/ and tell me how token refresh works" does not. If you genuinely need a wide exploration, delegate it (below).

/compact with focus instructions

Claude Code auto-compacts as the window nears its limit, summarising older history. You can trigger it yourself and steer what survives:

/compact Focus on the API changes and the list of files we modified

You can also put standing compaction instructions in CLAUDE.md (e.g., "When compacting, always preserve the full list of modified files and any test commands"). Two mechanical details worth knowing: the project-root CLAUDE.md is re-read from disk after compaction, but instructions given only in chat are exactly what gets summarised away — so promote important mid-session rules into CLAUDE.md. And /compact is itself a large request (it reads everything it summarises); when you want a fresh start rather than continuity, /clear costs nothing.

For a question you don't want to become part of the record, /btw asks a side question whose answer never enters conversation history. And /context shows what is currently consuming the window — check it before blaming the model.

Why long sessions degrade

Three things compound in a long session: (a) the model's attention is spread across material that is no longer relevant, so instruction adherence drops; (b) failed approaches accumulate — after two corrections on the same issue, the docs recommend /clear and a better initial prompt rather than a third correction, because "a clean session with a better prompt almost always outperforms a long session with accumulated corrections"; (c) every turn re-sends all of it, so cost per turn rises even as quality falls.

Subagents to keep the main context clean

Since context is the constraint, subagents are the primary tool for protecting it. A subagent (spawned via the Agent tool — formerly named Task, and that alias still works in settings) runs in its own context window and returns only a summary. Use it for exactly the operations that flood a window: broad codebase exploration, running a verbose test suite, reading a 10,000-line log, fetching documentation. Ask for it explicitly — "use subagents to investigate how our authentication system handles token refresh" — or fan out several in parallel: "research the auth, database, and API modules in parallel using separate subagents." Claude Code also ships a read-only Explore subagent it delegates to for searches. The mechanics, cost curve, and worktree isolation for parallel writers are covered in the MCP Servers and Subagent Orchestration subject; the best-practice point here is simply: if a step will produce more output than you need to keep, don't run it in your main context.

Two related context savers from the docs: prefer CLI tools (gh, aws, gcloud, sentry-cli) over MCP servers where both exist, since CLIs add no per-tool listing to context (MCP tool definitions are deferred by default — only the tool's name enters context until Claude actually calls it — but the CLI still wins when both are available); and hooks can pre-filter output before Claude sees it (e.g., a PreToolUse hook that rewrites npm test to pipe through grep -E 'FAIL|ERROR' | head -100, turning tens of thousands of tokens into hundreds). A code intelligence plugin (/plugin install typescript-lsp@claude-plugins-official and equivalents for other languages) is a third: a single "go to definition" call replaces a grep followed by reading several candidate files, and installed language servers report type errors after edits without running a compiler.


Give Claude a Way to Verify Its Work

This is Anthropic's headline recommendation, and it changes the character of a session more than any other practice. Without a check, you review; with a check, Claude iterates until it passes and you review the evidence.

Instead of Say
"implement a function that validates email addresses" "write a validateEmail function. example test cases: user@example.com is true, invalid is false, user@.com is false. run the tests after implementing"
"make the dashboard look better" "[paste screenshot] implement this design. take a screenshot of the result and compare it to the original. list differences and fix them"
"the build is failing" "the build fails with this error: [paste error]. fix it and verify the build succeeds. address the root cause, don't suppress the error"

The check can be anything that returns a signal Claude can read: a test suite, a build exit code, a type-checker, a linter, a script diffing output against a fixture, or a browser screenshot (via the Chrome extension) compared against a design.

The TDD-style loop

The workflow Anthropic recommends for bug fixes is explicitly test-first: "write a failing test that reproduces the issue, then fix it." Generalised into a loop:

1. Ask for tests from the spec first   "write tests for X covering cases A, B, C.
                                          do NOT write the implementation yet.
                                          run them and confirm they fail."
2. (Optionally commit the tests.)
3. "now implement X until those tests pass. don't modify the tests."
4. Claude edits  runs tests  reads failures  edits   until green.
5. Ask for evidence: the test command and its output, not "done".

The negative constraints in steps 1 and 3 matter: an agent that can edit tests will sometimes make a red suite green the wrong way. The docs' Writer/Reviewer idea applies here too — "have one Claude write tests, then another write code to pass them" — because a fresh context isn't biased toward the code it just wrote.

How hard should the check gate the stop?

The docs give four escalating options: (1) in one prompt — "run the tests and iterate until they pass"; (2) across a session — set the check as a /goal condition, evaluated by a separate model after every turn; (3) as a deterministic gate — a Stop hook that runs your check and blocks the turn from ending until it passes (Claude Code overrides it after 8 consecutive blocks); (4) as a second opinion — a verification subagent that tries to refute the result. Each step trades setup for attention; the prompt version works today, the /goal and hook versions are what let an unattended run finish correctly. /goal is covered in the Plan Mode and Autonomous Workflows subject.

Whatever the level, have Claude show evidence rather than assert success — the test output, the command and what it returned, a screenshot. Reviewing evidence is faster than re-running the verification yourself.


Explore → Plan → Implement → Verify

Letting Claude jump straight to code risks solving the wrong problem. The recommended four-phase workflow:

EXPLORE   Shift+Tab until "⏸ plan mode on" (or claude --permission-mode plan)
          "read src/auth and understand how we handle sessions and login."
PLAN      "I want to add Google OAuth. What files need to change? Create a plan."
          Ctrl+G opens the plan in your editor to hand-edit before approving.
IMPLEMENT approve the plan (or Shift+Tab out); "implement the OAuth flow from
          your plan. write tests for the callback handler, run the suite, fix failures."
VERIFY/   evidence of the check  "commit with a descriptive message and open a PR"
COMMIT

When plan mode pays off — and when it doesn't. The docs are direct: plan mode adds overhead. Skip it for typo fixes, log lines, renames — "if you could describe the diff in one sentence, skip the plan." Use it when you are uncertain about the approach, when the change touches multiple files, or when you're unfamiliar with the code. Wrong assumptions are cheap to fix in a paragraph of plan and expensive to fix once baked into ten files. (Plan mode's approval flow is covered in the Plan Mode and Autonomous Workflows subject.)

For larger features, have Claude interview you first. A prompt like "I want to build [X]. Interview me in detail using the AskUserQuestion tool. Ask about technical implementation, UI/UX, edge cases, concerns, and tradeoffs. Keep going until we've covered everything, then write a complete spec to SPEC.md" surfaces decisions you hadn't made. Then start a fresh session to execute the spec — the implementation session gets clean context, and you have a written artefact to review against. Good specs name files and interfaces, state what's out of scope, and end with an end-to-end verification step.


Effective Prompting for Agents

Claude can infer intent but not read your mind. The docs' before/after table is the pattern to copy:

Strategy Vague Specific
Scope the task "add tests for foo.py" "write a test for foo.py covering the edge case where the user is logged out. avoid mocks."
Point to sources "why does ExecutionFactory have such a weird api?" "look through ExecutionFactory's git history and summarize how its api came to be"
Reference existing patterns "add a calendar widget" "look at how existing widgets are implemented on the home page. HotDogWidget.php is a good example. follow the pattern… build from scratch without libraries other than the ones already used"
Describe the symptom "fix the login bug" "users report login fails after session timeout. check the auth flow in src/auth/, especially token refresh. write a failing test that reproduces it, then fix it"

The recurring ingredients: a file or module (use @src/auth/session.ts — Claude reads it before responding, and @ also pulls in the CLAUDE.md files along that path), acceptance criteria (test cases, expected output, a screenshot), an example to imitate ("follow the pattern in X"), and negative constraints ("avoid mocks", "no new dependencies", "don't touch the migrations folder", "don't suppress the error"). Give URLs for docs rather than describing an API from memory (/permissions can allowlist frequently used domains), paste images directly, and pipe data in: cat error.log | claude.

Vague prompts are not always wrong — "what would you improve in this file?" is a legitimate exploratory ask when you can afford to course-correct. The failure is being vague when you actually have a precise outcome in mind.


Course-Correcting Early

Sessions are persistent and reversible; use that. The best results come from tight feedback loops, and the cost of a wrong turn grows with every step Claude takes after it.

  • Esc stops Claude mid-action. Context is preserved, so you redirect: "stop — the bug is in session handling, not the login form."
  • Esc Esc or /rewind opens the rewind menu: restore the conversation, the code, or both to a previous checkpoint (every prompt creates one; files are snapshotted before edits). This is what makes "try the risky approach; if it fails, rewind" a cheap experiment. Checkpoints only cover Claude's own file edits — not Bash side effects — so they are not a git replacement.
  • "Undo that" — ask Claude to revert its own change.
  • Edit and resubmit. If the plan is wrong, edit the plan (Ctrl+G) or rewind to your prompt and rewrite it — don't argue with a half-built implementation.
  • The two-corrections rule. If you've corrected the same issue twice, stop correcting: /clear, write a better initial prompt that incorporates what you learned, and start fresh.
  • Git is the real undo. Work on a branch, commit checkpoints often (asking Claude to commit is fine), and review git diff before merging.

CLAUDE.md Hygiene

The full design of CLAUDE.md, auto memory, .claude/rules/, and hooks is the CLAUDE.md and Context Configuration subject; here is the operating discipline distilled from the docs:

  • Short, curated, actionable. Target under 200 lines. For each line ask: "Would removing this cause Claude to make mistakes?" If not, cut it. Bloated files cause Claude to ignore your actual instructions.
  • Include commands Claude can't guess, style rules that differ from defaults, test runners, repo etiquette, environment quirks, gotchas. Exclude anything derivable from the code, standard conventions, long tutorials, file-by-file descriptions, "write clean code."
  • Iterate like code. Add an entry when Claude makes the same mistake twice, when review catches something it should have known, or when you type the same correction you typed last session. Prune when things go wrong; test edits by observing whether behaviour actually shifts. /doctor will propose trims of derivable content.
  • Adding to it. Ask Claude directly ("add this to CLAUDE.md") or open it with /memory; /init generates a starting file. (Older material described a #-prefix shortcut for saving memory; it isn't in the current documentation, so use /memory or an explicit ask.) Note that "remember X" phrasing routes to auto memory, not CLAUDE.md.
  • Move task-specific procedures out. Workflows that only matter sometimes belong in skills (.claude/skills/<name>/SKILL.md), which load on demand; path-specific conventions belong in .claude/rules/ with paths: frontmatter. Both keep the always-loaded file small.
  • Emphasis works ("IMPORTANT", "YOU MUST") for the few rules that truly matter — but if Claude keeps violating a rule despite it, the file is probably too long and the rule is lost in noise. If it must hold every time, it isn't a CLAUDE.md line at all — it's a hook.
  • Verify it loaded with /context (Memory files section) when Claude seems to be ignoring it.

Working in Large Codebases and Monorepos

Everything above scales down to a small repo without any extra thought. In a large single-tree codebase or a monorepo with many packages, the defaults tuned for small projects start working against you: a root CLAUDE.md that tries to cover every subsystem, unscoped searches that read files from packages you'll never touch, and worktrees that check out the entire tree for a task that needs one directory. The fix in every case is the same idea already established above — scope what loads — applied per directory instead of per session.

Where you start claude matters. From the repository root, Claude can read and edit every file, but only the root CLAUDE.md loads at launch; subdirectory files load on demand as Claude reads there. From a subdirectory, Claude is scoped to that subtree until you grant more access, and it loads that directory's CLAUDE.md plus every ancestor's. Pick the starting point that matches the task's actual scope.

Layer CLAUDE.md by directory. A single root file either grows to cover every package (costing context on rules unrelated to the current task) or stays too generic to help. Instead, keep a root file for repo-wide rules — coding standards, commit conventions, repository layout — and one file per package or subsystem (packages/api/CLAUDE.md, src/db/CLAUDE.md) for that area's stack: its test runner, its port, its own gotchas. Starting from packages/api/ loads the root file and packages/api/CLAUDE.md, with nothing from packages/web/ in context. For packages you never work in — another team's code, a legacy subtree — claudeMdExcludes (a glob list in settings) skips their CLAUDE.md and rules files entirely, so it's a personal filter, not a per-task switch: use /context (Memory files) to confirm what actually loaded.

Block reads of what's checked in but never worth reading. .gitignore'd paths like node_modules/ already stay out of search results, but committed build output, generated code, or a vendored SDK need explicit Read deny rules in permissions.deny — e.g. Read(./**/dist/**), Read(./**/*.generated.*), Read(./vendor/**). Deny rules cover Claude's file tools and recognized Bash file commands (cat, head, grep, find) but not arbitrary subprocesses that open files themselves.

Scope worktrees and cross-package access. --worktree normally checks out the whole repository; worktree.sparsePaths in settings writes only the listed directories (plus root-level files) to disk, so a subagent working on packages/api/ doesn't also get a full copy of packages/web/. Pair it with symlinkDirectories for a heavy shared directory like node_modules so worktrees don't each duplicate it. When a task legitimately spans packages — updating a shared type and its call sites — grant access with additionalDirectories in settings (committed, for everyone working in that area) or --add-dir at launch (one-off); note that additionalDirectories never loads that directory's CLAUDE.md or skills, while --add-dir does, if you also set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1.

Give each area its own on-demand skills. A .claude/skills/ directory inside packages/api/ describes API-specific testing patterns; one inside packages/web/ describes component conventions. Neither loads during the other's work, which keeps the "many small, on-demand files beat one large always-loaded one" principle from CLAUDE.md Hygiene working at package scale too. When conventions are shared across many directories and start drifting out of sync, centralize them as a plugin instead of copy-pasting a CLAUDE.md into every package — a platform team versions it once and everyone installs it.

For a change that spans packages, hand Claude the whole change in one session rather than doing it package by package — re-deriving the same decision per package produces inconsistent edits. And for anything that will run long enough to trigger compaction, ask Claude to save the plan to a markdown file before editing: conversation history can be summarized away, but a committed plan file survives.

Goal Mechanism
Load only the conventions for the code you're touching Per-directory CLAUDE.md (root + one per package/subsystem)
Skip CLAUDE.md files for packages you never touch claudeMdExcludes (glob patterns)
Keep Claude from opening build output, generated code, vendored deps Read deny rules in permissions.deny
Find a symbol without a grep-and-read sweep of the tree A code intelligence plugin (/plugin install typescript-lsp@claude-plugins-official)
Check out only what a task needs when creating a worktree worktree.sparsePaths (+ symlinkDirectories for shared deps)
Edit a sibling package or repo from the current session additionalDirectories in settings, or --add-dir at launch
Give one area its own on-demand procedures Per-directory skills under <area>/.claude/skills/

None of this is exclusive to a multi-package monorepo — a single large repository with one src/ tree applies the same techniques per subsystem (src/api/, src/web/, src/db/).


Permissions Strategy

Which mode you start in depends on your plan. On Pro, Max, and Team plans, auto mode is the built-in starting permission mode for interactive terminal and VS Code sessions: a classifier model reviews most actions and blocks only what looks risky — scope escalation, unknown infrastructure, hostile-content-driven actions — so routine work proceeds without a prompt. On other plans, Manual mode is the default: Claude Code asks before file writes, Bash commands, and MCP tool calls. Manual mode is safe but tedious, and "after the tenth approval you're clicking through rather than reviewing." Two tools cut prompts further and apply in either mode — use them instead of the blanket bypass:

  1. Allowlist specific safe commands in permissions.allow (via /permissions or .claude/settings.json): Bash(npm run lint), Bash(npm run test *), Bash(git diff *). Note the syntax: the trailing * (with a space) is prefix matching; Bash(git diff*) would also match git diff-index. Never Bash(*) — it matches on prefix, not intent, and doesn't distinguish npm test from rm -rf. Put genuinely destructive operations in deny explicitly, plus Read(./.env)-style rules for secrets.
  2. Sandboxing (/sandbox, or sandbox.enabled: true in settings) — OS-level filesystem and network isolation for Bash: commands can write only to the working directory and temp dir, and reach only allowlisted domains, with the first new domain prompting for approval. Sandboxed commands can then auto-run.

Auto mode is a real safety layer, but it reduces prompts rather than guaranteeing safety — treat it as a default you supervise, not a substitute for the allowlist/deny and sandbox layers above.

Hooks are the guardrail layer. CLAUDE.md is advisory; a PreToolUse hook that blocks writes to migrations/ or refuses rm -rf runs every time regardless of what Claude decides. Ask Claude to write them ("write a hook that runs eslint after every file edit"), then check /hooks.

bypassPermissions / --dangerously-skip-permissions disables prompts and safety checks. The documentation's warning is unambiguous: "Only use this mode in isolated environments like containers, VMs, or dev containers without internet access, where Claude Code cannot damage your host system." It also refuses to run as root, and there's a one-time acceptance dialog. Treat it as a container-only tool.


Cost Control

Enterprise averages are around $13 per developer per active day and $150–250 per developer per month, with 90% of users staying under $30/day — and unexpectedly high bills usually trace to two habits: long sessions never cleared, and Opus left as the default model. The levers, in rough order of impact:

Lever What to do
Keep context lean /clear between tasks; scoped prompts; subagents for verbose steps; hooks that filter output; CLAUDE.md under 200 lines; disable unused MCP servers (/mcp)
Match the model to the job Sonnet handles most coding; reserve Opus for architecture and hard multi-step reasoning; /model mid-session; model: haiku for simple subagents
Tune reasoning /effort (or effort in /model) for simpler tasks; on fixed-budget models, MAX_THINKING_TOKENS=8000 — thinking tokens bill as output
Watch it /usage (/cost is an alias) shows session tokens and an estimated cost; /context shows what's consuming the window; /insights reports on how you work across recent sessions
Avoid cache misses The first message after a break longer than the cache lifetime reprocesses the full context uncached — that lifetime is an hour on a subscription (5 minutes once you're drawing on usage credits, unless ENABLE_PROMPT_CACHING_1H=1) and 5 minutes by default on an API key or cloud provider; a long idle session then costs a whole conversation for a one-line question
Bound unattended runs In claude -p: --max-turns N, --max-budget-usd X, --allowedTools to scope tools, --bare to skip loading hooks/MCP/CLAUDE.md, --output-format json (includes total_cost_usd)
Batch deliberately Fan out with a shell loop over claude -p calls (test on 2–3 items, refine the prompt, then run at scale); use plan mode on complex tasks to avoid expensive rework
Keep agent teams small Each teammate is a full instance with its own context window — usage scales with headcount and roughly 7x with teammates running in plan mode; default teammates to Sonnet, keep spawn prompts focused (everything in one adds to that teammate's starting context), and shut a teammate down once its work is done

The model lineup, effort levels, compaction internals, and prompt-cache mechanics behind several of these levers get a full treatment — with worked token math at three cost points for the same feature — in the Models, Cost, and Context subject; this table is the habit, not the mechanism.

Worked example: the same task at two context sizes

Suppose a bug fix takes 40 tool-call turns. In session A you have /cleared and delegated exploration to a subagent, so the main context sits around 20K tokens per turn. In session B you have been working for hours without clearing and the context is at 100K.

\text{input tokens processed} \approx \sum_{t=1}^{40} C_t \;\Rightarrow\; A: 40 \times 20\text{K} = 0.8\text{M},\quad B: 40 \times 100\text{K} = 4.0\text{M}

Session B processes five times the input tokens for identical work — mostly at cache-read rates, so the bill isn't literally 5×, but it is materially higher — and, per the degradation constraint, produces worse output while doing it. Add one cache miss after a lunch break and B pays for a full uncached 100K read on top. Nothing about the task changed; only the hygiene did.


Safety Habits

  • Review diffs before they land. Fluency is not correctness. Run git diff (or /code-review) before merging anything, especially in acceptEdits or auto mode where you weren't prompted per edit.
  • Never paste secrets into a prompt. They become part of a persisted transcript. Read them from env or files that permissions.deny blocks (Read(./.env), Read(./secrets/**)); the sandbox's credentials settings can deny or mask env vars and files from sandboxed commands.
  • Containers for risky work. Anything you'd run with --dangerously-skip-permissions belongs in a dev container or VM without internet, per the docs — that's where the isolation actually holds.
  • Destructive commands stay gated. Force pushes, reset --hard, terraform destroy, production deploys: keep them out of allow, and consider a hook. Auto mode's classifier blocks these categories, but you shouldn't rely on it as your only line.
  • Non-interactive runs load project config. claude -p in a folder you haven't trusted still runs that project's .claude/settings.json hooks and connects its .mcp.json servers, because there's no trust dialog. In CI or on unfamiliar repos, add --bare and pass exactly the settings you mean.
  • Scope MCP credentials to least privilege — an MCP call lands outside your filesystem and outside git's undo (covered in the MCP subject).

Unattended-Run Safety Habits

Everything so far assumes someone is watching. The moment a run is scheduled, fanned out over claude -p, handed to auto mode for an hour, or driven from another device, that assumption breaks — and per the docs, "the longer Claude works unattended, the more an independent check matters before you count the work as done." A short habit checklist, roughly in the order you'd apply it:

  1. Allowlist and deny before you step back, not after. The narrow permissions.allow/deny block from the Permissions Strategy section is what an unattended run falls back on when nothing is there to click "approve" — get it right first.
  2. Bound the blast radius of the run itself. In claude -p, --allowedTools restricts what the run can touch at all, and --max-turns / --max-budget-usd cap how far a run that's gone wrong can go before something stops it. Test on a handful of items before turning a loop loose on the full set.
  3. Prefer auto mode or a sandbox to bypassing permissions. Auto mode's classifier keeps blocking scope escalation and unknown infrastructure even with nobody watching; the sandbox keeps a stray command from touching anything outside the working directory and an allowlisted domain set. Neither is a reason to skip the allowlist — they're additional layers, not replacements.
  4. Make the stop conditional on evidence, not time. A one-off "run the tests and iterate" prompt only closes the loop for as long as you're there to have asked it. For a run nobody is watching, escalate to a /goal condition or a Stop hook so the turn can't end silently on a false "looks done."
  5. Put a reviewer between the run and anything real — a fresh-context subagent checking the diff against a plan or PLAN.md, same as in Collaboration Patterns below, so the last thing that happens before a PR or a deploy isn't the same agent grading its own work.
  6. Know what "unattended" actually means for that run. A desktop scheduled task, a cloud routine that runs with your machine off, and a session on a device you're controlling remotely have different trust boundaries and different failure-visibility — including the rule that a message or approval relayed from another session or device is not the same as a human approving it in front of you. Session scheduling, routines, and remote control belong to the Scheduled Tasks and Remote Agents subject; the full threat model (prompt injection via untrusted content, sandbox scope, MCP trust, incident response) belongs to the Sandboxing and Security subject. This subject gives you the starting checklist; those two own the depth.

Choosing Your Orchestration Primitive

Every practice above assumes one agent in one context. Once a task is bigger than that, Claude Code gives you four ways to add more agents, and picking the wrong one is either wasted setup or a context you didn't need to protect in the first place.

Primitive What it is Reach for it when
Subagent A delegate that runs in its own context window with its own allowed tools and reports a summary back to you — the mechanism behind "use subagents to investigate X" A single step would flood your main context and you won't need to keep the detail: broad exploration, a verbose test run, a log file, a one-off review pass
Skill A SKILL.md Claude applies automatically when relevant, or you invoke directly with /skill-name; it can also encode a repeatable multi-step procedure (optionally side-effecting, via disable-model-invocation: true) The same domain knowledge or the same procedure comes up more than once and should load on demand instead of sitting in CLAUDE.md every session
Agent team (experimental — requires CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1) Several full Claude Code instances — a lead plus teammates — coordinating on shared tasks and messaging, each with its own context window The work genuinely benefits from multiple agents working the same problem concurrently, and the cost (roughly proportional to headcount, and up to ~7x a normal session when teammates run in plan mode) is worth paying
Dynamic workflow A larger-scale, saveable, rerunnable orchestration mechanism for big multi-agent jobs, distinct from a single ad hoc subagent call The job needs to be rerun with different inputs, needs to run at a scale beyond a handful of parallel subagents, or needs to keep an adversarial-review loop going across many tasks unattended

As a default: reach for a subagent first — it's the lowest-setup way to keep verbose or exploratory work out of your main context, and it's what the rest of this subject assumes. Reach for a skill when you notice you're repeating the same instructions across sessions. Agent teams and dynamic workflows are heavier-weight primitives for genuinely multi-agent jobs; their mechanics, the full orchestrator/results/repeatability/scale comparison, and worked examples belong to the Workflows and Agent Teams subject — this section is only the fork in the road.


Collaboration Patterns

Parallel sessions with worktrees. claude --worktree feature-auth (or -w) creates an isolated checkout under .claude/worktrees/feature-auth/ on branch worktree-feature-auth; run it again with another name in a second terminal. Edits never collide, and Claude Code actively blocks tool calls that would touch the main checkout. Add .claude/worktrees/ to .gitignore, and use a .worktreeinclude file to copy gitignored .env files into each new worktree. Subagents can be isolated the same way (isolation: worktree in their frontmatter, or "use worktrees for your agents").

Writer / Reviewer. A fresh context improves review because Claude isn't biased toward code it just wrote:

Session A (Writer) Session B (Reviewer)
"Implement a rate limiter for our API endpoints"
"Review the rate limiter in @src/middleware/rateLimiter.ts. Look for edge cases, race conditions, and consistency with existing middleware patterns."
"Here's the review feedback: [B's output]. Address these issues."

The lighter-weight version is a verification subagent: "Use a subagent to review the rate limiter diff against PLAN.md. Check that every requirement is implemented, the listed edge cases have tests, and nothing outside scope changed. Report gaps, not style preferences." The bundled /code-review skill does a correctness pass in a fresh subagent. One caveat from the docs: a reviewer asked to find gaps will find some even when the work is sound — tell it to flag only what affects correctness or the stated requirements, or you'll over-engineer.

Claude reviewing PRs in automation. gh pr diff 123 | claude -p --append-system-prompt "You are a security engineer. Review for vulnerabilities." --output-format json — pipe the diff in so Claude doesn't even need Bash permission to fetch it.


Debugging When Claude Goes Wrong

Symptom What's usually happening What to do
Loops on the same fix, tests still red Polluted context full of failed approaches; or the check is wrong After two failed corrections, /clear and re-prompt with what you learned; verify the test itself; add a negative constraint ("don't modify the tests")
Hallucinated API / flag Answering from memory instead of the source Point it at the source: give the docs URL, @ the type definitions, or say "run foo --help and use what it reports"; "check the installed version in package.json first"
"Fixed" but not actually fixed No verification target — "looks done" was the only signal Give it a failing test or a command whose exit code is the check; ask for the output as evidence
Ignores a CLAUDE.md rule File too long, rule buried, or file not loaded /context to confirm it loaded; prune; promote must-hold rules to a hook
Reads hundreds of files Unscoped "investigate" Scope it, or delegate to a subagent
Suppresses the error instead of fixing it Prompt asked for a green build, not a root cause "address the root cause, don't suppress the error"

Two habits cover most of it: give error output verbatim (paste the traceback; cat build-error.txt | claude -p "explain the root cause") rather than paraphrasing, and ask for a plan before a retry when the first attempt went sideways — a wrong plan is a paragraph to reject; a wrong retry is another diff to unwind. /doctor diagnoses setup issues; /context and /usage tell you whether the window itself is the problem.


Worked Example: One Feature, Done Badly vs Done Well

Task: add rate limiting (100 requests/minute per API key) to the public API.

Done badly. In a session that's been open since the morning (context ~110K, three unrelated tasks in history), you type: "add rate limiting to the API." Claude greps broadly, reads 30 middleware and route files, picks a design, writes an in-memory limiter, edits eight files, and reports done. There's no test. You run the app; limits reset on every deploy because the counter is in-process, and the limiter also throttled the internal health check. You say "that's wrong, it should be per key." It patches. You say "still throttling healthchecks." It patches again. Now the context is at ~160K, three failed approaches deep, and the third patch reintroduces the first bug. Cost so far: 60+ turns at 110–160K per turn — on the order of 8M input tokens — and you still have to review an eight-file diff you don't trust.

Done well.

  1. /clear. Enter plan mode. "Read src/middleware/ and src/api/routes/. I want per-API-key rate limiting, 100 req/min, sliding window, backed by our existing Redis client in src/lib/redis.ts. Exempt /health. Follow the pattern in @src/middleware/auth.ts. What files change? Create a plan." Claude reads ~6 files (context ~25K), proposes: one new middleware file, registration in one place, a config constant, three tests. You approve.
  2. "Write the tests first: 100 requests pass, the 101st in the same minute returns 429 with a Retry-After header, a different key is unaffected, /health is never limited. Don't implement yet; run them and confirm they fail." → four red tests.
  3. "Now implement until those tests pass. Don't modify the tests. No new dependencies." Claude edits, runs, fixes an off-by-one, runs again — green in three iterations. Context ~45K.
  4. "Use a subagent to review the diff against the plan: every requirement covered, edge cases tested, nothing out of scope. Report gaps only." One gap: no test for the window sliding. Claude adds it. Green.
  5. "Show me the test output, then commit and open a PR." You read a three-file diff plus tests, with evidence.

Roughly 25 turns at 25–45K per turn — under 1M input tokens, a fraction of the bad run — and the result is verified, reviewed, and scoped. Every difference traces back to the three facts in the mental model: context stayed lean, Claude had a check to run, and a wrong assumption was caught in a plan, not in a diff.


Session Checklist

Before you start:

  • [ ] /clear if the last task was unrelated; /context if unsure what's loaded
  • [ ] Is the approach ambiguous or multi-file? → plan mode. One-sentence diff? → just ask
  • [ ] What check will Claude run? (tests, build, lint, screenshot, script) — put it in the prompt
  • [ ] Prompt names files (@), acceptance criteria, an example pattern, and negative constraints
  • [ ] Right model/effort for the job; permissions allowlist covers the routine commands

While it runs:

  • [ ] Watch the first few tool calls; Esc the moment it heads the wrong way
  • [ ] Two corrections on the same issue → /clear and re-prompt
  • [ ] Verbose or wide steps → "use a subagent"

Before you call it done:

  • [ ] Evidence of the check (test output, screenshot), not "done"
  • [ ] git diff / /code-review / a reviewer subagent against the plan
  • [ ] Commit; /rename the session if you'll come back to it
  • [ ] Anything you had to explain twice → CLAUDE.md (or a hook if it must always hold)

Anti-Patterns and Interview Traps

Anti-pattern Why it hurts Fix
Kitchen-sink session Unrelated context dilutes attention and is re-sent every turn /clear between tasks
Correcting over and over Failed approaches pollute the window; each retry is worse After two, /clear + better prompt
Over-specified CLAUDE.md Important rules lost in noise; Claude ignores half Prune to <200 lines; move procedures to skills; must-hold rules to hooks
Trust-then-verify gap Plausible code that misses edge cases Always provide a verification target; if you can't verify it, don't ship it
Infinite exploration "Investigate X" reads hundreds of files Scope it or use subagents
Bash(*) / blanket bypass on your laptop Removes the one human checkpoint on irreversible actions Narrow allowlists, hooks, sandbox; bypass only in containers
Opus for everything, thinking maxed Multiplies cost with no quality gain on routine work Sonnet default; Opus for hard reasoning; /effort
Pasting secrets into prompts Persisted in transcripts Deny-rules on secret files; env vars; sandbox credential masking
Dictating line-by-line edits Uses an agent as a typist; no exploration, no verification Describe the outcome, the constraints, and the check
Treating checkpoints as git Only Claude's edits are snapshotted, not Bash side effects Branch + commit; rewind is for conversation-level experiments
One CLAUDE.md for a whole monorepo Every session pays for every package's rules whether relevant or not Root file for repo-wide rules + one per package; claudeMdExcludes for packages you never touch
Unattended run with only a one-off "run the tests" prompt Nobody is there when it silently stops on a false "looks done" /goal or a Stop hook as a real gate; a reviewer subagent before anything ships

Interview traps. "How do you keep an AI agent from making things up?" — the answer is a verification loop and pointing it at sources, not "better prompting." "How do you control cost?" — context hygiene first, model choice second, thinking budget third; the answer that skips context is incomplete. "Would you run it with permissions bypassed?" — only in an isolated container, and say why (blast radius, no undo for external side effects). "How do you get better output over time?" — CLAUDE.md that grows only from repeated mistakes, plus hooks for what must always hold.


Key Takeaways

  • One constraint explains most of the practices: the context window fills fast and quality degrades as it does. Keep it lean — /clear between tasks, /compact with focus, /btw for side questions, subagents for anything verbose, scoped prompts.
  • Give Claude a check it can run (tests, build, lint, screenshot). Escalate from "run it in this prompt" → /goal → Stop hook → verification subagent as the run gets more unattended. Ask for evidence, not assertions.
  • Explore → plan → implement → verify. Plan mode when the approach is uncertain or multi-file; skip it when you can describe the diff in a sentence. For big features, have Claude interview you into a SPEC.md, then execute in a fresh session.
  • Prompt like a spec: files (@), acceptance criteria, an example pattern, negative constraints, error output verbatim.
  • Correct early: Esc to stop, Esc Esc//rewind to restore, two corrections → /clear. Git remains the real undo.
  • CLAUDE.md: short, curated, iterated; hooks for what must always hold; skills for on-demand procedures. In a large codebase, apply the same discipline per directory: layered CLAUDE.md, claudeMdExcludes, deny rules on generated/vendored paths, sparse worktrees, and per-directory skills.
  • Permissions: on Pro/Max/Team, auto mode is the interactive default; Manual mode elsewhere. Either way, narrow allowlists (Bash(npm run test *)), deny secrets, hooks as guardrails, sandbox for Bash — and bypass only inside a container.
  • Cost: context size × turns; then model choice, effort/thinking budget, cache awareness; bound headless runs with --max-turns, --max-budget-usd, --allowedTools, --bare; keep agent teams small and on Sonnet. Deeper cost mechanics live in the Models, Cost, and Context subject.
  • Orchestration: subagent for one-off delegation, skill for a repeated procedure, agent team/dynamic workflow for genuinely multi-agent jobs — full comparison in the Workflows and Agent Teams subject.
  • Collaborate: worktrees for parallel sessions, writer/reviewer or a reviewer subagent, /code-review, and pipe diffs into claude -p for automated review.
  • Unattended runs: allowlist first, bound the blast radius, gate the stop on evidence (/goal/Stop hook), add a reviewer before anything ships — deeper threat models and scheduling mechanics live in Scheduled Tasks and Remote Agents and Sandboxing and Security.
  • When it goes wrong: paste the error verbatim, point it at the source, ask for a plan before a retry, and check /context before blaming the model.

Ready to test your knowledge?

Practice questions

We use cookies for product analytics to improve OmniAtlas. See our Privacy Policy.