In my previous posts, I covered setting up Claude Code with Amazon Bedrock and getting it running in your IDE. Now you have Claude Code working with Bedrock in your terminal or IDE. That's the infrastructure piece. Your code stays in your AWS environment with IAM governance.
But there's another layer that makes Claude Code dramatically more effective: teaching it how YOU work. Your coding style, your team's conventions, your project architecture, the commands you run, the gotchas you've learned the hard way.
That's where CLAUDE.md comes in. Think of it as Claude's memory for your project. Without it, Claude has to guess. With it, Claude knows.
The Problem: Claude Doesn't Know Your Conventions
Out of the box, Claude Code is smart. It understands code, can follow patterns, and makes reasonable assumptions. But it doesn't know:
- That your team uses
pnpminstead ofnpm - That you always run
npm run typecheckbefore committing - That the authentication system has a non-obvious quirk with session refresh
- That you prefer verbose variable names
- That API endpoints must be versioned in the URL path
- That your Lambda functions use
arm64with a custom runtime - That you follow Conventional Commits format
Without this context, you end up correcting Claude repeatedly. "No, use pnpm." "Run the type checker." "That won't work because of the session refresh issue." Each correction costs context window space and adds friction.
CLAUDE.md solves this. Claude reads it at the start of every session. The instructions persist. You write them once, and Claude remembers.
How CLAUDE.md Files Work: Three Levels
Claude Code supports a three-tier hierarchy:
| Location | Scope | Shared? | Use Case |
|---|---|---|---|
~/.claude/CLAUDE.md |
All your projects | No | Personal preferences across everything |
./CLAUDE.md or ./.claude/CLAUDE.md
|
Single project | Yes (via git) | Team conventions for this project |
./CLAUDE.local.md |
Single project | No (gitignored) | Personal project-specific overrides |
All three files load together. They're additive. More specific instructions take precedence over broader ones.
When to Use Each
Global (~/.claude/CLAUDE.md): Your personal style that applies everywhere
- Communication preferences ("Be concise", "Show diffs before changes")
- Your preferred tools ("Use vim when possible")
- Your coding style across all languages
- How you like commit messages formatted
Project (./CLAUDE.md): Team-shared conventions for this codebase
- Build commands and test runners
- Architecture decisions
- Code style for this project
- API design patterns
- Git workflow
Project-Local (./CLAUDE.local.md): Your personal tweaks for this project
- "Skip the long explanations, I know this codebase well"
- Personal shortcuts or commands
- Anything you don't want in source control
Quick Start: Generate Your First CLAUDE.md
The easiest way to get started:
cd your-project
claude
Then run:
/init
Claude analyzes your codebase and generates a starter CLAUDE.md with detected build systems, test frameworks, and code patterns. You can then refine it.
Practical Examples: What to Put In Your Files
Here are copy-pasteable templates based on real-world usage patterns and official best practices.
Example 1: Personal Global CLAUDE.md
Location: ~/.claude/CLAUDE.md
# My Personal Preferences
## Communication Style
- Be concise in explanations unless I ask for details
- Always show me the diff before making changes
- Ask clarifying questions if requirements are ambiguous
## Code Style Across All Projects
- Use descriptive variable names (prefer `userAuthToken` over `token`)
- 2-space indentation for JS/TS, 4-space for Python
- Prefer functional programming patterns over imperative
- Always include error handling, never silently fail
## Language Preferences
- Prefer TypeScript over JavaScript for all new projects
- Use strict mode in TypeScript (`"strict": true`)
- Avoid `any` types, use `unknown` when type is truly unknown
## AWS Architecture Preferences
- Prefer serverless over instance-based solutions (Lambda > EC2)
- Use managed services when possible (RDS/Aurora over self-managed databases)
- Default to event-driven architectures with EventBridge
- Infrastructure as Code: Use AWS SAM, not CDK or raw CloudFormation
- Always use least-privilege IAM policies, never use wildcards
## Git Conventions
- Follow Conventional Commits format
- Types: feat, fix, docs, refactor, test, chore
- Format: `type(scope): description`
- Example: `feat(auth): add OAuth2 support`
- Commit messages: single sentence summary, then detailed explanation
- Word wrap commit messages at 72 columns
## Testing Standards
- Minimum 80% coverage for business logic
- Use descriptive test names (it should...)
- Arrange-Act-Assert pattern
- Mock external dependencies
## Security Essentials
- Never commit secrets (use environment variables)
- Validate all user inputs
- Use parameterized queries, never string concatenation
- Always sanitize data before rendering
This is mine. It applies to every project I work on.
Example 2: Simple Project CLAUDE.md
Location: ./CLAUDE.md (project root)
# Project Conventions
## Build Commands
- Dev server: `npm run dev`
- Build: `npm run build`
- Tests: `npm test`
- Type check: `npm run typecheck`
- Lint: `npm run lint`
## Workflow
- Always run `npm run typecheck` after making code changes
- Run the full test suite before committing
- Never push directly to main
## Code Style
- Use ES modules (import/export), not CommonJS (require)
- Destructure imports when possible: `import { foo } from 'bar'`
- Prefer functional components with hooks
## Testing
- Use Jest for unit tests
- Run single tests for performance: `npm test -- path/to/test`
- Avoid mocks when testing business logic
Short, focused, actionable.
Example 3: More Comprehensive Project CLAUDE.md
Location: ./CLAUDE.md
See @README.md for project overview and @package.json for available commands.
# Build and Development
## Package Manager
- Use `pnpm`, NOT `npm` or `yarn`
- Install dependencies: `pnpm install`
- Add package: `pnpm add <package>`
## Available Commands
- Dev server: `pnpm dev` (runs on http://localhost:3000)
- Build: `pnpm build`
- Tests: `pnpm test`
- Type check: `pnpm typecheck`
- Lint: `pnpm lint`
- Format: `pnpm format`
## Workflow Rules
1. Run `pnpm typecheck` after making changes
2. Run `pnpm test` before committing
3. Ensure `pnpm lint` passes
4. Never commit without tests for new features
# Code Conventions
## Architecture
- This is a React app with TypeScript
- State management: React Query for server state, Zustand for client state
- Routing: React Router v6
- API calls: Always use the useApi hook
## File Organization
- Components: `src/components/`
- Pages: `src/pages/`
- Hooks: `src/hooks/`
- Utils: `src/utils/`
- Types: `src/types/`
## Naming Conventions
- Components: PascalCase (`UserProfile.tsx`)
- Hooks: camelCase with `use` prefix (`useAuth.ts`)
- Utils: camelCase (`formatDate.ts`)
- Constants: SCREAMING_SNAKE_CASE
## Component Guidelines
- Use functional components with hooks
- Keep components under 200 lines
- Extract complex logic to custom hooks
- Use TypeScript for all prop types
# API Conventions
## REST API Design
- Base URL: `https://api.example.com/v1`
- All endpoints must use authentication
- Use kebab-case for URL paths: `/user-profiles`
- Use camelCase for JSON properties: `{"firstName": "John"}`
- Always include pagination for list endpoints
- Error responses follow the format in `docs/api-errors.md`
# Git Workflow
## Branch Naming
- Feature branches: `feature/add-user-profile`
- Bug fixes: `fix/login-token-refresh`
- Never work directly on `main`
## Commit Messages
- Follow Conventional Commits
- Format: `type(scope): description`
- Example: `feat(auth): implement OAuth2 flow`
## Pull Requests
- Always create a PR for review
- Link to the issue number
- Include testing notes
- Run the full test suite before requesting review
# Known Gotchas
## Authentication
- The session refresh happens automatically but requires a specific header
- See `src/auth/session.ts` for implementation details
- Don't cache the auth token - it refreshes every 5 minutes
## Database
- Always use the query builder in `src/db/query.ts`
- Never construct raw SQL queries
- Database migrations live in `migrations/`
## Environment Variables
- Required: `API_URL`, `AUTH_TOKEN_SECRET`
- Optional: `DEBUG`, `LOG_LEVEL`
- See `.env.example` for complete list
This is more detailed but still focused. Notice the @README.md import at the top to avoid repeating project overview.
Example 4: Modular Rules with Path Matching
For larger projects, you can break rules into separate files in .claude/rules/:
File: .claude/rules/api-conventions.md
---
paths:
- "src/api/**/*.ts"
---
# API Development Rules
- All API endpoints must include input validation with Zod
- Use the standard error response format from `src/api/errors.ts`
- Include OpenAPI documentation comments above each handler
- Version APIs in the URL path (/v1/, /v2/)
- Always return proper HTTP status codes
File: .claude/rules/testing.md
---
paths:
- "**/*.test.ts"
- "**/*.spec.ts"
---
# Testing Conventions
- Use descriptive test names: `it('should throw error when user is not authenticated')`
- Follow Arrange-Act-Assert pattern
- Mock external API calls
- Test both success and error cases
- Minimum 80% coverage for new code
File: .claude/rules/react-components.md
---
paths:
- "src/**/*.{tsx,jsx}"
---
# React Component Guidelines
- Use functional components with hooks (no class components)
- Keep components under 200 lines
- Extract complex logic to custom hooks in `src/hooks/`
- Use TypeScript for all prop types (no PropTypes)
- Export components as named exports, not default
These load automatically when Claude works in those directories.
Example 5: Monorepo Structure
Root: /project/CLAUDE.md
# Monorepo Structure
This is a pnpm monorepo with multiple packages:
- `packages/frontend` - React application
- `packages/backend` - Node.js API
- `packages/shared` - Shared utilities and types
## Global Rules
- Use pnpm workspaces: `pnpm -r <command>` to run in all packages
- Run tests from root: `pnpm test`
- All packages must use TypeScript
- Shared types go in `packages/shared/src/types/`
## Commands
- Install all: `pnpm install`
- Build all: `pnpm -r build`
- Test all: `pnpm -r test`
- Run specific package: `pnpm --filter frontend dev`
Frontend: /project/packages/frontend/CLAUDE.md
# Frontend Package
React 18 application with Vite.
## Commands
- Dev: `pnpm dev` (runs on :3000)
- Build: `pnpm build`
- Test: `pnpm test`
## Conventions
- Use React Query for API calls
- Styles in `.module.css` files
- Follow atomic design principles
- Import shared types from `@shared/types`
Backend: /project/packages/backend/CLAUDE.md
# Backend Package
Express.js API with PostgreSQL.
## Commands
- Dev: `pnpm dev` (runs on :8080)
- Build: `pnpm build`
- Migrations: `pnpm migrate`
- Test: `pnpm test`
## Conventions
- Use async/await (no callbacks)
- Validate all inputs with Zod
- Use the query builder in `src/db/`
- Import shared types from `@shared/types`
Each package adds context as you work in it.
Example 6: AWS Serverless Project
Location: ./CLAUDE.md
# AWS Serverless API Project
Serverless API built with AWS SAM and Lambda.
## SAM Commands
- Build and deploy: `sam build && sam deploy --config-env dev`
- Local testing: `sam local start-api`
- Validate template: `sam validate`
- View logs: `sam logs --tail`
## Project Structure
- SAM template: `template.yaml`
- Lambda functions: `src/functions/`
- Shared code: `src/layers/`
- Tests: `tests/`
## Standards
### Lambda
- Runtime: Node.js 22 with TypeScript
- Timeout: Default 30 seconds
- Environment variables for all configuration
- Enable X-Ray tracing
### Security
- Least-privilege IAM policies (use SAM policy templates)
- Secrets in AWS Secrets Manager
- Never log sensitive data
- Input validation on all endpoints
### Observability
- Enable X-Ray tracing on all Lambda functions
- Use structured logging (JSON format)
- Set CloudWatch log retention (7 days dev, 30 days prod)
- Create CloudWatch alarms for errors and throttles
### Deployment
- Never deploy to production from local
- Always run `sam validate` before deploying
- Use `samconfig.toml` for environment-specific configs
## Common Gotchas
- API Gateway has 10MB payload limit (use S3 presigned URLs for large files)
- Lambda timeout max is 15 minutes (use Step Functions for longer workflows)
- DynamoDB Query/Scan limited to 1MB per request (use pagination)
Focused on essential SAM-specific knowledge without over-prescribing architecture.
Best Practices: What Works (And What Doesn't)
Based on the official Claude Code best practices documentation:
DO Include
ā
Bash commands Claude can't guess
- Run migrations: `npm run db:migrate`
- Start with test database: `TEST_MODE=1 npm run dev`
- Deploy to AWS dev: `sam deploy --config-env dev`
- Invoke Lambda locally: `sam local invoke FunctionName`
- Start local API: `sam local start-api --env-vars env.json`
ā
Code style that differs from defaults
- Use 2-space indentation (project default is 4)
- Always add trailing commas in arrays
ā
Testing instructions
- Use Jest, run single tests with: `npm test -- path/to/test`
- Test coverage required: 80% minimum
ā
Repository conventions
- Branch naming: `feature/xxx`, `fix/xxx`
- PR must link to issue number
ā
Architectural decisions
- We use Redux for state, not Context API (decision doc: @docs/state-management.md)
- Use Lambda for compute, not ECS (serverless-first approach)
- DynamoDB single-table design, not multiple tables per entity
- EventBridge for async workflows, not SQS (decision: @docs/architecture.md)
ā
Environment quirks
- Required env vars: `DATABASE_URL`, `API_KEY`
- On M1 Macs, use Rosetta for the Python build
ā
Non-obvious gotchas
- Auth tokens refresh every 5 minutes automatically
- Don't cache tokens - use the session manager
- Lambda cold starts: First invocation takes 2-3 seconds
- DynamoDB eventually consistent by default - use ConsistentRead for critical data
- API Gateway has 10MB payload limit - use S3 presigned URLs for large files
DON'T Include
ā Things Claude can infer from code
// Don't write: "The API uses Express"
// Claude sees this in package.json and imports
ā Standard conventions
// Don't write: "Use camelCase for JavaScript variables"
// Claude already knows this
ā Detailed API docs
// Don't copy/paste API documentation
// Instead: "See API docs at @docs/api-reference.md"
ā Obvious practices
// Don't write: "Write clean code"
// This doesn't help Claude avoid mistakes
ā File-by-file descriptions
// Don't list every file and what it does
// Claude can explore the codebase itself
Keep It Short
The golden rule: Every line should prevent Claude from making mistakes.
If your CLAUDE.md is over 500 lines, it's probably too long. Claude processes your CLAUDE.md at the start of every session. A bloated file doesn't just cost tokens ā it adds latency to every interaction and buries your important rules in noise. If you need more than 500 lines, that's exactly what .claude/rules/ is for.
Instead of:
# API Endpoints
We have many API endpoints. They follow REST conventions.
GET requests retrieve data. POST requests create data.
PUT requests update data. DELETE requests remove data.
Each endpoint should validate inputs and return proper
status codes. We use Express for the backend framework.
[... 50 more lines of basic REST explanation ...]
Write:
# API Rules
- Validate inputs with Zod
- Return errors in format from `@src/api/errors.ts`
- Version in URL: `/v1/`, `/v2/`
Short. Specific. Actionable.
Advanced: File Imports and References
You can import other files to keep your main CLAUDE.md focused:
See @README.md for project overview.
# Additional Context
- Git workflow: @docs/git-workflow.md
- API conventions: @docs/api-style.md
- Testing guide: @docs/testing.md
- Personal settings: @~/.claude/my-preferences.md
The @ syntax tells Claude to read those files. This keeps your main CLAUDE.md clean while still providing deep context when needed.
Watch out for auto-expansion: When you reference a file with @, Claude injects its full content into the context at startup. If your @README.md is a 500-line marketing document, you just bloated your context window. Only reference small, high-density files. If your README is heavy, create a focused docs/architecture-summary.md specifically for Claude instead.
Adding Emphasis to Critical Rules
If Claude keeps ignoring a rule, add emphasis:
**IMPORTANT: Always run `pnpm typecheck` before committing**
**YOU MUST use pnpm, NOT npm or yarn**
**NEVER commit directly to main**
This helps Claude prioritize those instructions.
Testing Your CLAUDE.md
How do you know if your CLAUDE.md is working?
- Watch Claude's behavior: Does it stop making the mistakes you documented?
- Ask Claude: "What do you know about our testing conventions?"
- Start fresh sessions: Close and reopen Claude Code. Your instructions should persist.
If Claude keeps doing something wrong despite having a rule, the file is probably too long and the rule is buried. Prune aggressively.
Common Pitfalls
The kitchen sink problem: Your CLAUDE.md has everything. Build instructions, API docs, architectural history, personal notes. Claude ignores half of it.
Fix: Move reference material to separate docs. Only include actionable instructions.
The obvious rule problem: Your file says "Write good code" and "Use meaningful variable names."
Fix: Delete these. Claude already knows. Only include non-obvious conventions.
The outdated rule problem: Your CLAUDE.md references old build commands or deprecated patterns.
Fix: Review quarterly. Update as your project evolves.
The no-priority problem: All rules look equally important. Critical conventions get lost.
Fix: Use emphasis for must-follow rules. Put critical items first.
Troubleshooting
Claude isn't following my CLAUDE.md rules
- File might be too long (aim for under 500 lines)
- Rule might be buried among too many other rules
- Rule might be too vague ("write better code" doesn't help)
- Add emphasis to critical rules: "IMPORTANT:"
Claude asks questions that are answered in CLAUDE.md
- Phrasing might be ambiguous
- Rule might be in the wrong section
- File might be too long
- Rephrase the rule to be more explicit
Changes to CLAUDE.md aren't taking effect
- Close and reopen Claude Code (CLAUDE.md loads at startup)
- Check file location (should be project root or
~/.claude/) - Verify markdown syntax is correct
- Run
/memoryto open and verify your memory files, or ask Claude "What context do you have loaded?"
CLAUDE.md conflicts between global and project
- More specific (project-level) rules take precedence
- Use
CLAUDE.local.mdfor personal overrides - Both files load additively, they don't replace each other
Real-World Workflow
Here's how this works in practice:
-
Start a new project
- Run
/initto generate starter CLAUDE.md - Add 5-10 critical rules
- Commit it to git
- Run
-
Notice patterns
- Claude does something wrong repeatedly
- Add a rule to CLAUDE.md
- Close and reopen Claude Code
- Problem stops happening
-
Refine over time
- Review monthly
- Delete rules Claude already follows
- Update outdated conventions
- Add gotchas as you discover them
-
Share with team
- Commit project CLAUDE.md
- Team members benefit immediately
- Everyone contributes rules they discover
The file compounds in value. After a few months, it contains all the non-obvious knowledge about your codebase.
AWS Development Example:
- You start an AWS SAM project
- Run
/initto generate starter CLAUDE.md - Add "Prefer Lambda over ECS", "Use single-table DynamoDB design", "Always use SAM for IaC"
- Document your SAM deployment commands and samconfig.toml structure
- Add gotchas as you discover them ("Lambda timeout is 30s for API Gateway", "Use sam local start-api for testing")
- After a month, Claude knows your AWS patterns and builds correct SAM templates first time
Conclusion
CLAUDE.md transforms Claude Code from "smart assistant" to "team member who knows your codebase." The difference is massive.
Without it, you're correcting Claude constantly. With it, Claude follows your team's conventions from the start. Less friction, better output, more trust.
Start small. Generate a starter with /init, add 5-10 critical rules, and iterate. The file will grow as you discover what Claude needs to know.
For more details on authentication, model selection, and cost optimization with Bedrock, see the previous post.
Additional Resources
- Claude Code Best Practices
- CLAUDE.md Documentation
- Previous: Claude Code with Bedrock
- Previous: IDE Setup
What conventions are you putting in your CLAUDE.md? Let me know in the comments!
Top comments (3)
Great article here. Couple more from my own experience:
Architecture.mdat the start of the session and refer it from theClaude.mdthat way, your architecture and design decisions are concentrated at one place.package.jsonand add the dependencies manually since it otherwise adds lot of dependencies without much reason. Supply chain attacks are pretty common and blind-spot these days hence the precaution.I like those. I'll definitely think of adding a version of the package.json one. Do you tell it to suggest packages to you and then you add them manually?
Been using CLAUDE.md for a few months now and the path-based rules in
.claude/rules/are a game changer that I don't see enough people talking about. Having different conventions auto-load depending on which directory you're working in means the context stays tight and relevant.One thing I've found works really well that's not covered here: adding a "common mistakes" section. Stuff like "Don't use
fs.readFileSyncin Lambda handlers" or "The auth middleware must come before the rate limiter". Basically documenting the bugs you've already fixed so Claude doesn't reintroduce them. It's like a lightweight regression prevention layer.Also +1 to @lalitkale's point about restricting package.json access. I do something similar - I have a rule that says "suggest new dependencies in comments but don't add them to package.json". Keeps the supply chain clean and forces you to actually evaluate whether you need that new dep.