How to Write a CLAUDE.md File for Consistent Claude Code Output

claude code claude.md ai coding developer tools configuration

CLAUDE.md is a markdown file you place in your project root (or ~/.claude/CLAUDE.md globally) that Claude Code reads automatically on every invocation. It acts as a persistent system prompt scoped to your repo. The key to deterministic output is being ruthlessly specific about conventions, forbidden patterns, and expected output formats. Vague instructions produce vague results.

The Foundation: a Production CLAUDE.md File

Start with this structure. Every section exists because Claude Code responds measurably better to explicit constraints than to general guidance. The file should read like a strict style guide, not a wishlist.

# CLAUDE.md

## Project Overview
# TypeScript monorepo. Node 20. pnpm workspaces.
# Backend: Express + Prisma + PostgreSQL.
# Frontend: Next.js 14 App Router.
# Tests: Vitest for unit, Playwright for e2e.

## Hard Rules
# NEVER use `any` type. Use `unknown` then narrow.
# NEVER use default exports. Always use named exports.
# NEVER add dependencies without asking first.
# NEVER modify files in /generated or /prisma/migrations.
# ALWAYS run `pnpm typecheck` before declaring a task done.

Why NEVER and ALWAYS Work Better Than Polite Suggestions

Claude Code treats hedged language like "prefer named exports" or "try to avoid any" as soft preferences it can override when it deems appropriate. The words NEVER, ALWAYS, MUST, and DO NOT in all caps produce significantly more consistent adherence. This isn't a stylistic choice. It directly impacts how reliably the model follows your rules across long sessions where context pressure builds.

Put your hardest constraints at the top of the file. Claude Code weights earlier content more heavily when the context window fills up and older content gets summarized.

## Code Style Constraints
# Functions must not exceed 30 lines. Extract helpers.
# Use early returns. No nested if/else beyond 2 levels.
# Error handling: throw typed errors from /lib/errors.ts.
# Imports order: node builtins, external deps, internal (@/*).
# File naming: kebab-case for files, PascalCase for components.

## Git Conventions
# Commit message format: (): 
# Types: feat, fix, refactor, test, docs, chore.
# NEVER amend or force-push to main or develop.
# Create a branch for every change.

Teaching Claude Code Your Codebase Patterns

The most impactful section of any CLAUDE.md shows Claude Code how your project actually works. Don't write abstract rules. Write concrete commands and file paths. Claude Code uses these to run validation, find existing patterns, and avoid hallucinating APIs that don't exist in your codebase.

## Common Commands
# Type check: pnpm typecheck
# Lint: pnpm lint --fix
# Test single file: pnpm vitest run src/path/to/file.test.ts
# Test all: pnpm test
# DB migrate: pnpm prisma migrate dev
# Generate client: pnpm prisma generate

## Key Files to Reference Before Writing Code
# Database schema: /prisma/schema.prisma
# API route patterns: /apps/api/src/routes/users.ts
# Shared types: /packages/types/src/index.ts
# Error classes: /packages/shared/src/errors.ts
# Auth middleware: /apps/api/src/middleware/auth.ts

Scope: Project vs User vs Directory-Level Config

Claude Code supports three levels of CLAUDE.md that stack on top of each other. Understanding the hierarchy prevents conflicts and keeps config DRY.

~/.claude/CLAUDE.md stores global preferences that apply to every project. Put your universal style preferences, language defaults, and personal conventions here. This is your "I always want TypeScript strict mode" file.

/project-root/CLAUDE.md holds project-specific rules. This is the primary file your team commits to version control. It should contain everything that a new contributor (human or AI) needs to produce consistent code in this repo.

/project-root/src/subsystem/CLAUDE.md provides directory-scoped overrides. Use these sparingly for genuinely different subsystems, such as a Python ML pipeline living inside a TypeScript monorepo. Claude Code picks them up when working in that directory.

# ~/.claude/CLAUDE.md (global user config)

