Claude Code,
the way it works.

A short, opinionated setup guide for any dev who wants the model to actually help, not just talk.

Read10 minSetup20 minStackanyDate2026-05-12
01

The whole idea, in 30 seconds

Claude Code works best when you set it up in three layers:

LayerLives inJob
Global~/.claude/How it talks, what it never does, what it always checks.
Project./CLAUDE.mdWhat this codebase is, the rules, the commands.
SessionThe chatBrainstorm, plan, execute, verify. Discarded when closed.
# If you find yourself correcting the same thing twice, that’s a missing rule, not a missing reminder.
02

Global setup, once per machine

Three files live in ~/.claude/ and apply to every project.

A · ~/.claude/CLAUDE.md, your defaults

One markdown file telling the model how you want it to behave everywhere:

  • Response style. “Keep answers short. No long preambles. Show the diff, not a recap.”
  • Stack defaults. “Backend NestJS, frontend Next.js, mobile Kotlin. Use repository pattern, DTOs, DI.”
  • Code style. “No comments unless explaining a non obvious why. Custom error classes, not generic Error.”
  • Git safety. “Never run git commit / push / merge / rebase on the main tree unless I say go.”
  • Before claiming done. “Run tests, lint, types. All green.”
Paste this into your global CLAUDE.md
~/.claude/CLAUDE.md
# Response style
- Keep answers under 8 lines unless I ask for more.
- After a tool call, one line saying what changed. No recap.
- No tables or headers for simple yes/no answers.

# Git
- Suggest git write commands, don't run them, unless I say go.
- Worktrees are always allowed.

# Done means done
Before saying a task is done:
1. Run the test suite. All green.
2. Run the linter. Zero warnings.
3. Run the type checker. Zero errors.

B · ~/.claude/settings.json, runtime config

Allowlist common commands so you stop clicking “allow”.

~/.claude/settings.json
{
  "permissions": {
    "allow": [
      "Bash(bun run:*)",
      "Bash(bun test:*)",
      "Bash(npx tsc:*)",
      "Bash(chmod:*)"
    ]
  },
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "~/.claude/hooks/block-dangerous-git.sh"
      }]
    }]
  }
}

C · The one global hook worth it

A tiny bash script that blocks dangerous git commands before they run. Save as ~/.claude/hooks/block-dangerous-git.sh, then chmod +x:

~/.claude/hooks/block-dangerous-git.sh
#!/usr/bin/env bash
input=$(cat)
cmd=$(echo "$input" | jq -r '.tool_input.command // ""')

patterns=(
  'git reset --hard'
  'git clean -f'
  'git branch -D'
  'git checkout \.'
  'git restore \.'
  'push --force'
  'push -f'
  'push --no-verify'
)

for p in "${patterns[@]}"; do
  if [[ "$cmd" =~ $p ]]; then
    echo "Blocked: $p. Run it yourself if you really mean it." >&2
    exit 2
  fi
done
exit 0

Two minutes to set up. Catches mistakes you’d wish you hadn’t made.

03

Project setup, once per repo

Every non-trivial repo gets a CLAUDE.md at the root.

What goes in it

  1. One line: what this repo is.
  2. Stack: versions, runtimes.
  3. How to run it: install, dev, test, build, deploy.
  4. Architecture: the main folders and what lives in each.
  5. Rules: the things that should never happen.
  6. Done checklist: the exact commands to run before claiming done.
Minimal but real CLAUDE.md
./CLAUDE.md
# Acme Dashboard

Internal admin dashboard for Acme. Next.js 14, PostgreSQL, Prisma.

## Run
- bun install
- bun dev          (local at :3000)
- bun test         (vitest)
- bun run build

## Layout
- app/             Next.js routes
- lib/db/          Prisma client, queries
- lib/api/         REST handlers
- components/      Reusable UI

## Rules
- Never query the DB from app/ directly. Go through lib/db/.
- All API responses use { success, data, error }.
- No CSS-in-JS. Use Tailwind.

## Done means done
1. bun test       all pass
2. bun lint       zero warnings
3. bunx tsc       zero errors
4. Manually open the page you changed and click through it.

You don’t need 800 lines. You need the rules a new teammate would ask about on day one.

Optional but nice: .claude/settings.json per project

Same pattern as global, scoped to this project.

./.claude/settings.json
{
  "permissions": {
    "allow": [
      "Bash(bun run:*)",
      "Bash(bun test:*)",
      "Bash(docker compose:*)",
      "Bash(prisma:*)"
    ]
  }
}
04

The workflow that actually works

For anything bigger than a one-liner, use this rhythm. Feels slow the first time. Saves hours after that.

StepWhat you sayWhat it does
1 · Brainstorm“Let’s brainstorm how to add X.”Asks questions, surfaces unknowns, before any code.
2 · Plan“Write a plan for this.”Step by step approach, file list, risks. You review.
3 · Execute“Execute the plan.”Works through it, ticking off steps.
4 · Verify“Verify before claiming done.”Runs tests, lint, types. Reports actual output, not vibes.
Real shape of a session
session.log
You:    Let's add invoice PDF export. Brainstorm first.

Claude: A few questions. Server side render or client?
        Header / footer template fixed or per tenant?
        Async (queue + email) or sync (download)?

You:    Server side, fixed template, sync download.

You:    Plan it.

Claude: [returns 6 step plan, files to touch, libraries to add]

You:    Looks good, execute.

Claude: [works through steps, marks each done]

You:    Verify.

Claude: bun test  -> 47 pass
        bun lint  -> 0 warnings
        bunx tsc  -> 0 errors
        Manually tested /invoices/123/pdf. Renders.
