Part 22 of 22

CLAUDE.md vs. skills: where should Claude Code instructions live?

LLM Mart · Sep 23, 2026 · 1 views 83 listing impressions
CLAUDE.md vs. skills: where should Claude Code instructions live?

Put it in CLAUDE.md if Claude should know it in every session: build commands, conventions, architecture, "always do X" rules. Put it in a skill if it is reference material Claude needs occasionally, or a procedure you want to trigger by name. Put it in a path-scoped rule if it only applies to part of the codebase.

The distinction is loading behaviour, and loading behaviour is a cost. CLAUDE.md is in context for every request whether it is relevant or not. A skill costs a description until the moment it is used.

The three mechanisms

CLAUDE.md .claude/rules/ Skill
Loads Every session Every session, or when matching files are opened On demand, when invoked or judged relevant
Scope Whole project Can be scoped to file paths Task-specific
Context cost Full content, every request Full content when loaded Description every request; body only when used
Can be triggered by name No No Yes, with /<name>
Best for Core conventions and commands Language- or directory-specific guidance Reference material and repeatable workflows

All three are context rather than enforced configuration. Claude treats them as instructions to follow, not rules it cannot break. If something must hold every time — never edit .env, never push to main — a PreToolUse hook is enforcement and a written instruction is a request.

What belongs in CLAUDE.md

Treat CLAUDE.md as the place you write down what you would otherwise re-explain. The documented triggers for adding to it are specific:

  • Claude makes the same mistake a second time.
  • A code review catches something Claude should have known about this codebase.
  • You type the same correction you typed last session.
  • A new teammate would need the same context to be productive.

That last one is the best test. If a new hire would need it on day one regardless of what they were working on, it is a CLAUDE.md fact.

Files can live at several scopes, loaded from broadest to most specific so a project instruction appears after a user instruction:

Scope Location Shared with
Managed policy /Library/Application Support/ClaudeCode/CLAUDE.md (macOS), /etc/claude-code/CLAUDE.md (Linux/WSL), C:\Program Files\ClaudeCode\CLAUDE.md (Windows) All users in the organisation
User ~/.claude/CLAUDE.md Just you, all projects
Project ./CLAUDE.md or ./.claude/CLAUDE.md The team, via source control
Local ./CLAUDE.local.md Just you, this project — gitignore it

Files in the directory hierarchy above the working directory load at launch; files in subdirectories load on demand when Claude reads files there. All discovered files are concatenated rather than overriding each other, ordered from the filesystem root down, so instructions closer to where you launched Claude are read last.

Run /context in a session and check Memory files to confirm what actually loaded. That is a more reliable answer than reasoning about the resolution rules.

The 200-line rule, and what it is really about

Target under 200 lines per CLAUDE.md file. Longer files consume more context and reduce adherence — the second effect matters more than the first. A 600-line instruction file does not make Claude follow 600 lines of instruction; it dilutes the ones that mattered.

Four writing habits move the needle:

Be specific enough to verify. "Use 2-space indentation" instead of "format code properly." "Run npm test before committing" instead of "test your changes." "API handlers live in src/api/handlers/" instead of "keep files organised."

Structure it. Markdown headers and bullets. Claude scans structure the way readers do.

Remove contradictions. If two rules conflict, Claude may pick one arbitrarily. Review nested CLAUDE.md files and .claude/rules/ periodically for rules that outlived their reason.

Leave notes for humans in HTML comments. Block-level <!-- ... --> comments are stripped before the content enters context, so maintainer notes cost nothing. Comments inside code blocks are preserved.

If a checked-in CLAUDE.md has already grown past the point of usefulness, /doctor produces trim proposals rather than leaving you to guess what to cut.

Move it to a rule when it only applies to part of the repo

.claude/rules/ holds topic files — testing.md, api-design.md, security.md — discovered recursively, so you can organise them into frontend/ and backend/ subdirectories.

A rule without paths frontmatter loads at launch with the same priority as .claude/CLAUDE.md. A rule with paths loads only when Claude works with matching files:

---
paths:
  - "src/api/**/*.ts"
---

# API development rules

- All API endpoints must include input validation
- Use the standard error response format
- Include OpenAPI documentation comments

That frontmatter is the whole point: your API conventions stop consuming context while Claude works on the front end. Path-scoped rules trigger when Claude reads a matching file, not on every tool use.

Brace expansion works — src/**/*.{ts,tsx} — but each brace group multiplies the pattern count, and a rule's whole paths list shares a budget of 1,000 expanded patterns. A pattern that would exceed it is used unexpanded, where its literal braces match nothing.

