EngineeringMay 15, 2026· 10 min read
Context Engineering for Coding Agents: why a single CLAUDE.md isn't enough
Why one CLAUDE.md isn't enough on a real codebase, and the 3-layer structure we use at Maleus to feed agents the right instructions at the right moment.
Adrien MaretCTO
Key takeaways
- One big CLAUDE.md doesn't scale: it dilutes the context until the model ignores the rules that matter
- Layer instructions in three levels (coding rules, CLAUDE.md, per-module AGENT.md), each loaded only when it's relevant
- A coding rule keeps the AGENT.md index fresh, so the docs maintain themselves with no human in the loop
- Measured: without the index, agents take twice the turns and ~50% more cost to reach the same answer

On this page
- Why 3 levels and not just one
- Level 1: coding rules, triggered by pattern matching
- One rule per file type
- Edit flow: deterministic pattern matching on the read
- Write flow: the problem of the file that doesn't exist yet
- Skills: LLM evaluation as a complement
- CRITICAL INSTRUCTION, to be used sparingly
- Building the rules empirically
- Level 2: CLAUDE.md, the project plumbing
- Level 3: per-module AGENT.md, the business logic
- One file per functional domain
- Standard structure
- Short example: features/agent-sessions/AGENT.md
- AGENT.md as a codebase index
- Benchmark: what AGENT.md files save
- The rule that creates the rule
- Conclusion
See how Maleus can accelerate your team.


