Skip to content

Best Practices

The difference between agents that reliably ship code and agents that spin their wheels? Context. Learning to engineer context—and structure your code to help agents succeed—makes agentic development predictable.

Context engineering is the discipline of providing AI agents with the right information at the right time. It’s not just prompt engineering—it’s the entire system of what an agent knows when it makes decisions.

Think of it this way: an agent’s context window is its entire working memory. Everything it can “see” at once—your instructions, the code, conversation history, tool outputs—shapes every decision it makes. Engineer that context poorly, and even the smartest model produces garbage.

A perfect prompt with bad context fails. A mediocre prompt with excellent context often succeeds.

Context includes:

  • System instructions — The agent’s baseline behavior and constraints
  • Codebase knowledge — Files, patterns, conventions the agent can reference
  • Conversation history — What’s been discussed and decided
  • Tool outputs — Results from file reads, command execution, searches
  • External documentation — API docs, specs, requirements

Prompts are just one piece. The agent’s entire context window determines output quality.

1. Curate what goes in

Not everything belongs in context. More isn’t better—it’s often worse. Irrelevant context dilutes signal and wastes tokens.

Ask: What does the agent need to complete this task? Include that. Exclude everything else.

2. Structure for retrieval

Agents scan context looking for relevant information. Make it findable:

  • Use clear headings and sections
  • Put critical constraints early
  • Group related information together
  • Use consistent formatting

3. Manage context lifecycle

Context windows fill up. When they do, older content gets truncated—the agent literally forgets it.

  • Start fresh conversations for new tasks
  • Summarize long discussions before continuing
  • Re-state critical constraints when context is getting full
  • Use tools like AGENTS.md to persist important context across sessions

Include relevant code, not all code. If you’re modifying a function, include that function and its direct dependencies. Don’t dump the entire codebase.

Provide examples of desired output. Show the agent what good looks like. A single example often beats paragraphs of description.

State constraints explicitly. “Don’t modify the public API” is context. “Follow the error handling pattern in auth.ts” is context. Make implicit knowledge explicit.

Use documentation as context. When working with unfamiliar APIs, paste relevant docs into the conversation. The agent can’t hallucinate what’s right in front of it.

Leverage persistent context files. AGENTS.md, README files, and similar documents provide context that persists across sessions. Keep them current.

Kitchen sink context — Dumping everything “just in case.” Dilutes signal, wastes tokens, confuses the agent.

Stale context — Outdated documentation or code that contradicts current state. Leads to hallucinations and wrong assumptions.

Implicit context — Assuming the agent knows things you haven’t told it. Your mental model isn’t in the context window.

Context fragmentation — Spreading related information across many messages. Group related context together.

Agents fail in predictable ways. Learning these patterns helps you catch problems early.

Agents use APIs, methods, or parameters that don’t exist. Code looks right but fails at runtime.

Triggers: Less popular libraries, recently changed APIs, domain-specific tools.

Catch it: Be suspicious of unfamiliar method names. Verify imports exist. Run the code—don’t just read it.

Prevent it: Include relevant docs in your context. Use well-known patterns. Ask the agent to explain its reasoning.

The agent starts solving your problem but gradually shifts to a different, easier problem.

Triggers: Long prompts with multiple objectives. Ambiguous requirements.

Catch it: Re-read the original requirement after each iteration. Ask: “Does this still address my actual problem?”

Prevent it: Break into smaller, focused pieces. State success criteria explicitly.

The agent tries the same failing approach repeatedly with minor variations.

Triggers: Errors the agent doesn’t understand. Tasks beyond capability. Missing context.

Catch it: Track iterations—if you’re past 3-4, something’s wrong.

Prevent it: Provide error context. Break out manually and try a different approach.

The agent declares success when code doesn’t work, or claims certainty about incorrect information.

Catch it: Never trust self-assessment. Run the code yourself. Write tests for actual requirements.

Prevent it: Ask how it verified correctness. Request specific edge case tests.

Early context gets “forgotten” as conversations grow. Agent contradicts earlier decisions or ignores constraints.

Triggers: Long conversations. Large codebases in context. Complex multi-step tasks.

Catch it: Watch for inconsistencies. Notice when agent asks for info you already provided.

Prevent it: Keep conversations focused and short. Start fresh for new tasks. Re-state critical context.

Code looks sophisticated but fundamentally misunderstands the problem or domain.

Catch it: Trace logic mentally—does it make sense? Get domain expert review.

Prevent it: Provide domain context explicitly. Include examples of correct solutions.

The same patterns that help new developers help agents: clear structure, explicit types, good documentation, consistent patterns.

Keep files focused. One concept per file. Agents request specific files—make those requests useful.

Use descriptive names. UserAuthenticationService.ts beats uas.ts. Agents infer purpose from names.

Flatten when possible. Deep nesting forces agents to understand hierarchy.

Keep functions short. Under 50 lines. Single responsibility. Clear inputs and outputs.

Use types generously. TypeScript, Python type hints. Types are machine-readable documentation.

// Harder for agents
function process(data) {
/* what's data? */
}
// Easier for agents
function processOrder(order: Order): ProcessedResult {
/* clear context */
}

Define interfaces at boundaries. Explicit interfaces prevent integration bugs.

Avoid any/unknown. More type information enables better inference.

Document the “why,” not the “what.” Agents read what code does. They can’t read your mind.

// Less useful
// This function calculates the discount
function calculateDiscount(order: Order): number;
// More useful
// Business rule: Premium customers get 10% off orders over $100
// This discount stacks with promotional codes
function calculateDiscount(order: Order): number;

Keep READMEs current. Agents often start by reading them. Outdated docs mislead.

Document non-obvious constraints. Rate limits, known limitations, things that “just work that way.”

Write tests as specification. Tests tell agents what code should do.

Keep tests fast. Agents run tests frequently. Slow tests break feedback loops.

Test edge cases explicitly. Tests covering edges tell agents about edges they might miss.

Use descriptive names. test_user_creation_fails_with_duplicate_email beats test_user_3.

  • Magic and metaprogramming — Dynamic method generation, heavy reflection confuse agents
  • Implicit dependencies — Service locators, ambient context hide needed information
  • Circular dependencies — Confuse agents about what depends on what
  • Inconsistent patterns — If you do the same thing three ways, agents won’t know which to follow

You don’t need to rewrite everything. As you touch code:

  • Improve the file you’re modifying
  • Add types where you add code
  • Write a test when you fix a bug
  • Update docs when you discover they’re wrong

Over time, the codebase becomes more agent-friendly—and more human-friendly.