A Working CLAUDE.md/AGENTS.md Template You Can Copy Today A developer published a copy-paste CLAUDE.md/AGENTS.md template for repository-level coding agent context files, arguing that vague prose instructions like "write clean code" fail to change agent behavior. The template prescribes six sections — frontmatter metadata, project context, conventions, testing, and others — with the core technique of replacing adjectives with concrete avoid/preferred code block pairs. It recommends limiting conventions to the three to five highest-value ones to avoid context rot. If you've added a CLAUDE.md or AGENTS.md file to your repo and felt like your coding agent still ignores half of it, you're not alone. Most of these files are just vague prose — "write clean code," "follow best practices," "use good error handling" — and vague prose doesn't change agent behavior any more than it changes a new hire's behavior on day one. The fix isn't a longer file. It's a differently structured one. Below is a template you can copy straight into your repo today, plus the reasoning behind each section so you're not just cargo-culting it. Here's the pattern almost everyone starts with: Code style - Write clean, maintainable code - Use good error handling - Follow best practices None of this is wrong, exactly. It's just useless to a model. "Good error handling" means nothing without a concrete shape to imitate. Compare that to this: // Avoid catch e { console.log e ; } // Preferred catch e { logger.error 'checkout.payment failed', { orderId, cause: e } ; throw new PaymentError orderId, e ; } The second version gives the model an actual pattern to pattern-match against — a logger call with a namespaced event, structured metadata, and a typed error thrown upward. That's the single biggest lever in this whole exercise: replace adjectives with code blocks. A good context file has six parts, in this order: Let's build the file section by section. --- last updated: 2026-09-14 owner: platform-team scope: global review cadence: quarterly --- This looks like overhead, but it's the difference between a file that rots silently and one that gets maintained. When a rule looks outdated, whoever finds it knows exactly who to ping. State the things a competent engineer would otherwise have to reverse-engineer from the codebase — especially anything that goes against the obvious default. Project context - Stack: Node.js 20, Express, PostgreSQL raw SQL via pg , no ORM — see ADR-014 - Monorepo managed with pnpm workspaces - Auth: sessions via iron-session , not JWTs — do not introduce JWT-based auth - All API responses follow the envelope in src/lib/response.ts ; never return raw objects from route handlers Any model trained broadly will default to reaching for an ORM the moment it touches a database. Stating the constraint and pointing to the reasoning heads that off before it happens. Pick your 3-5 highest-value conventions rather than trying to cover everything. More isn't better here; it's context rot. API responses // Avoid res.json { id: user.id, name: user.name } ; // Preferred res.json successResponse { id: user.id, name: user.name } ; Async error handling js // Avoid app.get '/users/:id', async req, res = { const user = await getUser req.params.id ; res.json user ; } ; // Preferred app.get '/users/:id', asyncHandler async req, res = { const user = await getUser req.params.id ; res.json successResponse user ; } ; Naming js // Avoid const d = new Date ; const u = await getUser id ; function calc x, y { return x y 0.08; } // Preferred const requestTimestamp = new Date ; const user = await getUser id ; function calculateSalesTax subtotal, taxRate = 0.08 { return subtotal taxRate; } Dependency access js // Avoid import { db } from '../../../lib/db'; export async function getOrders userId { return db.query 'SELECT FROM orders WHERE user id = $1', userId ; } // Preferred import { OrdersRepository } from './orders.repository'; export async function getOrders userId, ordersRepo = new OrdersRepository { return ordersRepo.findByUserId userId ; } Repos are injectable so tests can pass a fake — see tests/api/orders.test.ts for the pattern. Each pair takes about 30 seconds to write and saves you from re-explaining the same thing in code review, repeatedly, forever. Testing - Every new route handler needs an integration test in tests/api/ , following the pattern in tests/api/users.test.ts - Run pnpm test:unit before considering any change complete - Do not mock the database in integration tests — use the test containers setup in tests/setup.ts - Minimum coverage for new files: 80% Do not touch - migrations/ — migrations are hand-reviewed only; never generate or edit these - src/legacy/billing/ — frozen code pending a rewrite; bug fixes only, no refactors - .github/workflows/ — CI changes require a platform-team review; flag instead of editing directly If you've ever had an agent "helpfully" refactor a file that was explicitly untouchable, this section is why it happened — nobody told it not to. Commands - Install: pnpm install - Run dev server: pnpm dev - Run all tests: pnpm test - Lint: pnpm lint - Type check: pnpm typecheck --- last updated: YYYY-MM-DD owner: team-name scope: global review cadence: quarterly --- Project context - Stack: languages, frameworks, database - Architecture: monorepo/polyrepo, key services - Non-obvious constraints: things that go against the default assumption Conventions Convention name Avoid: bad example Preferred: good example repeat for 3-5 highest-value conventions Testing - what every change requires - how to run tests - coverage or quality gates Do not touch - path : reason Commands - Install: command - Dev: command - Test: command - Lint: command Here's the same template filled in for a mid-sized ASP.NET Core Web API with EF Core and Clean Architecture: --- last updated: 2026-09-14 owner: payments-team scope: global review cadence: quarterly --- Project context - Stack: .NET 8, ASP.NET Core Web API, EF Core 8, SQL Server - Architecture: Clean Architecture — Api/, Application/, Domain/, Infrastructure/ - CQRS via MediatR — every write is a Command, every read is a Query - Do not call EF Core directly from controllers — always go through a MediatR handler - Dependency injection only — no new SomeService inside business logic Controllers stay thin // Avoid HttpPost public async Task