Notes on CLAUDE.md Structure and Best Practices

Posted on Jul 4, 2025

I’ve been diving deep into Claude Code and wanted to share some notes on CLAUDE.md structure and best practices. Here’s what I’ve learned from various sources and experiments.

File Imports and Structure

One of the most powerful features is the ability to import additional files using @path/to/import syntax:

See @README for project overview and @package.json for available npm commands for this project.

# Additional Instructions

- git workflow @docs/git-instructions.md

You can also import files from your home directory, which is convenient for team members to provide individual instructions that aren’t checked into the repository:

# Individual Preferences

- @~/.claude/my-project-instructions.md

Keep in mind that imports are not evaluated inside markdown code spans and code blocks - so this won’t work: @anthropic-ai/claude-code.

Imported files can recursively import additional files, with a max depth of 5 hops. You can see what memory files are loaded by running the /memory command.

Configuration Options

For different project setups, you have several options:

  • Use CLAUDE.local.md (and add it to .gitignore) to have your own flavor of CLAUDE.md
  • For monorepos, you might run Claude from root/foo, and have CLAUDE.md files in both root/CLAUDE.md and root/foo/CLAUDE.md - both will be pulled into context automatically
  • ~/.claude/CLAUDE.md applies to ALL of your Claude sessions

Resources and Examples

Here are some great resources I’ve found:

Simon Willison had some insightful observations about Armin’s approach: “The thing I find most interesting about Armin’s new sloppy-xml-py open source package, written almost entirely using Claude and Claude Code, is that the code is good.” He emphasized that “this is good code because Armin is an expert developer and stayed in full control the whole time” and clarified that “This example is not an argument for replacing programmers with LLMs. The code is good because Armin is an expert programmer who stayed in full control throughout the process.” (Simon’s post)

Advanced Techniques

Anchor Comments

Using easily greppable anchor comments in your codebase and referring to them in guidelines is a smart pattern (via diwank.space):

Anchor comments example

Setting Boundaries

For large codebases, set clear boundaries both in CLAUDE.md and locally in your code (via diwank.space):

Setting boundaries example

Things Not to Do

A good “things not to do” section can be incredibly valuable (via diwank.space):

Things not to do example

Local Context with Anchor Comments

As your codebase grows, CLAUDE.md alone isn’t enough. You need anchor comments that serve as local context to prevent AI from making locally bad decisions (via diwank.space):

Local context example

Team Practices

Test File Policy

Some teams have strict policies about AI touching tests. The folks from Julep have a hard rule: “If an AI tool touches a test file, the PR gets rejected. No exceptions.” (via diwank.space)

Test policy example

Context Investment

An important insight: being stingy with context to save tokens actually costs you more. Front-load context to avoid iteration cycles. Think of tokens like investing in good tools - the upfront cost pays for itself many times over (via diwank.space).

Context investment example

Protected Areas

Common things that Claude should NOT touch (via diwank.space):

  • Test files
  • DB migrations
  • Security critical code
  • API contracts without versioning
  • Configuration and secrets

Protected areas examples

Advanced Configuration

MCP Integration

You can check in a .mcp.json file to list MCP servers available to Claude Code. When working with MCP, launch Claude with the --mcp-debug flag to help identify configuration issues. Here’s an example in the wild.

URL Permissions

You can paste specific URLs alongside your prompts for Claude to fetch and read. To avoid permission prompts for the same domains (e.g., docs.foo.com), use /permissions to add domains to your allowlist.

Hooks

Claude Code has hooks that let you run shell commands deterministically at different stages of the lifecycle. This can remove the need for extra explanation in claude.md. Check out the hooks documentation.

Tool Management

Review and curate tools that are available to Claude using the --allowedTools flag or allowed_tools param in GitHub Actions.

Bespoke Scripts

An interesting pattern is instructing Claude how to write “throw away” bespoke scripts - where to put them and how to run them. These become tools that Claude can then use to accomplish tasks, reducing reliance on static MCPs (from @mitsuhiko.at).

Continuous Improvement

You should occasionally run your CLAUDE.md files through the prompt improver to improve adherence.

One interesting case study combines a well-maintained CLAUDE.md with SPEC.md and targeted prompts for production use.

Style Preferences

I’m not a huge fan of overly detailed style sections like:

### Code Style  
- Formatting: Prettier with 100-char lines  
- Imports: sorted with simple-import-sort  
- Components: Pascal case, co-located with their tests  

Most of this stuff can be handled by deterministic formatting tools chained with Claude Code invocations or through hooks.

These practices have significantly improved my Claude Code experience. The key is finding the right balance between guidance and flexibility, while leveraging the various configuration options available.

This post was generated using bsky2md and was written by Claude and Philip