CLAUDE.md and Skill Files: The Complete Best Practices Guide for 2026
CLAUDE.md is loaded before every Claude Code session. Most developers treat it like a wish list and wonder why Claude ignores half of it. Here's the research-backed guide to writing one that actually works — and when to offload context to skill files instead.
A lean, effective CLAUDE.md — under 50 lines, nothing wasted
What is CLAUDE.md?
CLAUDE.md is a markdown file that Claude Code reads automatically at the start of every session. Think of it as a persistent briefing document — a way to tell Claude things about your project that it can't infer from the code alone: how to run your tests, which patterns your team prefers, what it should never do, and where to look for domain-specific knowledge.
You can place CLAUDE.md files in several locations, and Claude loads all of them:
- ~/.claude/CLAUDE.md — your personal global rules, applied to every project
- ./CLAUDE.md — the project root file, checked into git and shared with your team
- ./src/CLAUDE.md — subdirectory files, loaded on demand when Claude works in that folder
- Parent directories — useful in monorepos where both root and package-level files are pulled in
The file also supports imports. You can write @path/to/file to pull in other documents — a clean way to keep the main file short while linking to deeper references like authentication flow docs or architecture diagrams.
CLAUDE.md — uppercase CLAUDE, lowercase .md. Claude Code looks for this specific casing. A file named claude.md will be silently ignored.The instruction budget problem
Here's the thing most guides don't tell you: Claude doesn't treat your CLAUDE.md as a permanent command list. It treats it as context — and context competes with everything else in the session for the model's attention.
Research from HumanLayer into Claude Code's internals found that frontier thinking models can follow roughly 150–200 instructions with reasonable consistency before compliance degrades. Smaller or non-thinking models follow even fewer. Here's the problem: Claude Code's own system prompt already occupies around 50 of those slots before you write a single line of your own.
That leaves you with roughly 100–150 instruction slots for your rules, preferences, and workflows combined. This isn't a lot. And Claude doesn't simply drop newer instructions when the budget runs out — it begins to follow all instructions less reliably, uniformly, across the whole file.
The practical upshot: a bloated CLAUDE.md is worse than a short one. Every instruction you add dilutes every other instruction. HumanLayer keeps their own file under 60 lines. Anthropic's own documentation suggests keeping it "short and human-readable." The rule of thumb used by production teams is brutal: for every line, ask "would removing this cause Claude to make a mistake?" If the answer is no, cut it.
What to actually put in CLAUDE.md
With the instruction budget in mind, here's what earns its place in a lean, effective CLAUDE.md:
Project identity — one sentence
Your exact run commands
npm run dev, npm test, npm run typecheck. Claude will use these verbatim when you ask it to verify its work. Ambiguous descriptions ("run the tests") produce inconsistent results.Hard rules — the non-negotiables
Architecture landmarks
Project-specific gotchas
Skill file pointers — not the content itself
@ import syntax rather than copying their content in. This keeps your main file short while making the knowledge available on demand.What to leave out
Most of what people put in CLAUDE.md shouldn't be there. Here's what to cut:
Use a linter. ESLint and Prettier enforce style deterministically, every time, for free — without eating into your instruction budget. LLMs are comparably slow and expensive for work a $0 formatter handles instantly. Putting "use single quotes" in CLAUDE.md is a waste of three instruction slots.
They go stale fast, and they eat context. Instead, use file:line references to point Claude at the authoritative version in your codebase. "See src/lib/auth.ts:45 for the session pattern" is more reliable than a copied snippet that may no longer match the real code.
If Claude only needs to know about your Stripe integration when you're working on payments, don't load that knowledge every session. That's exactly what skill files are for. CLAUDE.md is for universal truths about the project, not specialist knowledge.
Personal preferences (your editor setup, your preferred branch naming) belong in your global ~/.claude/CLAUDE.md, not the project file that gets checked into git.
TypeScript types, test passes, lint cleanliness — these are all checkable programmatically. Use hooks (see below) to enforce them automatically rather than instructing Claude to remember to check.
The ideal structure
There's no required format, but a consistent structure makes the file easier to maintain and easier for Claude to parse. Here's what production teams converge on:
# CLAUDE.md
## Project
Next.js 15 e-commerce app. TypeScript strict mode. Supabase auth and DB. Stripe payments. Deployed on Vercel.
## Commands
- `npm run dev` — start dev server (port 3000)
- `npm test` — run Jest unit tests
- `npm run test:e2e` — run Playwright end-to-end tests
- `npm run typecheck` — TypeScript strict check
- `npm run lint` — ESLint
- `npm run db:migrate` — run Drizzle migrations
## Architecture
- `/app` — Next.js App Router pages and layouts
- `/components/ui` — reusable UI components (shadcn/ui)
- `/lib` — shared utilities and server actions
- `/app/api` — API routes
- `/drizzle` — schema and migrations
## Hard rules
- NEVER commit .env or .env.local files
- NEVER use raw SQL — always use Drizzle ORM
- The Stripe webhook handler (/app/api/webhooks/stripe/route.ts) MUST validate signatures
- Use named exports, not default exports
- All server actions must validate session before any DB operation
## Gotchas
- Product images live in Cloudinary — never store locally
- The `useCart` hook is client-only — never import in server components
- Emails go through Resend, not SMTP — see @docs/email-setup.md
## Skills
- Stripe flows: @.claude/skills/stripe/SKILL.md
- Deployment: @.claude/skills/deploy/SKILL.mdNotice what isn't there: no code style rules (that's ESLint's job), no code snippets, no lengthy explanations of how Stripe works. The whole file is under 40 lines.
Skill files: what they are and when to use them
Skill files are the natural companion to CLAUDE.md. Where CLAUDE.md loads universally at the start of every session, skill files load on demand — either when you invoke them explicitly with a slash command, or when Claude detects they're relevant to the current task.
You create a skill by adding a directory to .claude/skills/ with a SKILL.md inside it:
.claude/
skills/
stripe/
SKILL.md ← loads when Claude works on payments
deploy/
SKILL.md ← loads when Claude works on deployment
database/
SKILL.md ← loads when Claude works on migrationsEach skill file uses YAML frontmatter to describe when it applies:
---
name: Stripe Payments
description: Payment processing, webhooks, and subscription management
user-invocable: true
allowed-tools: Read, Write, Bash
---
# Stripe Payments Skill
## Overview
This project uses Stripe for one-time payments and subscriptions.
The webhook handler is at /app/api/webhooks/stripe/route.ts.
## Key patterns
- Always use `stripe.webhooks.constructEvent()` to validate webhook signatures
- Test with Stripe CLI: `stripe listen --forward-to localhost:3000/api/webhooks/stripe`
- Use `STRIPE_SECRET_KEY` for server-side calls, `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` for client
- Price IDs live in /lib/stripe-prices.ts — never hardcode them
## Test cards
- Success: 4242 4242 4242 4242
- Decline: 4000 0000 0000 0002
## Common tasks
### Create a checkout session
See /lib/stripe.ts:createCheckoutSession for the canonical implementation.The critical insight: skill files let you give Claude deep domain expertise precisely when it needs it, without that expertise polluting every other session. Your Stripe knowledge doesn't need to be in Claude's context when you're working on UI components.
Skill file best practices
Write for the task, not the technology
Don't write a skill that explains how Stripe works in general. Write a skill that explains how your specific project uses Stripe. The distinction matters — Claude already knows how Stripe works. It doesn't know that your price IDs live in /lib/stripe-prices.ts or that your webhook endpoint expects raw body parsing.
Use file:line references, not code copies
Instead of pasting the canonical implementation into the skill file, point to it: "See /lib/auth.ts:createSession for the session creation pattern." The code in the file is always current. A pasted copy in a skill file will drift.
Inject dynamic context with shell commands
Skill files support inline shell commands using !`command` syntax. Claude runs the command on invocation and sees only the result — not the command itself. This is useful for injecting the current state of something: the current git branch, the list of active feature flags, the output of a health check.
---
name: Database Status
description: Check current migration state before any DB work
---
# Database Skill
## Current migration state
!`npm run db:status`
## Rules
- Always check migration state before generating new migrations
- Use `npm run db:migrate` to apply, never run SQL directlyKeep the description accurate — Claude uses it for auto-discovery
The description field in your YAML frontmatter is how Claude decides whether to load a skill automatically. If the description doesn't match the kind of tasks you want it to trigger on, Claude won't load it when you need it. Be specific and task-oriented: "Stripe payment processing, webhooks, and subscription management" not "payments."
Use PreToolUse hooks to measure which skills are actually firing
You can add a hook that logs whenever a skill is invoked, which lets you identify skills that are undertriggering (Claude isn't loading them when it should) or over-triggering (loading all the time when they're not relevant). Without this visibility, you're guessing.
When to use hooks instead of instructions
CLAUDE.md instructions are advisory — Claude follows them roughly 80% of the time. If something must happen every single time with zero exceptions, use a hook instead. Hooks are deterministic.
A common hook pattern: run Prettier automatically on every file Claude edits. Add this to.claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write $CLAUDE_TOOL_INPUT_PATH"
}
]
}
]
}
}Now Claude generates well-formatted code, and the hook handles the last 10% to keep your CI happy — without any CLAUDE.md instruction reminding Claude to "format code properly."
Maintaining both over time
Both CLAUDE.md and skill files compound in value over time — but only if you maintain them like code, not documentation.
Treat CLAUDE.md like a codebase. Check it into git. Review it when sessions go wrong. When Claude does something unexpected, ask: is this a CLAUDE.md problem (missing rule, unclear gotcha) or an instruction budget problem (file too long, rule getting lost)? Prune regularly. The file should shrink over time as you offload domain knowledge to skill files and enforce style with hooks.
Watch for the "still ignoring" signal. If Claude keeps doing something you've explicitly prohibited, the file is either too long or the rule is too vaguely worded. Try adding emphasis — "IMPORTANT:" or "YOU MUST" — before genuinely critical rules. If that doesn't work, the file is probably too bloated overall and the rule is getting drowned out.
Skill files drift — review them on major dependency upgrades. A Stripe skill written for Stripe SDK v12 will give Claude incorrect patterns after you upgrade to v14. Skill files that reference specific APIs need to be updated when those APIs change. File:line references help — they point to the live code rather than a copy — but the overall structure of a skill still needs human review.
A full worked example for a Next.js SaaS project
Here's what the complete CLAUDE.md + skill file structure looks like for a production Next.js app:
# Root CLAUDE.md (~35 lines)
## Project
Next.js 15 SaaS app. TypeScript strict. Supabase. Stripe subscriptions. Resend email. Deployed on Vercel.
## Commands
- `npm run dev` — dev server (localhost:3000)
- `npm test` — Jest (unit)
- `npm run e2e` — Playwright
- `npm run typecheck` — strict TS check
- `npm run db:migrate` — apply Drizzle migrations
## Architecture
- `/app` — App Router pages and layouts
- `/components/ui` — shadcn/ui components (never modify directly)
- `/lib` — server utilities, actions, helpers
- `/app/api` — API routes and webhook handlers
- `/drizzle` — schema, migrations
## Hard rules
- NEVER commit .env or .env.local
- NEVER use raw SQL — Drizzle ORM only
- NEVER import server-only modules in client components
- Stripe webhook: always validate signature (see @.claude/skills/stripe/SKILL.md)
- Named exports only — no default exports
## Gotchas
- File uploads go to Supabase Storage, not /public
- useAuth() is client-only — never call in server components
- Email uses Resend SDK, not nodemailer.claude/skills/
stripe/SKILL.md ← payment flows, webhook patterns, test cards
database/SKILL.md ← migration workflow, schema conventions
deploy/SKILL.md ← Vercel deploy steps, env var checklist
email/SKILL.md ← Resend templates, sending patternsThe result: a CLAUDE.md that loads fast, stays within the instruction budget, gives Claude everything it needs to work confidently in the codebase — and offloads specialist knowledge to skills that only load when relevant.
Frequently asked questions
How long should a CLAUDE.md file be?
As short as possible. Production teams and Anthropic's own documentation recommend keeping it under 200 lines, with the most effective files coming in well under 100. HumanLayer keeps theirs under 60 lines. Every line competes with every other line for the model's attention — shorter files produce more reliable instruction-following.
What is the difference between CLAUDE.md and a skill file?
CLAUDE.md loads at the start of every session and should contain universal project truths — run commands, hard rules, architecture landmarks. Skill files load on demand when relevant to a specific task. Domain knowledge that Claude only needs sometimes (Stripe integration details, deployment procedures, database migration patterns) belongs in skill files, not CLAUDE.md.
Why does Claude keep ignoring my CLAUDE.md rules?
Usually one of two things: the file is too long and the rule is being crowded out by the attention mechanism, or the rule is phrased too vaguely. Try shortening the file significantly and adding "IMPORTANT:" or "YOU MUST" before genuinely critical rules. If the rule must be followed without exception, consider making it a hook instead of an instruction.
Should I put code examples in CLAUDE.md?
No. Code examples go stale quickly and eat instruction budget. Instead, use file:line references to point Claude at the authoritative implementation in your codebase. "See src/lib/auth.ts:45 for the session pattern" is more reliable than a pasted snippet that may no longer match the real code.
Can I have multiple CLAUDE.md files in one project?
Yes. Claude supports CLAUDE.md files at the project root, in subdirectories, and in your home folder. The project root file is checked into git and shared with your team. Subdirectory files load on demand when Claude works in those directories — useful for monorepos where each package has its own conventions.
What is the @import syntax in CLAUDE.md?
CLAUDE.md supports importing other files using @path/to/file syntax. This lets you keep the main file short while linking to deeper references. For example: "Git workflow: @docs/git-instructions.md" — Claude will read the linked file when it needs that context.
Valid8it
Get a Claude Code starter pack with your validation
Valid8it generates a project-specific CLAUDE.md, scaffold, and skill files alongside your Reddit validation report. Free to start.