## My Defaults
# I use TypeScript with strict: true for all projects.
# I prefer functional patterns over class hierarchies.
# When writing tests, always include edge cases for:
#   empty inputs, null/undefined, boundary values.
# NEVER suggest console.log for debugging. Use proper loggers.
# When I say "refactor" I mean improve structure without
#   changing behavior. Run tests before and after.

The Gotcha: Context Window Pressure

CLAUDE.md consumes tokens from your context window on every interaction. A 2,000-word CLAUDE.md is roughly 2,500 tokens eaten before you type your first prompt. This tradeoff is almost always worth it for consistency, but there's a practical ceiling. If your CLAUDE.md exceeds roughly 4,000 words, Claude Code might start dropping instructions from the middle of the file during long sessions.

Keep it under 1,500 words. If you need more, use directory-scoped files to distribute domain-specific rules to where they're relevant. Don't repeat project-level rules in subdirectory files because Claude Code composes them automatically.

Another common pitfall: including example code in CLAUDE.md that's too long. Claude Code sometimes reproduces these examples verbatim instead of adapting them. Keep examples to three to five lines maximum, showing the pattern, not the implementation.

## API Route Pattern (follow this structure exactly)
# export const createUser = asyncHandler(async (req, res) => {
#   const body = parseBody(req, CreateUserSchema);
#   const user = await userService.create(body);
#   res.status(201).json({ data: user });
# });

## Test Pattern (follow this structure exactly)
# describe("UserService.create", () => {
#   it("creates user with valid input", async () => {
#     const result = await userService.create(validInput);
#     expect(result.id).toBeDefined();
#   });
# });

Advanced: Controlling Claude Code Workflow With Rules

Beyond code style, you can shape how Claude Code approaches tasks. This is where deterministic behavior improves most. You make Claude Code verify its own work through commands rather than relying on its judgment alone.

## Workflow Rules
# Before writing any code, read the relevant existing files first.
# After modifying any .ts file, run: pnpm typecheck
# After modifying any test file, run that specific test.
# After modifying any API route, verify with: curl the endpoint.
# When creating new files, check for existing similar files first
#   to maintain consistency.
# DO NOT batch more than 3 file changes without running checks.

## When Stuck
# If a type error persists after 2 attempts, stop and show me
#   the full error. DO NOT add type assertions to work around it.
# If a test fails, show the diff between expected and actual
#   before proposing a fix.

CLAUDE.md vs Cursorrules vs Copilot Instructions

These files serve the same purpose for different tools. If your team uses multiple AI coding tools, you can maintain a single source of truth and symlink or copy. However, there are format differences worth knowing. .cursorrules tends to work better with terse, imperative sentences. CLAUDE.md handles longer explanatory context well because Claude's context window is large and it can reason over paragraphs. Copilot instructions work best as flat lists.

If you only use Claude Code, commit CLAUDE.md to your repo root and add it to your PR template checklist: "Did you update CLAUDE.md if you changed conventions?" Treat it like a living document, not a one-time setup. Every time Claude Code produces output that surprises you, add a rule to prevent it.

# .claude/settings.json (project-level permissions)
# Place in project root to control tool access.
{
  "permissions": {
    "allow": [
      "Bash(pnpm:*)",
      "Bash(npx vitest:*)",
      "Bash(git status)",
      "Bash(git diff:*)",
      "Bash(git log:*)"
    ],
    "deny": [
      "Bash(rm -rf:*)",
      "Bash(git push:*)",
      "Bash(pnpm add:*)"
    ]
  }
}

Key Takeaways for CLAUDE.md Best Practices

Put your most critical rules first because context window summarization drops middle content first. Use absolute language (NEVER, ALWAYS, MUST) instead of soft preferences. Include real commands that Claude Code can run to validate its own work. Keep the file under 1,500 words and use directory-scoped files for subsystem rules. Show tiny code patterns (three to five lines), not full implementations. Pair CLAUDE.md with .claude/settings.json to enforce permissions at the tool level, not the prompt level. Most importantly, iterate on it. Every surprising output from Claude Code is a missing rule in your CLAUDE.md.

← Back to all articles