05

Tools worth installing

Install once, use everywhere. Skip the rest, they cost context tokens.

ToolUse it when…
superpowersYou want brainstorm / plan / execute / verify built in.
context7You need current library docs. Training data is months old.
serenaYou’re renaming or refactoring across many files. Faster than grep.
code-reviewYou want /review on a PR or branch before merging.
chrome-devtoolsYou want the model to actually open a page and test it.
frontend-designYou’re building UI and want it to not look generic.
firecrawlYou need live web search or page scraping.
typescript-lspYou code in TS daily and want type aware edits.

Install with /plugin install <name>.

06

Lazy prompts vs good ones

Same task, different specificity. The second one saves you a round trip.

Lazy
fix the bug in users
Better
In app/users/page.tsx, the list shows duplicate rows when
filtering by status. Investigate why, write a failing test
that reproduces it, then fix it. Don't touch unrelated code.
Lazy
add invoices
Better
Add an invoices module. Brainstorm with me first, then plan,
then execute. Follow the rules in CLAUDE.md (response shape,
db access through lib/db only).
Lazy
make this faster
Better
The /dashboard page takes 4s to first paint. Profile it
(use chrome-devtools), find the top 3 culprits, propose fixes.
Don't implement until I pick which to do.
07

Do / Don’t

do

  • Write a CLAUDE.md for every real project.
  • Allowlist your common commands. Stop clicking allow.
  • Brainstorm before building anything non trivial.
  • Ask it to verify (tests, lint, types) before claiming done.
  • Use context7 for library APIs. Don’t trust the model’s memory.
  • Read the diff. Don’t accept “I updated the file” as proof.
  • Tell it when you’re unhappy. It will adjust.

avoid

  • Don’t let it run git push / commit / merge without you saying go.
  • Don’t skip the project CLAUDE.md. It has no other way to know your rules.
  • Don’t let it design visuals (decks, mockups, social). Use Claude Design.
  • Don’t accept vague “done”. Ask for actual command output.
  • Don’t install plugins you won’t use weekly. They cost context.
  • Don’t use --no-verify to silence a failing hook. Fix the issue.
08

Tips & tricks

Things that aren’t obvious until someone tells you.

[01]

The model can fix its own setup, let it.

When something feels off (slow responses, weird behavior, missing rules), the model can audit and fix its own config.

Prompt
Audit my ~/.claude/CLAUDE.md and ~/.claude/settings.json.
Tell me what's missing, what's weak, what I should add.
Then propose the fixes as diffs.
[02]

Long sessions get dumb. Ask before continuing.

Long sessions degrade. The model compresses earlier turns and starts inventing decisions you didn’t make. For anything new, start fresh. Ask the current session to dump what’s worth carrying first.

Prompt
This session is getting long. I want to add feature X next.
Should we continue here or start a new session?
If new, dump the context I need to bring over.
[03]

The model agrees too easily. Force it to push back.

By default it agrees with whatever you put in front of it. Doesn’t matter if the idea is actually good. Tell it to argue with you before you act on anything that matters.

Prompt
Before I act on this plan, run /grill-me on it.
What's wrong with it? What did I miss?
Don't agree with me, find the holes.

The /grill-me command (superpowers plugin) is built for this.

[04]

Use separate sessions for plan, execute, and review.

A fresh session is a fresh critic. The one that wrote the plan thinks the plan is good. A different one, with no memory of writing it, will catch what the author missed.

Workflow
Session 1: "Give me a prompt to start a new feature
            that does X, Y, Z."
           -> Claude writes the prompt.

Session 2: Paste the prompt fresh.
           -> Claude executes the task.

Session 1: "Here's what session 2 produced: <paste>.
            Validate it. Does it match what you asked for?"

Same trick works for code review: write the code in one session, review it in another.

[05]

Learn the tooling. Build your own.

Hooks, plugins, slash commands, skills. Ask the model to explain any of them, then build the ones you want. A hook that blocks rm -rf takes five minutes to write.

Prompt
Explain how Claude Code hooks work. Then write me a
PreToolUse hook that blocks any Bash command containing
`rm -rf` or `DROP TABLE`. Put it in ~/.claude/hooks/.

Caveat: hooks catch what you remember to block, nothing more. The actual safety net is reading the diff before you accept it.

[06]

Use /clear between unrelated tasks.

Same session, different problem? Run /clear. Same window, fresh context. The answers stop being colored by whatever you were just doing.

[07]

Point to files with exact paths, not vague names.

“Look at the users file” makes the model guess. lib/db/users.ts:42 removes the guessing.

Lazy
fix the bug in the dashboard
Better
fix the bug in app/dashboard/page.tsx, the useEffect
on line 58 fires twice on mount
[08]

Ask for diffs, not “I’ll update the file.”

When the model says “I’ve updated X”, that’s a description, not evidence. Make it show the diff. Read it before you accept it.

Prompt
Show me the diff of what you changed before moving on.
I want to see the actual edit, not a summary.
[09]

Make it estimate confidence.

The model sounds confident even when guessing. Ask outright: low, medium, or high. That pulls the hedge out into the open.

Prompt
How confident are you in this fix, low / medium / high?
What could still break? What did you not check?
[10]

Let it write the test first.

For anything with logic, ask for a failing test before the fix or feature. The test becomes the spec. “Done” turns into something you can run, not something you have to trust.

Prompt
Before fixing this bug, write a failing test that reproduces
it. Run it, confirm it fails. Then write the fix and confirm
it passes.
09

Quick start, 20 minutes

If you’re starting from zero, this is the entire setup.

# An hour writing CLAUDE.md will save you a hundred prompts later.