Infra 7 min read

11 Claude Code Features Worth Using (and Gotchas to Know)

Claude Code has more settings than most of us ever touch. Here are the ones that change a working day, with the catch sitting next to each one.

Abstract illustration of a terminal window in teal lines on navy, with an amber rewind arrow and three amber blocks.

Mettons you have a 42,000-character CLAUDE.md in a subfolder of your repo. The memory page says Claude Code gets the first 40,000 characters of it, plus a note to go read the rest with the Read tool. On my 715-line test file, the first 40,000 characters contain 679 complete lines, leaving the rest of the file partially or entirely unloaded.

That is the first surprise. The second is a hook that prints “Blocked” and blocks nothing, because it exited with 1 instead of 2.

Every block below is real shell output from a test machine (git, jq, wc, Python, claude mcp) or a doc quote, never a model session.

Abstract illustration of a terminal window in teal lines on navy, with an amber rewind arrow and three amber blocks.

Claude Code setup: size it, then split it

Check your nested CLAUDE.md files with wc -m. The target is under 200 lines per file. The hard edge is different. It applies only to files that load on demand: a CLAUDE.md or CLAUDE.local.md in a subdirectory, subdirectory rules, and path-scoped rules. The limit is 40,000 characters on a 200K window, and 200,000 on a 1M window for Opus 4.6 or Sonnet 4.6 (150,000 on later models). Your root CLAUDE.md is not on that list.

I generated a file of 715 made-up one-line rules and ran the docs’ own wc -m. head -c counts bytes, and the limit is in characters, so I used Python for that part:

$ wc -m big.md
42077 big.md
$ python3 -c "import sys;print(open(sys.argv[1]).read()[:40000].count('\n'))" big.md
679

Plain ASCII, so bytes and characters agree here. Write the same rules in French with accents and head -c 40000 | wc -l says 489 while the character count says 495. My own files have accents, so I use the Python line.

The same page says an over-length file shows a warning “at startup and when you run /status“, so run /status first and wc -m second. Then /doctor proposes trims, and /doctor prompt-audit (v2.1.283 or later) lists outdated or conflicting instructions.

Keep team rules and your own rules apart. Team rules go in ./CLAUDE.md. Yours go in ~/.claude/CLAUDE.md or a gitignored ./CLAUDE.local.md. The catch, tested with plain git:

$ ls -A repo
.gitignore CLAUDE.local.md CLAUDE.md
$ git worktree add ../repo-feature -b feature
$ ls -A ../repo-feature
.gitignore CLAUDE.md

The gitignored file does not follow you into a new worktree. The docs’ fix is an import from your home directory: @~/.claude/my-project-instructions.md. Me, I would use that instead of CLAUDE.local.md. Imports tidy a big file but do not make it cheaper: they load with the file that imports them.

See what Claude sees

Put the context percentage in the status line. Type /statusline and Claude writes it. The status line docs say it gets JSON on stdin, so I ran their one-liner with a fake payload:

$ echo '{"model":{"display_name":"Opus"},"context_window":{"used_percentage":41}}' | jq -r '"[\(.model.display_name)] \(.context_window.used_percentage // 0)% context"'
[Opus] 41% context

Seeing that number all day beats typing /context after things go strange. When you do run /context, read the Memory files list first. Per the memory page, a CLAUDE.md missing from that list is one Claude cannot see.

Treat context as a budget. /compact [instructions] frees space. /btw asks a side question that is answered from the conversation alone, with no tools, and stays out of the history. After a compact, the memory page says the project-root CLAUDE.md is re-read from disk, but an instruction you gave only in conversation is gone. Anything you said twice goes in the file.

Habits that save the session

Plan before you edit. Shift+Tab cycles default, acceptEdits and plan. /plan fix the auth bug jumps straight in. In plan mode Claude researches and proposes without editing your source, and Ctrl+G opens the prompt in your editor, which is where I would fix a plan. Use it on anything that touches more than one file.