Personal rules in ~/.claude/rules/ apply to every project on your machine and load before project rules, giving project rules higher priority.

Move it to a skill when it is a procedure or a reference

A skill is a directory with a SKILL.md: YAML frontmatter that tells Claude when to use it, and markdown content Claude follows when it runs. The directory name becomes the command you type.

---
description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
---

## Current changes

!`git diff HEAD`

## Instructions

Summarize the changes above in two or three bullet points, then list any risks you notice.

The documented trigger is direct: create a skill when you keep pasting the same instructions, checklist, or multi-step procedure into chat, or when a section of CLAUDE.md has grown into a procedure rather than a fact.

That is the dividing line worth internalising. "We deploy with Helm" is a fact. The eleven steps of your deployment are a procedure.

Four properties make skills the right home for that content:

The body costs nothing until used. Only the description loads at session start. Long reference material — an API style guide, a schema, a runbook — is nearly free until the moment it is needed.

They can be invoked by name. /deploy is deterministic in a way "Claude should remember to deploy correctly" is not.

The description is the trigger. Claude matches your task against skill descriptions to decide what is relevant. Put the key use case first: description plus when_to_use is truncated at 1,536 characters in the skill listing.

They can be scoped and gated. paths frontmatter limits automatic activation to matching files. disable-model-invocation: true hides a skill from Claude entirely until you invoke it — worth using for anything with side effects, since it also drops the description's context cost to zero.

For authoring guidance, see how to create an AI agent skill; for what skills are in general, what are AI agent skills.

Imports are not a way around the size limit

CLAUDE.md supports @path/to/import syntax, with relative or absolute paths, recursing up to four hops. Import parsing skips code spans and fenced blocks, so `@README` in backticks stays literal.

But imported files are expanded and loaded into context at launch. Splitting a 500-line CLAUDE.md into five imported files organises the source and changes the context cost not at all. Use imports for maintainability; use path-scoped rules or skills when the goal is to load less.

One import pattern is genuinely useful: @~/.claude/my-project-instructions.md shares personal instructions across git worktrees, where a gitignored CLAUDE.local.md would only exist in the worktree you created it in.

Note the trust boundary. An import in a project memory file whose path resolves outside the working directory is external, and Claude Code shows an approval dialog the first time it encounters one. Declining disables those imports permanently for that project. The dialog exists to protect you from files other people commit to a shared repository.

If your repo already has an AGENTS.md for other coding agents, Claude Code does not read it directly — create a CLAUDE.md that imports it with @AGENTS.md, and add Claude-specific instructions below.

A worked refactor

A 400-line CLAUDE.md, split:

Original section Goes to Why
Build and test commands CLAUDE.md Needed in every session
Directory layout CLAUDE.md Orientation, always relevant
"Use pnpm, not npm" CLAUDE.md Short, unconditional
60 lines of React component conventions .claude/rules/frontend.md with paths: ["src/components/**/*.tsx"] Only matters in the front end
80 lines of SQL migration policy .claude/rules/migrations.md with paths: ["db/migrations/**/*"] Only matters during a migration
The 11-step release checklist .claude/skills/release/SKILL.md A procedure you trigger, not a fact
The full API style guide .claude/skills/api-style/SKILL.md Reference material, needed sometimes
"Never commit to main" A PreToolUse hook Must hold every time

What used to load 400 lines into every request now loads perhaps 60, with the rest arriving when it is relevant.

After a refactor like this, delete the originals. Duplicated instructions in two places is the failure mode that follows, and the version that gets updated is rarely the version that gets loaded.

Troubleshooting

An instruction is being ignored. Check /context to confirm the file loaded. Then look for a conflicting rule elsewhere in the hierarchy, and check whether the instruction is specific enough to act on.

A skill never triggers. Its description probably does not match how you phrase the request. Put the concrete use case first, and add trigger phrases via when_to_use. Or invoke it by name and stop relying on inference.

A skill triggers too often. Narrow the description, add paths, or set disable-model-invocation: true and invoke it yourself.

Someone else's CLAUDE.md is in your context. In a monorepo, claudeMdExcludes skips ancestor files by path or glob. Put it in .claude/settings.local.json so the exclusion stays on your machine.

Next step: Browse Claude skills on LLM Mart and move one repeated procedure out of your persistent project instructions — the release checklist is usually the one that pays for itself first.

Sources

0 0 0 0 Sign in to react

Comments (0)

Sign in to join the conversation.

No comments yet.