DEV Community

Alex Rogov
Alex Rogov

Posted on • Originally published at alexrogov.hashnode.dev

5 Things I Put in Every CLAUDE.md That Make AI Actually Useful

Most developers treat CLAUDE.md like a prompt — a paragraph of instructions they paste once and forget. That's why most developers get mediocre results from AI coding assistants.

I've been using Claude Code with CLAUDE.md files across 10+ production projects over the past year. After hundreds of iterations, I've narrowed it down to 5 things that consistently make the difference between "AI that generates random code" and "AI that understands my project."

Here's what I put in every single CLAUDE.md — and why.

1. Project Identity: What This Is (and What It's Not)

The first thing Claude Code reads is your CLAUDE.md. If it doesn't immediately understand what it's working on, every subsequent output will be slightly off.

Bad:

# My Project
This is a web app.
Enter fullscreen mode Exit fullscreen mode

Good:

# Fintrack — Personal Finance Dashboard

## What is this
SaaS application for tracking personal finances.
Built as a monorepo with a Next.js frontend and NestJS API.

## Stack
- Frontend: Next.js 15, React 19, TypeScript 5.7, Tailwind CSS
- Backend: NestJS 11, PostgreSQL 16, Prisma ORM
- Infrastructure: Docker, AWS ECS, GitHub Actions CI/CD

## What this is NOT
- Not a mobile app (we have a separate React Native repo)
- Not a library — this is a product with users
Enter fullscreen mode Exit fullscreen mode

The "What this is NOT" section is surprisingly powerful. It prevents the AI from suggesting React Native patterns in your web app, or treating your product code like a library that needs to be published to npm.

Why it works: Claude uses this context for every decision — file naming, import paths, error handling patterns, even how it writes commit messages. The more specific you are, the more targeted every output becomes.

2. Architecture Rules: The Dependency Map

This is the single highest-ROI section. Without it, Claude will create files anywhere, import anything from anywhere, and violate your architecture in ways that compile but slowly destroy your codebase.

## Architecture

### Layer Structure (Clean Architecture)
Enter fullscreen mode Exit fullscreen mode

src/
├── domain/ # Entities, value objects, domain errors
│ └── NO imports from infrastructure or application
├── application/ # Use cases, ports (interfaces)
│ └── Can import from domain only
├── infrastructure/ # Repositories, external APIs, DB
│ └── Can import from domain and application
└── presentation/ # Controllers, resolvers, DTOs
└── Can import from application only


### Rules
- NEVER import from infrastructure in domain or application layers
- Every use case must go through a port (interface), never call infrastructure directly
- DTOs live in presentation layer, entities live in domain
- Database models (Prisma) are infrastructure — never leak them into domain
Enter fullscreen mode Exit fullscreen mode

I can't stress this enough: AI coding assistants will cheerfully violate your architecture if you don't tell them not to. They optimize for "works now," not "maintainable in 6 months."

The dependency rules act as guardrails. When Claude Code generates a new service, it knows exactly where to put it, what it can import, and what patterns to follow.

Real example: Before I added this section, Claude kept importing Prisma models directly into my use cases. After — it automatically creates proper repository interfaces in the application layer and implements them in infrastructure. Zero prompting needed.

3. Code Style Contracts: Be Specific or Be Sorry

"Follow best practices" means nothing to an AI. It will default to whatever pattern appeared most in its training data — which might be a 2019 Express.js tutorial.

Instead, I write explicit contracts:

## Code Style

### TypeScript
- Strict mode always (`strict: true` in tsconfig)
- No `any` — use `unknown` with type guards
- Prefer `interface` over `type` for object shapes
- Use discriminated unions for state management
- Exhaustive switch with `never` for union types

### Error Handling
- Domain errors are custom classes extending `DomainError`
- Never throw generic `Error` — always use typed errors
- Use `Result<T, E>` pattern for operations that can fail
- HTTP errors are mapped in presentation layer, never in use cases

### Naming
- Files: kebab-case (`user-repository.ts`)
- Classes: PascalCase (`UserRepository`)
- Functions/methods: camelCase (`findById`)
- Use cases: verb-noun (`CreateUser`, `GetTransactions`)
- No abbreviations in public APIs (`transaction`, not `tx`)

### Testing
- Test files: `*.spec.ts` next to source files
- Use `describe/it` blocks, not `test`
- Mock external dependencies only (DB, APIs)
- Don't mock what you own — test through public interfaces
Enter fullscreen mode Exit fullscreen mode

The naming section alone saves me hours of refactoring. Without it, Claude alternates between userRepo, UserRepository, user_repository, and userDataAccess — sometimes within the same PR.

Pro tip: Add examples of what NOT to do. AI models learn boundaries faster from negative examples:

### Anti-patterns (NEVER do this)
-`const data: any = await fetch(...)`
-`const data: unknown = await response.json()`
-`try { ... } catch (e) { console.log(e) }`
-`try { ... } catch (e) { throw new InfrastructureError('Failed to fetch user', { cause: e }) }`
Enter fullscreen mode Exit fullscreen mode

4. Verification Commands: Trust but Verify

This is the one most people skip — and it's the one that prevents broken PRs.

## Verification

Before completing any task, run:
1. `npm run typecheck` — must pass with zero errors
2. `npm run lint` — must pass (we use strict ESLint config)
3. `npm run test` — all tests must pass
4. `npm run build` — must compile successfully

### After creating new files
- Run `npm run typecheck` to verify imports are correct
- Ensure new files follow the naming convention
- Add exports to the nearest barrel file (index.ts)

### After modifying API endpoints
- Update the OpenAPI schema: `npm run generate:schema`
- Regenerate client types: `npm run generate:client`
Enter fullscreen mode Exit fullscreen mode

When Claude Code reads this, it actually runs these commands before telling you "done." Without this section, you get code that looks right but fails on the first CI run.

I've measured the impact: adding verification commands reduced my "fix CI after AI changes" time by ~70%. The AI catches its own mistakes before I even review the code.

Why this beats "just run tests": Being specific about which commands to run and when gives the AI a decision tree. It doesn't have to guess whether this change needs a type check, a build, or a schema regeneration — you've already told it.

5. Current Context: The Living Section

This is the section that evolves. While the first four sections are relatively stable, this one changes weekly:

## Current Context

### Active Sprint
- Implementing multi-currency support for transactions
- Related files: `src/domain/transaction/`, `src/application/use-cases/transactions/`
- Key constraint: amounts stored in cents (integer), never floats

### Recent Decisions
- 2026-03-01: Switched from REST to GraphQL for mobile API (REST stays for webhooks)
- 2026-02-15: Adopted Result pattern instead of throwing in use cases
- 2026-02-01: Migrated from Jest to Vitest (faster, native ESM)

### Known Issues
- `UserService.findByEmail` has N+1 query — fix after current sprint
- Rate limiter on `/api/auth` is too aggressive (100 req/min → change to 300)

### Do Not Touch
- `src/infrastructure/legacy/` — migrating gradually, don't refactor
- `docker-compose.prod.yml` — managed by DevOps team
Enter fullscreen mode Exit fullscreen mode

This section is gold for two reasons:

  1. It prevents the AI from "fixing" things you know about. Without the "Known Issues" section, Claude will happily refactor that N+1 query when you asked it to add a simple field.

  2. "Do Not Touch" saves you from disaster. I learned this the hard way when Claude "helpfully" refactored a legacy payment integration that was working fine but looked ugly. Three hours of rollback.

The CLAUDE.md Mental Model

Here's the key insight: CLAUDE.md is not a prompt. It's an architecture contract.

A prompt is something you write once to get a response. A contract is a living document that defines the rules of engagement between you and your AI collaborator.

Think of it like an onboarding document for a new senior developer joining your team. You wouldn't just say "write good code." You'd explain the project, the architecture, the conventions, the current sprint, and the things that are off-limits.

That's exactly what CLAUDE.md does — except the new team member reads it every single time and never forgets.

Getting Started

If you don't have a CLAUDE.md yet, start with this minimal template:

# [Project Name]

## What is this
[One paragraph describing the project, stack, and purpose]

## Architecture
[Folder structure + dependency rules]

## Code Style
[5-10 most important conventions]

## Verification
[Commands to run before completing any task]

## Current Context
[What you're working on right now]
Enter fullscreen mode Exit fullscreen mode

You can always add more later. The point is to start with something specific and iterate. Every time the AI does something wrong, ask yourself: "Could I have prevented this with a CLAUDE.md rule?" If yes, add it.

After a month, you'll have a CLAUDE.md that makes your AI assistant genuinely useful — not just fast, but accurate, consistent, and aligned with how you actually build software.

Key Takeaways

  • Project Identity prevents the AI from guessing what your project is — be explicit about what it is AND what it isn't
  • Architecture Rules are the highest-ROI section — without them, the AI will violate your dependency boundaries
  • Code Style Contracts must be specific, not generic — include negative examples
  • Verification Commands reduce CI failures by ~70% — tell the AI what to check and when
  • Current Context is the living section — update it weekly to keep the AI aligned with your sprint

Building a production app with Claude Code? I share daily tips on AI-augmented architecture on Twitter/X. Let's connect on LinkedIn if you're working in the same space.


Originally published on my Hashnode blog. Follow me for more AI + Architecture content.

Top comments (0)