Rewind, do not argue. /rewind (aliases /checkpoint and /undo, or Esc Esc on an empty prompt) opens a menu: restore code and conversation, only the conversation, only the code, or summarize from a chosen message. The checkpointing page says it keeps the 100 most recent checkpoints in a session. Catch, quoted: “A file that only Bash commands changed isn’t tracked and stays as it is.” Commit before a big run anyway. Correcting a model in the middle of a mess costs more turns than going back to before the mess.

Pick the effort. /model has left and right arrows for effort on models that support it, and Alt+P (Option+P on macOS) switches model without clearing your prompt. Per the commands page, /model saves your choice for new sessions, so press s on a row to change only the current one. Low effort for renames, high for design.

Claude Code automation: hooks and skills

A hook is the part you cannot ask nicely. A line in CLAUDE.md is context, not enforcement. A hook is code. A rule in a file is a suggestion; a hook that exits 2 is a wall, and there are commands in my week that need a wall. I saved the docs’ script that blocks rm and fed it two payloads:

$ echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf build"}}' | ./no-rm.sh
Blocked: rm commands are not allowed
exit=2
$ echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./no-rm.sh
exit=0

Now the catch. I changed exit 2 to exit 1 and got the same message, with exit=1. The docs say exit 1 is a non-blocking error: the tool call goes ahead and you only get a hook error notice in the transcript. It looks like a warning, not a block.

Register the script as a PreToolUse hook on the Bash matcher and set onFailure: "block" first (v2.1.295 or later). Without that setting a missing script only logs a hook error and ls runs; with it, the call is blocked. Then run the docs’ test: leave the script missing and ask Claude to run ls. If nothing complains, it is not wired. I did not register it in a live session; /hooks shows what is configured.

A skill costs almost nothing until you call it. One SKILL.md, invoked as /name. The skills docs say the listing text is cut at 1,536 characters, full content loads only when invoked, and after compaction Claude keeps the first 5,000 tokens of each invoked skill. Old .claude/commands/ files still work and create the same /deploy. I wrote about installing someone else’s skills. A skill for your own deploy check is the cheap next step.

Run Claude Code in parallel and unattended

One worktree per agent. Two agents on one checkout is the fastest way I know to have them step on each other’s changes. claude --worktree feature-auth creates .claude/worktrees/feature-auth/ on branch worktree-feature-auth. Add .claude/worktrees/ to .gitignore. A worktree is a fresh checkout, so .env is missing unless a .worktreeinclude file lists it. A file must match a pattern and be gitignored, so a CLAUDE.local.md line should qualify. I did not test that.

In scripts, use --bare. claude --bare -p "Summarize README.md" --allowedTools "Read" is the docs’ own example, and it needs ANTHROPIC_API_KEY. The headless page says that without --bare, -p runs the project’s hooks and connects the servers in .mcp.json with no approval prompt. My check, from the interactive side:

$ claude mcp add --scope project staging --transport http https://example.com/mcp
$ claude mcp list
staging: https://example.com/mcp (HTTP) - ⏸ Pending approval (run `claude` to approve)

Interactive sessions ask first. -p does not. The docs recommend --bare for scripted and SDK calls. The catch: it also skips hooks, skills, MCP servers, auto memory and CLAUDE.md, so nothing you built above runs in CI unless you spell it out again: --allowedTools grants tool permissions and nothing else, it does not bring CLAUDE.md back, so pass instructions with --append-system-prompt-file and settings with --settings. And --output-format json returns total_cost_usd, so a script can track what each call costs. I did not run that one.

What I would do first

  1. wc -m on every nested CLAUDE.md, then /status.
  2. /plan before your next multi-file change.
  3. Context percentage in the status line.
  4. One hook that blocks something you never want, ending in exit 2.
  5. claude --worktree for your next two parallel tasks.

What I have not measured is what the 40,000-character cut costs on a real repo. Run number 1 on yours and tell me.

Claude Code is getting powerful enough that knowing how it works matters almost as much as knowing what to ask it. The difference between a helpful assistant and a reliable development tool is often a setting, a hook, or an instruction Claude never actually read.

Dominic Plouffe

CTO of Trellis, with exits at FuseTalk, 360pi and DeadHeat. I write about making agents and big data work together safely, and I'm building yaaf, an open-source platform for running teams of AI agents that actually finish the job: yaaf.dplouffe.ca