Setting Up Claude Code in a New Project: A Checklist
Why the First 30 Minutes Matter
Most engineers install Claude Code, run one prompt in the repo root, and start typing away. It works, sort of. Then three days later they discover Claude has been reading .env files it should never have touched, has no idea the project uses pnpm instead of npm, and keeps suggesting patterns that violate a testing convention nobody wrote down anywhere. None of that is a Claude Code problem. It's a setup problem.
The gap between "Claude Code technically works in my project" and "Claude Code actually understands my project" comes down to about a dozen small configuration steps, most of which take less time than making a coffee. This checklist walks through them in order: installation, project memory, permissions, MCP servers, hooks, and the habits that keep the setup from rotting after week one. If you're the kind of person who likes to understand the "why" behind each step rather than blindly copy a config file, this is written for you.
We cover this exact setup process, step by step, inside our Claude Code Tutorial for Beginners course — but you don't need the course to follow along here. Everything below works whether you're setting up a brand-new repo or retrofitting Claude Code into a codebase that's been running for years.
Step 1: Install, Authenticate, and Confirm the Basics
Start clean. If you already have an old version floating around, upgrade it rather than layering a second install on top of it.
npm install -g @anthropic-ai/claude-code
claude --versionRun claude once from your home directory, not inside a project yet, to complete authentication. You'll be prompted to log in with your Anthropic account or paste an API key if you're wiring it to a workspace that bills through the API rather than a Claude subscription. Decide this up front. Mixing personal subscription auth with team API billing later is annoying to untangle, and it's much easier to pick the right mode before you have three teammates depending on your setup.
A checklist item people skip: confirm which model tier you default to.
claude config get modelIf your project involves heavy refactors or architectural decisions, you generally want the strongest available model as the default, and you can dial down to a faster, cheaper one per-task later when the work is simple and repetitive. Don't leave this on a stale default from months ago just because nobody thought to check it.
Step 2: Let Claude Code Read Itself In, Then Correct It
Before writing configuration by hand, run Claude Code's built-in initializer inside your project root. It scans the codebase, infers the stack, and drafts a starting CLAUDE.md for you.
cd your-project
claudeOnce inside the interactive session, run:
/initThis walks the directory tree, looks at your package manager files (package.json, pyproject.toml, go.mod, whatever applies), samples a handful of source files, and produces a first-draft memory file describing your stack, folder layout, and any conventions it detected on its own. Do not treat this draft as finished. Treat it as a rough transcript you now edit for accuracy, the same way you'd edit a new hire's onboarding notes before handing them to the next new hire.
A common mistake here is running /init and immediately moving on without reading the output line by line. Read every line. Delete anything wrong. Claude Code treats CLAUDE.md as ground truth, so a wrong inference left uncorrected gets repeated back to you in every future session, often with more confidence than the first time.
Step 3: Write a CLAUDE.md That Earns Its Place in Every Prompt
This is the single highest-leverage file in the entire setup. CLAUDE.md sits at your project root and its contents get loaded into context automatically every time Claude Code starts a session there. Think of it as onboarding notes for a very fast, very literal new hire who will forget everything the moment the session ends unless it's written down.
A CLAUDE.md that actually helps looks less like a mission statement and more like an internal engineering wiki page. Include:
- The exact commands to run tests, lint, typecheck, and build — not "run the tests," but the literal command string
- Naming conventions that aren't obvious from the code alone
- Anything Claude should never touch: generated files, vendored directories, merged migration files
- How to run the app locally, including which environment variables are required
- Any non-standard architectural decision that would otherwise look like a bug
Here's a compact but genuinely useful example for a typical Node and TypeScript API project:
# Project: billing-service
## Stack
Node 20, TypeScript, Express, PostgreSQL (raw SQL via `pg`, no ORM), Vitest.
## Commands
- Install: `npm ci`
- Dev server: `npm run dev` (port 4000)
- Tests: `npm test` (watch mode is `npm run test:watch`)
- Typecheck: `npm run typecheck`
- Lint: `npm run lint -- --fix`
## Conventions
- Route handlers: `src/routes/*.handler.ts`
- SQL lives in `src/db/queries/*.sql`, loaded via `src/db/loadQuery.ts`
- Never edit files under `src/generated/` — they come from `npm run codegen`
- All money values are integer cents, never floats
## Do NOT
- Do not add new npm dependencies without asking first
- Do not modify files under `infra/` without explicit confirmation
- Do not touch `.env*` filesNotice there's no fluff. Every line either saves Claude a wrong guess or prevents a category of mistake you've already seen once and don't want to see again. Keep the whole file under a page — Claude re-reads it on every session, so a bloated CLAUDE.md wastes context budget and dilutes the instructions that actually matter. Update it the same way you'd update a README: whenever a convention changes, or whenever you catch the agent making the same wrong assumption twice.
One underused trick for larger codebases: nested CLAUDE.md files. If you have a monorepo with packages/api and packages/web, drop a CLAUDE.md inside each subfolder with package-specific rules. Claude Code picks up the relevant nested file based on where you're working, in addition to the root-level one.
Step 4: Decide Your Permission Model Before Your First Real Task
By default, Claude Code asks for approval before running bash commands, editing files, or touching the network through certain tools. That's the right default for a brand-new, unfamiliar codebase. But if you leave every project at maximum caution forever, you'll spend your sessions dismissing permission prompts for commands you already trust, like git status or npm test.
The fix is a project-level .claude/settings.json, checked into the repo and shared with your team, plus an optional .claude/settings.local.json, gitignored and personal. Use the shared file for permissions everyone on the team agrees are safe, and the local file for anything specific to your own machine.
{
"permissions": {
"allow": [
"Bash(git status)",
"Bash(git diff:*)",
"Bash(git log:*)",
"Bash(pnpm test:*)",
"Bash(pnpm lint:*)",
"Bash(pnpm typecheck:*)"
],
"deny": [
"Bash(git push --force:*)",
"Bash(rm -rf:*)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./**/secrets/**)"
]
}
}A few notes on this file that trip people up:
- The
allowlist doesn't mean "never ask" for everything — it means these specific command patterns are pre-approved. Anything not matched still prompts. - The
denylist is a hard stop enforced by the harness itself, not a request the model can decide to override. Put your genuinely destructive commands there explicitly, even if you think you'd never type them by accident. You will, eventually, and this is the safety net. - Don't add broad wildcards like
Bash(*)toallowjust to stop the prompts. That defeats the entire safety model and turns Claude Code into something that can run anything unattended in your repo.
Explicitly blocking .env reads and secrets directories means you don't have to trust that Claude will always ask nicely before looking at something sensitive — the permission model enforces it regardless of what the model decides mid-task.
Step 5: Configure Hooks for Anything That Must Never Be Skipped
Hooks are shell commands that Claude Code's harness executes automatically at specific lifecycle points: before a tool runs, after a tool runs, when a session ends. They matter because some behaviors can't be reliably encoded as an instruction in CLAUDE.md. A memory file is a suggestion the model reads and generally follows; a hook is a script the harness actually executes, every time, regardless of what the model remembers to do under pressure.
The clearest example: if you want formatting enforced after every file edit, don't write "always run prettier after editing" in CLAUDE.md and hope. Write a hook instead.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_FILE_PATH\" 2>/dev/null || true"
}
]
}
]
}
}This runs Prettier against whatever file was just edited, every single time, no exceptions, no forgetting. The same mechanism works for blocking edits to protected paths outright:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "echo \"$CLAUDE_FILE_PATH\" | grep -q '^infra/' && exit 1 || exit 0"
}
]
}
]
}
}If a behavior needs to be guaranteed rather than merely requested, it belongs in a hook. For anything security-sensitive — secrets, production configs, billing code — prefer a hook or a deny permission over a polite note in CLAUDE.md.
Step 6: Wire Up MCP Servers Relevant to This Project, and Only Those
Model Context Protocol servers extend Claude Code with tools beyond the filesystem and shell: a database client, a browser automation tool, a project-management integration, a design tool. The temptation on a new project is to connect everything you've ever used. Resist it. Every connected MCP server adds tool definitions to the context window on every turn, and an agent choosing between forty half-relevant tools makes worse decisions than one choosing between six clearly relevant ones.
Check what's currently connected:
claude mcp listAdd servers deliberately, scoped to the project when the whole team needs them. For a database MCP server, scope its credentials tightly — if it only needs to read schema and debug queries, give it a read-only role, never a production write role:
claude mcp add staging-db --scope project \
-- npx -y @modelcontextprotocol/server-postgres \
"postgresql://readonly_user@staging-host:5432/mydb"Before adding a server, ask whether the task actually needs it this week. A project that touches no browser can safely skip browser-automation MCP servers entirely. You can always add one later in thirty seconds; removing noise from an already-bloated context is a slower process, because you first have to notice it's causing problems. Remove anything you added for a one-off task and never touched again:
claude mcp remove staging-dbStep 7: Set Up Subagents and Slash Commands for Repeatable Jobs
Subagents hand off a bounded piece of work, a code review, a security pass, a test run, to a separate context that doesn't pollute your main conversation with its intermediate steps. For a new project, define one or two early, even if the built-in general-purpose agent would technically do the job.
A useful first subagent for almost any project is a reviewer scoped to your stack's actual pain points, not a generic "review this code" prompt. Define it as a markdown file under .claude/agents/:
---
name: reviewer
description: Reviews diffs for correctness bugs, missing tests, and violations of project conventions. Use after any non-trivial change.
tools: Read, Grep, Bash
---
You review code changes in this repository. Check specifically for:
- SQL queries built with string concatenation instead of parameterized queries
- New route handlers missing a corresponding test file
- Money values stored as floats instead of integer cents
- Any edit to files under src/generated/
Report findings as a short bulleted list, one line per finding, with file and line number.This is more useful than a generic reviewer precisely because it encodes the specific mistakes this project has actually made before. Update it the way you'd update CLAUDE.md: every time a bug slips through that this subagent should have caught, add a line for it.
Slash commands solve a related but different problem: turning a repeatable multi-step ritual into a one-word invocation. Drop a markdown file in .claude/commands/ and it becomes available as /deploy-check:
---
description: Run the pre-deploy verification suite
---
Run the full pre-deploy checklist:
1. `pnpm typecheck`
2. `pnpm test`
3. `pnpm build`
4. Check that no console.log statements were left in `src/`
5. Report pass/fail for each step clearlyEvery project I've worked on ends up with three or four of these within the first week: one for running tests, one for pre-deploy checks, one for generating a changelog entry, one for whatever manual process keeps getting forgotten. Commit them so the whole team inherits the shortcut the moment they clone the repo.
Step 8: Decide Your Git Workflow Rules Up Front
This is the section most teams regret not doing on day one. Claude Code can commit, branch, and even push if you let it, and the defaults are sane, but "sane defaults" and "your team's actual workflow" are not always the same thing.
Put explicit git rules in CLAUDE.md:
## Git Workflow
- Never commit directly to main
- Always create a feature branch named `claude/<short-description>`
- Never force-push
- Never use --no-verify to skip hooks
- Ask before creating a pull request; don't auto-merge
- Commit messages: imperative mood, explain why not just whatCombine this with the deny permissions from Step 4, blocking git push --force, so the rule is backed by an actual guardrail rather than just a request. If your team has a strict commit message convention or requires linked ticket numbers, spell it out here too. It's cheap to write and saves a dozen rewritten commit messages down the line.
Step 9: Bake Verification Into the Habit, Not Just the Config
None of the configuration above replaces checking the work. A properly set up Claude Code produces better first drafts, runs your actual test command instead of guessing at one, and follows your naming conventions, but "better first draft" is not the same as "correct." Build a habit around this: after any non-trivial change, actually run the test suite yourself, read the diff rather than skimming a summary, and spot-check any file the agent claims to have verified.
A cheap way to make this concrete is to add a short verification note directly to CLAUDE.md:
## Before considering any task done
- Run `pnpm test` and confirm it passes, do not just claim it passes
- Run `pnpm typecheck`
- Show a diff of every changed file, not just a summary
- Never run the full e2e suite unless the task specifically requires itThat last line matters more than it looks. Without it, Claude Code will sometimes run your slowest test suite after every trivial change because it's being thorough, which burns time and tokens for no benefit. Being explicit about when heavyweight verification is warranted keeps the feedback loop tight for the majority of changes that only need a fast unit test pass.
Revisit the Setup After the First Real Week
Nobody gets this perfectly right on day one, and that's fine. The mistake is treating the initial setup as permanent. After the first week of real usage, do a quick pass:
- Check
.claude/settings.json— are there commands you keep manually approving that should move toallow? Any close calls that should move todeny? - Reread CLAUDE.md — has anything gone stale, or did you discover an unwritten convention the hard way that should now be documented?
- Audit MCP servers — anything connected that hasn't been used, or anything missing you kept wishing you had?
- Look at hook behavior — did any hook fire in a way you didn't expect, or block something it shouldn't have?
Treat this the same way you'd treat onboarding docs for a new human hire: written once, wrong in small ways immediately, and only useful if someone actually updates it based on what really happens.
The Condensed Checklist
If you want the short version to paste somewhere and just work through:
- Install and authenticate Claude Code, confirm your default model
- Run
/initinside the project, then correct its output by hand - Write a lean, project-root
CLAUDE.md— stack, exact commands, conventions, do-not list - Create
.claude/settings.jsonwith explicitallowanddenypermissions, commit it - Add hooks for anything that must be enforced, not just requested
- Connect only the MCP servers this project actually needs, scoped and least-privilege
- Define one or two subagents and slash commands for repeatable, project-specific rituals
- Spell out git workflow rules explicitly, backed by deny permissions
- Bake a verification step directly into CLAUDE.md so "done" always means "checked"
- Revisit the whole setup after week one, based on what actually happened, not guesses
None of this is exotic. It's the same discipline you'd apply to onboarding a new contractor: give them the map, tell them the boundaries, and make the important boundaries physically enforced rather than politely requested. The teams that get frustrated with Claude Code doing "the wrong thing" are almost always the ones that skipped this setup and expected the model to guess their conventions from vibes.
If you want to go deeper on this — advanced permission patterns, writing your own MCP servers, and structuring subagents for a real production codebase rather than a toy example — that's exactly the ground we cover step by step in our Claude Code Tutorial for Beginners course, building up from a completely empty project to a fully configured, team-ready setup.
AI CodingShip full-stack AI apps at conversation speed — specs, agents, deploys, all from the terminal.
Claude CodeGo from zero to confident with Claude Code, the terminal agent that reads, edits, runs, and verifies real code.
Related reading