Claude Code is Anthropic's agentic coding tool. It runs in your terminal, IDE, desktop app or browser, reads your codebase, runs commands, edits files and checks its own work. Used well, it can take a feature from idea to a tested pull request. Used carelessly, it produces confident code that is almost right.
The difference is almost entirely in configuration and workflow. This guide explains every file Claude Code reads, what belongs in each one, and a step-by-step process for building and testing a real web application with it.
The mental model: context, procedures, rules and workers
Claude Code gives you several ways to steer it. They are not interchangeable, and choosing the right one is the single most useful thing to learn:
- CLAUDE.md – what Claude always knows: build commands, conventions, project layout. Loaded into every session.
- Rules (
.claude/rules/) – instructions that only load for matching files, like "API handlers must validate input". - Skills (
.claude/skills/) – procedures Claude runs on demand, like "how we release" or "how we write a migration". Loaded only when used. - Subagents (
.claude/agents/) – workers with their own clean context and limited tools, like a code reviewer or a test writer. - Hooks (in settings) – rules the tool enforces, no matter what the model decides. A hook that blocks an action cannot be talked around.
- Settings and permissions (
.claude/settings.json) – which commands and files Claude may use without asking. - MCP servers (
.mcp.json) – connections to external tools and data, such as a browser, a database or an issue tracker.
A good rule of thumb: hooks are law, CLAUDE.md and skills are guidance, subagents are scoped workers.
The complete file map
Here is everything a well-configured web project might contain. You rarely need all of it on day one.
my-web-app/
├─ CLAUDE.md # Team instructions, loaded every session (commit it)
├─ CLAUDE.local.md # Your personal notes for this project (gitignore it)
├─ AGENTS.md # Optional: shared instructions for other AI tools
├─ .mcp.json # Project MCP servers, shared with the team
├─ .claude/
│ ├─ settings.json # Shared permissions, hooks, env (commit it)
│ ├─ settings.local.json # Your personal overrides (gitignored automatically)
│ ├─ rules/
│ │ ├─ testing.md # Always-on or path-scoped rules
│ │ └─ api.md # e.g. paths: ["src/app/api/**/*.ts"]
│ ├─ skills/
│ │ ├─ new-feature/SKILL.md # Reusable procedure, invoked as /new-feature
│ │ └─ release/SKILL.md
│ ├─ agents/
│ │ ├─ code-reviewer.md # Subagent with read-only tools
│ │ └─ test-writer.md
│ ├─ commands/ # Older style of custom slash commands (still works)
│ └─ hooks/
│ └─ format.sh # Scripts called by hooks in settings.json
└─ src/ …On your machine there is a matching personal layer in your home directory: ~/.claude/CLAUDE.md, ~/.claude/settings.json, ~/.claude/rules/, ~/.claude/skills/ and ~/.claude/agents/. These apply to every project.
CLAUDE.md: the project brief
CLAUDE.md is plain Markdown that Claude reads at the start of every session. It can live at ./CLAUDE.md or ./.claude/CLAUDE.md. Claude also loads CLAUDE.md files from parent folders at launch, and from subfolders when it starts working in them, so a monorepo can have one per package.
Run /init in a new project and Claude will generate a first draft by analysing your codebase. Then edit it.
What to put in it
- The exact commands to install, run, build, lint, type-check and test.
- Project layout that is not obvious from folder names.
- Conventions that differ from defaults ("we use pnpm", "server actions live in
src/actions"). - Hard rules ("never edit generated files in
src/gen", "all money values are integers in cents"). - How to verify work ("a task is done when
pnpm testandpnpm typecheckpass").
What to leave out
- Anything Claude can discover by reading the code, such as long directory listings or dependency lists.
- Procedures that only matter sometimes. Move those into skills.
- Rules for one part of the codebase. Move those into path-scoped rules.
Keep it under about 200 lines. Every line competes for attention, and vague lines ("write clean code") help nobody.
# Project: Acme Tasks (Next.js 16, TypeScript, Postgres via Drizzle)
## Commands
- Install: `pnpm install`
- Dev server: `pnpm dev` (http://localhost:3000)
- Checks: `pnpm lint && pnpm typecheck && pnpm test`
- E2E: `pnpm test:e2e` (Playwright, needs `pnpm dev` running)
## Conventions
- Server components by default; add "use client" only for interactivity.
- Database access only through `src/db/queries/*`. Never query from components.
- Validate every request body with the zod schemas in `src/lib/schemas.ts`.
## Definition of done
- New logic has unit tests. User-visible flows have a Playwright test.
- `pnpm lint && pnpm typecheck && pnpm test` passes before you say you are finished.
@docs/architecture.mdThe last line is an import: @path/to/file pulls another file into context. Imports keep the main file readable, but imported files still load every session, so use them for things that truly are always relevant.
CLAUDE.local.md and AGENTS.md
CLAUDE.local.md holds your personal, project-specific notes, such as your local database URL or preferred test data. Add it to .gitignore.
AGENTS.md is a cross-tool standard that Codex, GitHub Copilot, Cursor and others read. Current versions of Claude Code read AGENTS.md directly when there is no CLAUDE.md. If your team uses several tools, a clean pattern is to keep shared rules in AGENTS.md and put a short CLAUDE.md next to it that imports it:
@AGENTS.md
## Claude Code only
- Use plan mode for any change under `src/billing/`..claude/rules/: instructions that load only when relevant
For larger projects, split instructions into topic files under .claude/rules/. A rule with a paths field only loads when Claude reads a matching file, which saves context and reduces noise.
---
paths:
- "src/app/api/**/*.ts"
---
# API route rules
- Validate input with zod before any other logic.
- Return errors as { error: { code, message } } with the right HTTP status.
- Every route needs an integration test in tests/api/.Rules without paths load in every session, just like CLAUDE.md.
.claude/settings.json: permissions, environment and hooks
Settings are JSON files layered by scope. From highest to lowest priority: organisation-managed settings, command-line flags, .claude/settings.local.json (you, this project), .claude/settings.json (the whole team) and ~/.claude/settings.json (you, every project).
The most important part is permissions. Allow the safe, frequent commands so Claude does not interrupt you, and deny what it should never touch.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(pnpm lint)",
"Bash(pnpm typecheck)",
"Bash(pnpm test *)",
"Bash(pnpm test:e2e *)",
"Bash(git status)",
"Bash(git diff *)"
],
"ask": ["Bash(git push *)"],
"deny": ["Read(./.env)", "Read(./.env.*)", "Bash(rm -rf *)"]
},
"env": { "NODE_ENV": "development" }
}Settings files must be strict JSON: no comments and no trailing commas. Run /status to confirm what loaded and /permissions to review rules interactively.
Permission modes
You can switch how much autonomy Claude has during a session (Shift+Tab cycles through the common ones):
- Default – asks before edits and unapproved commands.
- Accept edits – edits files freely, still asks for risky commands.
- Plan mode – read-only. Claude explores and proposes a plan without changing anything. Ideal before large changes.
- Auto and bypass modes – far less supervision. Use them only in isolated environments such as containers or throwaway worktrees.
Hooks: guardrails the model cannot ignore
Hooks are shell commands (or HTTP calls, prompts or agents) that run at fixed points in Claude's lifecycle. The most useful events for web projects are:
- PreToolUse – before a tool runs. Exiting with code 2 blocks the action and sends your message back to Claude.
- PostToolUse – after a tool runs, for example to format a file Claude just edited.
- Stop – when Claude thinks it is finished. A good place to insist the tests pass.
- SessionStart – when a session begins, for example to print the current branch and open issues.
- UserPromptSubmit, Notification, SubagentStop and PreCompact cover other moments.
Format every edited file automatically:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/format.sh" }]
}
]
}
}#!/usr/bin/env bash
# .claude/hooks/format.sh – receives the tool call as JSON on stdin
file=$(jq -r '.tool_input.file_path // empty')
[ -n "$file" ] && npx prettier --write "$file" >/dev/null 2>&1
exit 0Block any edit to environment files:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"if": "Edit(.env*)",
"command": "bash",
"args": ["-c", "echo 'Editing .env files is not allowed' >&2; exit 2"]
}]
}
]
}
}Use /hooks inside Claude Code to inspect what is configured.
Skills: reusable procedures
A skill is a folder containing a SKILL.md file, plus any scripts or templates it needs. You invoke it with /skill-name, or Claude loads it automatically when your request matches its description. Only the short description is always in context; the body loads when used, so long checklists cost almost nothing until needed.
---
name: new-feature
description: Scaffold a new feature end to end with route, component, tests and docs. Use when asked to add a new page or feature.
argument-hint: "[feature-name]"
allowed-tools: Read Grep Glob Edit Write Bash(pnpm test *)
---
Build the feature "$ARGUMENTS":
1. Read `docs/architecture.md` and one existing feature folder as a pattern.
2. Write failing tests first: unit tests in `tests/unit/` and one Playwright flow in `tests/e2e/`.
3. Implement until `pnpm test` passes. Do not change the tests to make them pass.
4. Run `pnpm lint && pnpm typecheck`.
5. Summarise what changed and anything left to decide.Useful frontmatter fields include description (when to use it), argument-hint, allowed-tools (tools pre-approved while the skill runs), disable-model-invocation: true (only you can trigger it, good for deploys), context: fork (run it in a subagent) and paths (only offer it for matching files).
Older projects may have .claude/commands/*.md files. Those still work and create the same /name commands; skills are the newer, more capable format.
Subagents: focused workers with their own context
A subagent is a separate Claude instance with a fresh context window, its own system prompt and a restricted toolset. Define one as a Markdown file in .claude/agents/:
---
name: code-reviewer
description: Reviews diffs for bugs, security issues and missing tests. Use after completing a feature and before opening a pull request.
tools: Read, Grep, Glob, Bash(git diff *)
model: sonnet
---
You are a strict senior reviewer. Examine the current diff and report:
1. Bugs and incorrect edge cases.
2. Security problems: missing auth checks, unvalidated input, secrets in code.
3. Missing or weak tests.
Reference file and line for each finding. Do not edit files.Ask for it by name ("use the code-reviewer subagent on this branch"), or let Claude delegate automatically based on the description. Subagents are excellent for review, research across a large codebase and test generation, because they keep noisy work out of your main conversation.
MCP: connecting tools and data
The Model Context Protocol lets Claude use external tools. For web projects the most valuable are a browser automation server (so Claude can open your app, click through it and read console errors), your issue tracker, and database or documentation servers.
Project-wide servers live in .mcp.json so the whole team shares them:
{
"mcpServers": {
"playwright": { "command": "npx", "args": ["@playwright/mcp@latest"] }
}
}You can also add servers from the command line with claude mcp add, and check them with /mcp. Treat every MCP server as code you are running: prefer well-known publishers, and give each the narrowest credentials possible.
Step by step: building a web app with Claude Code
Step 1: Scaffold and commit a clean baseline
Create the project with the framework's own generator (for example npx create-next-app@latest or npm create astro@latest), install dependencies and make a first commit. A clean git history lets you review and undo every agent change.
Step 2: Generate and refine CLAUDE.md
Run claude in the project folder, then /init. Edit the result: add the exact check commands, your conventions and a definition of done. Commit it.
Step 3: Set up the test harness before features
Ask Claude to install and configure your test stack, and verify each piece runs:
- Unit tests with Vitest or Jest for pure logic and utilities.
- Component tests with Testing Library for UI behaviour.
- End-to-end tests with Playwright for real user flows.
- Type checking with
tsc --noEmitand linting with ESLint.
Add all of these to package.json scripts and to CLAUDE.md. An agent that can run fast, reliable checks iterates towards correct code; one that cannot is guessing.
Step 4: Add permissions and hooks
Allow the check commands in .claude/settings.json, deny secret files, and add the formatter hook. Optionally add a Stop hook that runs the test suite so Claude cannot declare victory while tests fail.
Step 5: Plan each feature
For anything beyond a small change, switch to plan mode and describe the feature, the constraints and how you will know it works. Review the plan, push back on anything wrong, then let Claude implement. Correcting a plan is far cheaper than reviewing a wrong 800-line diff.
Step 6: Build test-first
Ask Claude to write the tests first, confirm they fail, then implement until they pass, with an explicit instruction not to modify the tests. This is the most reliable way to keep an agent honest.
Add password reset. Write failing tests first (unit for token logic, Playwright for the email-link flow),
show me they fail, then implement until they pass. Do not edit the tests to make them pass.Step 7: Verify in a real browser
Unit tests do not catch layout bugs or console errors. With a browser MCP server, ask Claude to start the dev server, open the page, complete the flow and report any console or network errors. Look at the result yourself too.
Step 8: Review with a fresh context
Run your code-reviewer subagent (or the built-in code review command) on the diff. A reviewer with fresh context spots problems the implementing session has become blind to. Then read the diff yourself.
Step 9: Commit, open a pull request and let CI decide
Have Claude write a descriptive commit message and pull request summary. CI should run the same checks as your definition of done. For automation, Claude Code can also run non-interactively with claude -p "...", and there is a GitHub Action for responding to issues and pull requests.
Testing strategy for AI-built code
AI changes how you should think about tests:
- Tests are the specification. Clear, readable tests tell the agent what "correct" means better than prose does.
- Watch for tests edited to pass. Review test changes as carefully as code changes.
- Prefer real behaviour over mocks. Heavily mocked tests let incorrect code pass. Use a real test database or containers for integration tests where practical.
- Cover the edges explicitly. Empty states, errors, permissions, slow networks and large inputs are where generated code fails most.
- Keep the suite fast. If tests take ten minutes, the agent will run them less and guess more. Split fast and slow suites.
- Make flaky tests a priority. Agents cannot tell a flaky failure from a real one.
Managing context and cost
- Use
/clearbetween unrelated tasks. Old context confuses new work. - Use
/compactin long sessions to summarise history. Your projectCLAUDE.mdis reloaded afterwards. - Run
/contextto see what is using the context window. - Delegate large searches and reviews to subagents so their output does not flood your main session.
- Choose the model per task: a faster model for routine edits, the most capable one for planning and hard debugging.
Security checklist
- Deny reads of
.envfiles and keep production secrets off development machines. - Review every new dependency the agent adds.
- Be careful when Claude reads untrusted content (issues, web pages, dependency READMEs); it can contain prompt injection. Keep side effects behind approval.
- Use autonomous modes only in sandboxes, containers or disposable worktrees.
- Never let generated code skip authentication or authorisation checks; make them part of your rules and tests.
Common mistakes
- A giant CLAUDE.md. Move procedures into skills and area-specific guidance into rules.
- No tests before features. The agent has nothing to check its work against.
- Accepting large diffs unread. Keep tasks small and review every change.
- Relying on instructions for hard rules. If it must never happen, write a hook or a deny rule.
- One endless session. Clear context between tasks and write progress to a file for long work.
Key takeaways
CLAUDE.mdis what Claude always knows; keep it short, specific and verifiable.- Rules, skills, subagents and hooks each solve a different problem. Use the right one.
- Set up tests, type checks and linting before building features.
- Plan first, build test-first, verify in a browser, review with fresh eyes.
- Guard secrets and side effects with permissions and hooks, not just instructions.

Run Parallel AI Coding Agents with Git Worktrees: A Practical Workflow4 min read
Build a Web App with OpenAI Codex: AGENTS.md, config.toml, Sandbox and Testing8 min read
Build a Web App with GitHub Copilot: Instructions, Agents, Cloud Agent and Testing8 min read