“Anyone can type a prompt into Claude Code. The setup is what decides whether the answer is trustworthy.”

I’ve been asked a few times now what my actual Claude Code setup looks like — not the marketing version, the real one, layer by layer. So here it is. Not a features list. The reasoning behind each layer, and why it matters if you’re doing real engineering work with an AI coding agent instead of toy demos.
Layer 1: The CLAUDE.md Hierarchy — Global Rules vs. Project-Specific Instructions 📄
“Global preferences, project-specific gotchas — mixing the two up is how instructions stop working.”
Claude Code reads CLAUDE.md files at multiple scopes, and getting the split right matters more than people assume. I keep a personal one at ~/.claude/CLAUDE.md — that's the one that applies across every project I work in, no matter which repo I'm sitting in. And then every project gets its own CLAUDE.md checked into version control, so the whole team reads the same instructions through source control, not just me.
The actual split I use: anything that’s about how I work, regardless of codebase, goes global — tool-usage rules like preferring a code-intelligence lookup over a raw grep pass, general communication preferences, cross-project conventions I want followed everywhere. Anything that’s about this specific codebase — build commands, architecture decisions, naming conventions, a bug class that’s bitten this project specifically — goes in the per-repo file, because it’s meaningless noise in any other project and just eats context there for nothing.
The gotcha that actually taught me this: a recurring bug pattern showed up twice in the same area of a project before I wrote it down properly — the kind of subtle issue where a fix looks correct, passes a casual glance, and still reintroduces the exact same problem because the underlying constraint was never written anywhere Claude could see it. First time, I explained the constraint in conversation and moved on. Second time, same mistake, same explanation, same conversation. That’s the actual trigger for writing something into CLAUDE.md — not “this might be useful someday,” but “this cost me the same conversation twice.” Once it’s written down as a concrete, verifiable rule instead of a vague warning, it stops recurring.
Worth knowing if you’re setting this up yourself: CLAUDE.md content is delivered as a user message, not baked into the system prompt, so specificity matters a lot more than length. A file under 200 lines with concrete, checkable rules beats a long file with vague ones every time.
Layer 2: Claude Code’s Auto Memory — Persistent Context Beyond CLAUDE.md 🧠
“CLAUDE.md is what I tell it. Memory is what it figures out on its own.”
This is worth being precise about, because it’s easy to conflate with CLAUDE.md and they’re doing genuinely different jobs. Claude Code has a second system — auto memory — that accumulates per-project, stored outside the repo itself, and it’s Claude writing to it, not me. It tracks four kinds of notes: my role and working preferences, corrections and feedback I’ve given it, ongoing project state that isn’t derivable from the code itself, and external references — where to look for information that lives outside the codebase. All of it indexed through a lightweight MEMORY.md file.
Why this matters separately from CLAUDE.md: CLAUDE.md is instructions I deliberately wrote down. Memory is context Claude accumulated from actually working with me, session after session, that I never explicitly sat down to document. The first is a rulebook. The second is closer to institutional memory — the stuff a long-tenured teammate just knows about how you work together, without it ever being written in an onboarding doc.
A few concrete examples of what actually changes because of this:
- Correcting a preference once — “don’t touch this file without asking first” — and having that correction actually persist into the next session instead of needing to be restated every single time I open a new conversation.
- Project state that isn’t visible from reading the code itself — something’s mid-migration, a decision was deliberately deferred, a piece of the architecture looks wrong on purpose because of a constraint that doesn’t show up in a diff.
- A pointer to where something actually lives — which dashboard, which external doc, which system holds the answer to a question the codebase alone can’t answer.
None of that belongs in CLAUDE.md, because none of it is a rule I’m choosing to enforce. It’s context that would otherwise get lost the moment a session ends.
Layer 3: Guardrail Hooks — Deterministic Control Over an AI Coding Agent 🚦
“A speed bump you chose on purpose is different from friction you’re just stuck with.”
Hooks are deterministic — they run as actual shell commands at fixed points in the lifecycle, which is a meaningfully different guarantee than “the model usually remembers to do this.” A PreToolUse hook fires before any tool call executes and can block it outright. A Stop hook fires when Claude finishes responding and can force it to keep working if a condition isn't actually met yet.
The pattern I lean on most: a PreToolUse hook scoped to file edits that forces an explicit statement of facts before the edit goes through — what imports this file, what could break, what was actually asked for. It's a deliberate speed bump against careless changes, and the honest answer to "is the friction worth it" is yes, specifically because the moments it slows down are exactly the moments where slowing down was the right call. A hook doesn't get tired or rush through the fortieth edit of a long session the way attention naturally does.
A second one, tuned for output discipline: a hook that keeps responses terse by design, cutting the reflexive fluff and hedging that tends to creep into longer sessions. Less about safety, more about not wasting my own reading time on filler.
A third, tuned for context health: something that watches context size and nudges toward compacting at a natural boundary rather than an arbitrary one — a PreCompact or SessionStart hook keyed to compaction events, prompting a summary at a point where the conversation has actually reached a real stopping point instead of getting cut mid-thought.
Is the friction worth it? For the file-edit guardrail specifically, yes without hesitation — that one’s caught real mistakes before they became real problems. For the terseness and context hooks, it’s more of a quality-of-life tuning than a safety net, but a genuinely useful one once it’s dialed in.
Layer 4: Code Intelligence Over Grep-and-Read Loops 🔍
“Grep tells you where a string appears. It doesn’t tell you what calls it, what it calls, or what breaks if you change it.”
Traditional exploration is a loop: grep for a term, read the file, grep for what that file imports, read that file too, repeat until the picture assembles itself. It works, but it’s slow, and it burns a lot of tool calls rebuilding a mental model that’s genuinely just structural information about the codebase — not something that should need to be rediscovered by brute-force search every single session.
A pre-indexed knowledge graph of the codebase changes that cost/quality tradeoff meaningfully. Instead of iterative search-and-read, one query returns the relevant source alongside actual call paths — who calls this, what this calls, how deep the dependency chain runs — in a single pass instead of a chain of separate lookups. Fewer round trips, and critically, a more complete picture up front, since call-path information that grep would never surface on its own is just there in the result.
Claude Code’s own docs make a related point worth internalizing here: code intelligence plugins exist specifically because typed-language codebases benefit enormously from structural lookups over pattern matching, particularly at scale, where a grep-and-read loop’s cost compounds fast.
Layer 5: Live Device Verification for Flutter and Mobile Development 📱
“Did this actually fix on-device, or did it just compile? Those are very different claims.”
For Flutter specifically, this is the layer that matters most day to day. Instead of rebuilding from scratch to check a UI tweak, connecting directly to a running app on a physical device — screenshotting it, tapping through it, inspecting the live UI tree — closes a gap that pure code generation can’t close on its own. Claude Code actually ships real support for this now: device access for iOS simulators, and computer-use capabilities that can validate a native build, reproduce a layout bug, or test a simulator flow directly against what’s actually rendering.
Pairing that with a live connection to the Dart VM for hot reload and hot restart means the iteration loop stops being “guess, rebuild, wait, check” and becomes “guess, reload, check” — genuinely faster, and more importantly, it prevents a specific, common mistake: mistaking a clean compile for a working fix. Code that compiles cleanly and code that actually behaves correctly on a real device are not the same claim, and a rebuild-only workflow can’t tell the difference between them. Watching it run on the actual device closes that gap directly instead of hoping the two happen to line up.
Layer 6: Deferred Tool Loading and MCP Context Management ⚙️
“Every tool sitting in context whether you need it or not is a tax on every single turn, not just the turns that use it.”
Not everything gets loaded into context by default, and that’s a deliberate design choice worth understanding, not just accepting. Specialized tools — browser automation, less-common MCP servers, anything you reach for occasionally rather than constantly — load on demand instead of sitting in context the entire session whether they’re relevant or not.
This matters more than it sounds like over a long session. Every tool definition loaded into context costs tokens on every single turn, whether or not that turn actually uses it. Claude Code’s own MCP tool search exists specifically to scale past this — servers can register for on-demand discovery instead of front-loading every tool definition at session start, which keeps a long, multi-hour session efficient instead of slowly accumulating dead weight in context that never gets used but never stops costing something anyway.
Layer 7: Claude Code Skills and Subagents I Actually Use 🧩
“Access to a large library means nothing if you’re only ever touching three things in it.”
Claude Code ships a genuinely large ecosystem of packaged workflows — code review, security review, framework-specific reviewers, planning and execution agents, all invokable explicitly instead of reimplementing the same ad hoc logic every single session. Being honest about actual usage matters more than listing the full catalog:
Reached for regularly: a general code-review pass before anything meaningful gets merged, a security-focused reviewer for anything touching auth or data handling, and a dedicated planning subagent for anything big enough that jumping straight to implementation would mean skipping real architectural thinking. These three cover the overwhelming majority of what I actually invoke explicitly.
Rarely or never touched: most of the highly specialized framework-specific reviewers outside my actual stack, and most of the more exotic subagent patterns meant for genuinely large parallel research tasks. Not because they’re bad — because my day-to-day work mostly doesn’t need that scale of investigation, and reaching for tooling heavier than the problem actually calls for is its own kind of waste.
The honest lesson here: the value isn’t having access to everything. It’s knowing which three or four things you actually reach for reliably, and not feeling obligated to use the rest just because they exist.
Layer 8: Where I Draw the Line on AI Agent Autonomy 🛑
“Speed is only a win if I still trust the result. The moment I stop trusting it, speed is the thing that hurt me.”
Even with every layer above running, some things always require my explicit confirmation, full stop. Destructive git operations. Pushes. Anything genuinely irreversible. No amount of automation earns its way past that line, because the cost of getting it wrong there isn’t “redo some work” — it’s “undo damage that might not be undoable at all.”
How I’ve actually tuned the balance: the rule isn’t about how much I trust Claude Code generally — it’s about the reversibility of the specific action, not the perceived risk of the specific task. A large refactor across dozens of files is high-effort but fully reversible through version control, so I’m comfortable letting that run with less hand-holding. A single git push --force is low-effort and can be genuinely unrecoverable, so it gets a hard stop regardless of how routine it feels in the moment. Effort and risk aren't the same axis, and conflating them is exactly how someone ends up granting broad autonomy to the wrong category of action.
The honest version of “just do it” vs “ask first” isn’t a single dial. It’s a per-action-type decision, made once, in advance — not re-litigated in the moment when I’m tired and the confirmation prompt feels like the annoying thing standing between me and being done.
Why a Real Claude Code Setup Actually Matters 💭
None of these eight layers are impressive on their own. A CLAUDE.md file is just a text file. A hook is just a shell command that runs at the right time. What actually matters is that together, they turn Claude Code from “a chat window that happens to write code” into something closer to a properly configured engineering tool — one with memory, guardrails, structural understanding of the codebase, and real device verification, instead of just a fast typist with no persistent context and no sense of what’s actually reversible.
The setup isn’t the interesting part. The reasoning behind each layer is. If you’re still running Claude Code with none of this configured, the honest recommendation isn’t “add all eight layers at once” — it’s start with a real CLAUDE.md, actually written for the mistakes you’ve already made twice, and build outward from there.
Tags: Claude Code, AI Coding Agents, Software Engineering, Developer Tools, Agentic Coding, Mobile Development