Context Engineering is the art of feeding an agent the right information at the right moment so it codes correctly.
Today, most teams that tackle the subject dump all their instructions into one big
CLAUDE.mdat the repo root. It works for a prototype, it breaks down as soon as the codebase grows.On a real application, you need 3 distinct levels of instructions, each triggered at a different point in the agent's workflow. Without this layering, you dilute the context so much that the LLM ends up ignoring the most important rules.
In this article, I walk through the 3 layers we use at Maleus to generate applications in near-autonomy.
Why 3 levels and not just one
An LLM's context is limited and every token dilutes the others.
Instructions have different scopes:
CLAUDE.mdAGENT.mdIf you load everything at once, you get noise, contradictions between rules, and dilution.
The fix is to layer. Each layer is loaded when it's relevant.
The orders of magnitude at Maleus today:
Which gives two interesting ratios:
AGENT.mdper ~27 lines of code in the module it describesIf we dumped these 16,000 lines into one big
CLAUDE.md, we'd burn the equivalent of several tens of thousands of tokens on every prompt, 95% of it unrelated to the task at hand. Layering is what makes this context investment sustainable instead of unbearable.Level 1: coding rules, triggered by pattern matching
This is the most granular layer and the one with the most impact on the quality of the generated code.
One rule per file type
At Maleus, every backend file is suffixed with its role:
<Entity>.table.tsfor the Drizzle table definition<Entity>.repository.tsfor DB access<Entity>.service.tsfor business logic<Entity>.controller.tsfor Nest.js endpointsEach suffix maps to a dedicated rule, triggered by a precise glob in the
pathsproperty of the frontmatter:# .claude/rules/backend/backend-tables.md name: "backend-tables" paths: "**/{backend,studio-backend}/**/*.table.ts"This glob-based split lets you load only the relevant rule at the moment the agent touches a file of that type.
Edit flow: deterministic pattern matching on the read
The agent always reads a file before editing it. The path passed to
readserves as the key for pattern matching.It's pattern matching, so 100% deterministic, with no LLM randomness.
Write flow: the problem of the file that doesn't exist yet
This mechanism falls apart when the agent creates a new file.
Why? Because it has no reason to
readfirst, the file doesn't exist. It directly generateswrite("Foobar.table.ts", content), and the content is already in the buffer before we could pattern-match anything.Pattern matching has a gap, then: for file creation, you need another mechanism.
Skills: LLM evaluation as a complement
Skills are instructions that the LLM decides on its own to load by evaluating their description.
The mechanism:
\*\*/_.table.ts"*CRITICAL INSTRUCTION YOU MUST ALWAYSto reinforce its priorityAt our place, the rule (
.claude/rules/backend/backend-tables.md) and the skill (.claude/skills/backend-tables/SKILL.md) are the same file via a hardlink. The frontmatter and the content are identical, only the triggering mechanism changes: pattern matching for the edit flow, LLM evaluation for the write flow.The frontmatter therefore serves both channels at once:
--- name: "backend-tables" paths: "**/{backend,studio-backend}/**/*.table.ts" description: Contains guidelines for defining database tables using Drizzle ORM with PostgreSQL. [...] CRITICAL INSTRUCTION You MUST ALWAYS use this skill before editing/writing any file matching "**/{backend,studio-backend}/**/*.table.ts". ---In practice the skill triggers almost every time, but it's never 100% since no deterministic mechanism can intercept a file creation before the content is generated.
CRITICAL INSTRUCTION, to be used sparingly
The
CRITICAL INSTRUCTION YOU MUST ALWAYSkeyword is a prioritization channel specific to Anthropic models that works by contrast. If you write it everywhere, nothing is a priority anymore.Above all, writing
CRITICAL INSTRUCTIONis a sign that you need determinism, not prompt engineering. Prompt engineering increases the probability that an instruction is followed; determinism guarantees it. As soon as a deterministic mechanism is possible (pattern matching, hook, lint, test), it should be preferred. The skill is the acknowledged exception: nothing can intercept a file creation before content generation.CRITICAL INSTRUCTIONworks by contrast, so spend it sparingly. Reserve it for the rare case where no deterministic mechanism (pattern matching, hook, lint, test) can enforce the rule for you.Building the rules empirically
No need to write all the coding rules up front. You end up with generic rules that never trigger, or worse, that contradict each other.
The approach that works is the opposite. You start with a few essential rules (backend structure, method naming, error format), then you watch the agent and on each recurring mistake, you enrich the corresponding rule.
You might as well let Claude Code write these additions: Anthropic models are fine-tuned for it. You give it the correction ("in this repository you should have used X instead of Y") and it updates the rule. We always reread, and we keep the rules short: a rule that's too long gets diluted in the context.
Every human correction becomes a rule that prevents the same mistake next time.
This three-layer approach is one of the key elements that let Maleus generate applications ready to deploy in production.
Level 2: CLAUDE.md, the project plumbing
A single
CLAUDE.mdfile at the repo root, loaded once at the start of the session.It answers three questions: where am I, what tools do I have, what's the environment.
Concretely, you find:
14001, backend14002)Since this file is loaded for the whole session, you have to keep it short: every line stays in the context of every request.
Level 3: per-module AGENT.md, the business logic
Where the coding rules say how to write, the
AGENT.mdfiles say what this module does.One file per functional domain
Inside each functional module, we put an
AGENT.mdfile that contains the technical-functional info specific to the module.These
AGENT.mdfiles are written by the agents themselves as they code, in a style optimized for an LLM that needs to understand fast rather than for human onboarding.At Maleus today, we have 97
AGENT.mdfiles spread across the whole application, from a few dozen to several hundred lines depending on the module's complexity.Standard structure
The template we eventually converged on, generated and maintained by the agents:
# <Module> Module ## Overview [2-4 sentences: the module's reason for being] ## Main Flows [1 to 3 ASCII diagrams of the main flows] ## Important Files [Table: file path | responsibility] ## Tables Structure [Table per table: column | type | description] ## Permissions [Table: API action | allowed roles | description] ## Error Codes [Table: code | HTTP status | description] ## Dependencies [List of the other modules / clients we depend on]Not all sections appear in every module, only the ones that make sense. A module without a DB has no Tables Structure section. A module that doesn't consume AMQP events has no AMQP Events section.
Short example: features/agent-sessions/AGENT.md
As an illustration, here's a minimal
AGENT.md(69 lines) covering an AMQP event routing module:# Agent Sessions Module ## Overview The Agent Sessions module owns the AMQP event consumption for `snivel.agent-sessions.end` and `snivel.files.change` events. It routes these events to the appropriate feature module (versions or workshops). This module exists because the AmqpEventServer enforces one handler per event pattern, and multiple feature modules need to react to the same events. ## Important Files | File | Description | | ---------------------------------------- | ------------------------------------------- | | services/AgentSessionsRouting.service.ts | Event routing logic + Mutex | | presentation/AgentSessions.consumer.ts | AMQP consumer for snivel.agent-sessions.end | | presentation/FilesChange.consumer.ts | AMQP consumer for snivel.files.change | ## Routing Logic 1. Distributed Mutex lock on agent-session:processing:{conversationId} prevents duplicate processing 2. If sessionOrigin is present, dispatch directly to the owning module (no DB lookups) 3. Fallback: try each handler sequentially until one claims the conversationWith 69 lines like these, an agent that receives a task such as "add a new AMQP event type" immediately knows:
AgentSessionsRouting.service.tsplus a new consumerWithout this
AGENT.md, the agent would have had to open 5 to 10 files to reconstruct that understanding.AGENT.md as a codebase index
An
AGENT.mdplays the role of an index in the database sense: when an agent gets a task, it opens the ones for the potentially relevant modules and, in a few hundred tokens per module, it knows:The agent no longer rummages through the codebase at random, it knows directly which files to open, which saves tokens and keeps its context clean for reasoning.
Benchmark: what AGENT.md files save
To quantify it, we ran a code-comprehension question in production on the monorepo, "how do images flow from the studio frontend to Claude Code in snivel?", twice, with and without access to the
AGENT.mdfiles.2× more agent turns without the index (4 vs 2)
+103% more tokens without it (4.3M vs 2.1M)
+49% higher cost for the same answer ($2.28 vs $1.53)
Same final conclusion in both cases. But without the index, the agent doubles its token consumption and pays 50% more just to reconstruct what the
AGENT.mdfiles would have told it in a few lines.At the scale of a team running thousands of requests a day, that's the difference between a sustainable API bill and a budget that spirals.
The rule that creates the rule
And it's a coding rule that maintains these
AGENT.mdfiles, taken frombackend-architecture.md:**Module AGENT.md** Each module MUST contain a AGENT.md file at the root. This file contains a high level description of the module purpose and general behavior. It can contain anything necessary so a Senior developer is happy to read it to understand how to handle the module: - schema of main flow - important files - tables structure - particular permissions - important API actions - important business logic **You MUST ALWAYS update the AGENT.md file associated with a module after working on it.**This rule is loaded on every backend file edit, because its
paths:is**/{backend,studio-backend}/**/*, a generic glob that matches every file in the backend. So every time an agent touches a module file, it has the instruction to update theAGENT.mdright in its context.Layer 1 (coding rules) thus maintains layer 3 (
AGENT.md), without a human ever touching the docs.Conclusion
Three layers, three loading moments, each with a triggering mechanism suited to it.
Without this layering, you hit a ceiling fast as soon as the codebase grows. You end up putting so many instructions in the same place that none of them is followed properly.
It works because it respects how the LLM's attention actually works: the info has to be close to the moment of generation, and not lost 20,000 tokens earlier in a giant
CLAUDE.md.The
AGENT.mdfiles are themselves generated and maintained by the coding agents, which follow a rule loaded on every backend file edit, so the system maintains itself with no human intervention on the documentation.At Maleus, this is what lets us generate the majority of the code in near-autonomy without sacrificing quality.