Claude Code is the terminal-first agentic CLI from Anthropic — claude in your shell, reading the codebase, editing files, running commands, shelling out to git. It’s genuinely useful for real work. But using it without burning your context budget on the first session? That took some work.
The failure mode is consistent: people anchor it in chat-mode habits — long conversational prompts, no scope, no project context, no plan review — and end up with sprawling diffs, exhausted context, and a session they can’t recover. The fixes are structural, not stylistic.
CLAUDE.md is doing more work than you think
Claude Code reads CLAUDE.md at the start of every session. This is the single highest-leverage configuration file in the system. Most people either don’t write one or treat it as a sticky note.
Two locations matter:
./CLAUDE.mdin project root — project conventions, toolchain, paths, gotchas specific to this codebase.~/.claude/CLAUDE.md— personal global preferences that follow you everywhere.
What earns its place in there:
- The toolchain (language version, package manager, test runner, lint command).
- Conventions you actually enforce (naming, error handling, logging, formatting style).
- The paths that matter (where source lives, where generated code lives, what to ignore).
- Things that have bitten you before (the migration directory that needs
--no-tx, the build step that requires a specific node version).
What doesn’t:
- Aspirational rules you don’t follow yourself.
- Long prose about what the project “is.” Keep it operational.
# Project: oled-deej
- ESP32-S3 N16R8 DevKit, ESP-IDF v5.1, PlatformIO
- All firmware builds with `pio run -e esp32-s3-devkitc-1` # full command, not a shortcut alias
- Display modules use TCA9548A multiplexer on I2C0 (SDA=8, SCL=9)
- Don't suggest GPIOs 33–37 or 26–32 — reserved for octal PSRAM
- Header style: snake_case, no `using namespace`
Reads weirdly mechanical, but that’s the point. It’s machine-facing.
Scope prompts like a ticket, not a chat message
The single biggest difference between a productive session and a context-burn session is prompt shape. Chat-shaped prompts (“can you help me with the auth stuff?”) get chat-shaped output: meandering, unscoped, stateful in the wrong way.
Ticket-shaped prompts produce ticket-shaped work:
- File or path the change lives in.
- Current behaviour (what it does now, including the failure if there is one).
- Expected behaviour (what success looks like).
- Out-of-scope, explicitly, when there’s a temptation to drift.
"In src/refresh.ts, the token refresh handler calls /oauth/token but doesn't
catch the 401 returned when the refresh token itself expires. We log the
error and crash. Make it surface as a "session expired" event the UI layer
can subscribe to. Don't change the network layer in src/http.ts."
That’ll get you a contained diff. The chat-mode equivalent (“the session refresh isn’t working right, can you fix it?”) gets you a rewrite of three files, half of which you didn’t want touched.
Plan before you let it edit
Plan mode — letting Claude Code talk through the approach before touching files — is the cheapest insurance available. One round-trip’s cost; the saving is every revert you’d otherwise do.
The pattern that works:
- Describe the change (ticket-shaped, see above).
- Add: “Plan this. Don’t edit anything yet.”
- Read the plan. Push back where it’s wrong. Add constraints it didn’t infer.
- Approve the corrected plan, then let it execute.
The plans aren’t always right. They’re usually salvageably wrong — wrong assumption about a dependency, wrong inference about where state lives, wrong file targeted. Catching those before the edits go in is the entire point.
MCP is where it stops being just a coding tool
The Model Context Protocol (MCP) lets Claude Code reach out of the codebase. The servers worth wiring up depend on workflow, but the common ones are:
- Filesystem MCP — for projects spanning multiple repos or out-of-tree config.
- GitHub or GitLab MCP — issue context, PR review, CI status pulled into the session.
- Notion, Google Drive, or Jira — design docs and tickets read in-place rather than copy-pasted.
- Custom MCP servers — for whatever proprietary tooling your environment runs on (RMM systems, internal ticketing, monitoring).
The shift, once MCP is in play, is that the loop stops being “code → terminal → done.” It becomes “ticket → spec doc → code → tests → PR → CI” — Claude Code holds the whole arc. Whether that’s a feature or a footgun depends on whether you want it touching that much surface area.
Permissions: auto-accept is a footgun
Default mode confirms each tool call before running it — every file edit, every shell command, every git operation. It’s noisy. The temptation to flip on --dangerously-skip-permissions (the flag is named exactly what it should be named) is real.
Important: auto-accept is fine for a sandboxed scratch repo and dangerous for anything with side effects — production credentials in env files, deployment scripts in package.json, migrations that drop tables, anything that costs money on call. A single bad inference and you’re explaining to someone why prod is down.
The middle ground: confirm shell commands, allow reads and edits. The CLI has flags for tool-scoped permissions — exact names move between versions, so claude --help is the source of truth, not memory.
Where it stops being useful
Claude Code isn’t equally good at everything, and pretending otherwise produces bad sessions.
It’s strong on:
- Refactors with clear scope and good test coverage.
- Boilerplate generation from a written spec.
- Wiring a known library into a known shape (form handling, API client scaffolding, config loaders).
- Reading unfamiliar codebases and answering “where does X live.”
It’s weak on:
- Subtle correctness — race conditions, off-by-one, locale edge cases. Code that looks right and isn’t.
- Anything requiring infrastructure state it can’t see (a flaky test that depends on a misconfigured CI runner, a network issue that manifests as a build failure).
- Architectural decisions where the right answer is “don’t do this.” It’ll happily implement the wrong design well.
- Long-running coordination across state the codebase doesn’t surface — caching layers, message queues, sessions whose lifecycle lives outside the files in front of it.
Code review is not optional, even on its own output. The diffs are large enough and confident enough that skim-reviewing is how production breaks.
Lessons Learned
- Write a CLAUDE.md before you write the first prompt. The session-quality difference is structural, not marginal.
- Scope prompts like tickets. File, current behaviour, expected behaviour, out-of-scope.
- Use plan mode. Read the plan. Push back where it’s wrong before approving.
- Don’t enable auto-accept in any repo with side effects you can’t undo.
- Wire up MCP servers for the systems you actually pull context from — Jira, Notion, GitHub, internal tooling.
- Review every diff before merge. The output is good enough to fool a skim.
- Don’t ask it to make architectural decisions. It’ll execute the wrong design fluently.
What’s next
The patterns above are stable across model versions; the surface — slash commands, MCP server list, plugin ecosystem, exact CLI flags — moves quickly. Worth checking the docs map at https://docs.claude.com/en/docs/claude-code/overview every couple of months rather than relying on memory.
