| name | agent-engineering |
|---|---|
| description | Best practices for operating Claude Code on complex, multi-domain engineering projects. Covers agentic architecture, tool design, MCP integration, configuration hierarchy, prompt engineering, context management, multi-agent orchestration, session architecture, and reliability patterns. Based on the Claude Certified Architect — Foundations (CCA-F) exam domains. Use when planning large implementations, configuring Claude Code for a team, designing multi-session workflows, debugging agent behavior, or preparing for the CCA-F certification. |
Operational guide for using Claude Code as an engineering multiplier on large-scale, multi-domain projects. Every section maps to a CCA-F exam domain.
Every message triggers this cycle:
Message → Model decides: tool or respond?
If tool: call it → Result enters context → Decide again
→ Repeat until stop_reason: "end_turn"
stop_reason values: "tool_use" = loop continues. "end_turn" = agent finished. Claude Code manages this internally.
Three anti-patterns to avoid:
| Anti-pattern | Problem | Fix |
|---|---|---|
| Parsing natural language for completion ("if Claude says 'done'") | Ambiguous — "done checking first file" ≠ done | Use stop_reason / trust built-in loop |
| Arbitrary iteration caps as primary stop | Cuts useful work or wastes iterations | Caps are safety nets, not primary mechanisms |
| Checking text content as completion | Response can have text AND tool_use | A text block at index 0 + tool_use at index 1 would be killed incorrectly |
Common failure modes:
- Premature termination — Partial implementation presented as complete. Fix: define acceptance criteria upfront.
- Infinite exploration — Reads files endlessly without converging. Fix: decompose before starting.
- Confident long-session errors — After 30+ tool calls, early context is compressed. Fix: scratchpad files, structured plans, fresh sessions for distinct phases.
Model-driven vs pre-configured: Claude decides which tool to call based on context (model-driven). This is flexible and preferred for most workflows. But for critical business logic (financial, security, compliance), use programmatic enforcement via hooks — not model judgment. The decision framework: hooks = deterministic guarantees (100%). Prompts = probabilistic guidance (~95%).
Rule of thumb: If a task needs >15 tool calls, split into phases. Each phase produces a concrete artifact (file, commit, spec). Next phase starts from that artifact, not accumulated conversation.
Claude Code uses hub-and-spoke architecture:
- Main conversation = coordinator (hub)
- Subagents via
Task(Agent SDK) orAgent(Claude Code) = spokes - ALL communication flows through coordinator
- Subagents NEVER communicate directly with each other
Critical isolation principle: Subagents do NOT automatically inherit coordinator conversation history. Every piece of information a subagent needs must be explicitly included in its prompt.
Coordinator responsibilities:
- Analyze requirements, dynamically select which subagents to invoke
- Partition scope to minimize duplication (assign distinct subtopics)
- Implement iterative refinement: evaluate results for gaps, re-delegate with targeted queries
- Route all communication through coordinator for observability
Narrow decomposition failure: If coordinator decomposes a topic too narrowly (e.g., "AI impact on creative industries" → only visual arts subtasks, omitting music/writing/film), that's a coordinator bug, not a downstream agent bug. Fix: provide a spec before requesting implementation.
Agent SDK: Coordinator spawns subagents via the Agent tool (formerly Task in earlier versions). Each subagent has an AgentDefinition with description, system prompt, and tool restrictions. Coordinator's allowedTools must include the subagent tool.
Claude Code: Subagents configured via .claude/agents/*.md with YAML frontmatter:
tools:— allowlistdisallowedTools:— denylistmcpServers:— available MCP serversmodel:— haiku, sonnet, opus, inheritpermissionMode:— acceptEdits, plan, bypassPermissionsskills:— pre-loaded skills
Context passing principles:
- Include complete findings from prior work directly in subagent prompt
- Use structured data formats separating content from metadata (source files, line numbers, function names)
- Specify objectives and quality criteria, NOT step-by-step procedures — enables subagent adaptability
Parallel spawning: Emit multiple Task tool calls in a single response for parallel execution.
Session forking: --resume <session-name> --fork-session creates independent branches from a shared analysis baseline for exploring divergent approaches.
Enforcement spectrum:
| Mechanism | Reliability | When |
|---|---|---|
| Prompt-based guidance (CLAUDE.md) | ~95% | Style, conventions, workflow guidance |
| Programmatic enforcement (hooks) | 100% | Financial, security, compliance |
Decision rule: Financial/security/compliance consequences → programmatic enforcement (hooks). Always reject prompt-based solutions for high-stakes scenarios.
Hook handler types: command (shell scripts), prompt (LLM-based decisions), agent (spawn subagent), http (POST to URL).
Hook events (all handler types):
| Event | When | Use case |
|---|---|---|
PreToolUse |
Before tool executes | Block dangerous ops, enforce prerequisites, modify inputs |
PostToolUse |
After tool succeeds | Normalize data, validate outputs, add context |
PostToolUseFailure |
After tool fails | Error handling, logging |
UserPromptSubmit |
User sends message | Input validation, context injection |
PermissionRequest |
Claude asks tool permission | Auto-approve/deny, modify inputs |
Stop |
Claude finishes responding | Validate completeness, format output |
SubagentStop |
Subagent completes | Logging, QA |
TaskCompleted |
Task completes | Post-task actions |
Command-only events: PreCompact, PostCompact, SubagentStart, SessionStart, SessionEnd, Notification, InstructionsLoaded, WorktreeCreate, WorktreeRemove, Elicitation, ElicitationResult.
Exit codes (command hooks): 0 = allow. 2 = block.
Advanced control via hookSpecificOutput: PreToolUse hooks can modify inputs (updatedInput), inject context (additionalContext), or escalate (permissionDecision: "ask").
Multi-concern request handling: Decompose requests with multiple issues into distinct items. Investigate each in parallel using shared context. Synthesize a unified resolution.
Structured handoff protocol: When escalating, compile: context summary, what was done, what remains, root cause analysis, recommended action. The recipient has NO access to conversation transcript.
PostToolUse hooks intercept tool results after execution, before the model processes them. Use case: normalize heterogeneous data formats from different MCP tools (Unix timestamps → ISO 8601, numeric status codes → readable strings). The model receives clean, consistent data regardless of source tool.
PreToolUse hooks intercept tool calls before execution. Use case: block refunds above a threshold and redirect to human escalation. Use case: enforce compliance rules (require manager approval for certain operations).
Fixed sequential pipeline (prompt chaining): Best for well-understood domains with predictable structure. Consistent but doesn't adapt to unexpected discoveries.
Dynamic adaptive decomposition: Explore → identify changes → divide into implementable units → implement/test/adjust. Best for open-ended investigation, unknown codebases, uncertain scope.
Attention dilution problem: Processing many files in a single pass produces inconsistent depth — detailed feedback on first 5 files, superficial on last 10. Fix: per-component passes + separate integration pass.
Rule of thumb: If a task touches >8-10 files, decompose.
| Strategy | Syntax | When |
|---|---|---|
| Resume session | claude --resume or claude --resume <name> |
Left briefly, files unchanged |
| Continue latest | claude --continue / claude -c |
Quick continuation, same directory |
| Fork session | claude --resume <name> --fork-session |
Branch without modifying original |
| Fresh start + summary | New session with context in first prompt | Next day, files changed, context degraded |
Stale context problem: After resuming post-code-modifications, Claude's context still has old file contents. Fix: tell Claude what changed, or start fresh with summary injection (more reliable).
After /compact: CLAUDE.md is re-read and re-injected. File contents from previous reads are NOT preserved — Claude must re-read files.
Multi-day session architecture:
Session 1: Research + Spec → explore domain → write spec → create tasks with acceptance criteria
Session 2: Implementation Phase 1 → "Read spec at [path], implement data layer" → commit
Session 3: Implementation Phase 2 → fresh session → "Read spec. Phase 1 done (commit abc123). Implement business logic." → commit
Session 4: Integration + Tests → fresh session → "Read spec. All layers done. Write integration tests, fix gaps."
Each session is independent. Each references durable artifacts (specs, commits, task notes), not conversation memory.
Crash recovery (Agent SDK): Each agent exports structured state to a known file location (manifest). On resume, the coordinator loads the manifest and injects it into agent prompts.
Tool descriptions are the primary mechanism Claude uses for tool selection. Vague or overlapping descriptions cause misrouting.
Misrouting fix: NOT few-shot examples, NOT routing classifiers, NOT tool consolidation. Fix: better descriptions — clearly state what each tool does, expected inputs, example queries, edge cases, and explicit boundaries vs similar tools.
Tool splitting: Split generic tools into purpose-specific ones with defined input/output contracts. Instead of analyze_document, create extract_data_points, summarize_content, validate_format.
Practical routing guidance in CLAUDE.md:
## Tool Routing
- Rust build/test: prefer rust-mcp-server over Bash
- Swift/Xcode builds: use XcodeBuildMCP
- Filesystem search: use built-in Grep/Glob, not MCP equivalentsError categories:
| Category | Example | Recovery |
|---|---|---|
| Transient | Timeout, service unavailable | Retryable |
| Validation | Wrong format, missing field | Fix input, retry |
| Business | Policy violation, resource limit | Change approach; NOT retryable |
| Permission | Access denied, auth failure | Needs escalation or different credentials |
The MCP isError flag communicates failures back to the agent, enabling structured error handling.
Critical distinction: Access failure (tool couldn't reach data source — consider retry) vs valid empty result (tool succeeded, found no matches — no retry needed).
Error propagation in multi-agent systems: Subagents implement local recovery for transient failures. Only propagate errors they can't resolve locally. Include partial results and what was attempted when propagating.
Overload problem: 18+ tools degrades selection reliability. Optimal: 4-5 tools per agent, scoped to role. Mitigations: routing in CLAUDE.md, per-subagent tool scoping via tools:/disallowedTools:, disable unused MCP servers.
tool_choice (API level):
| Value | Behavior | When |
|---|---|---|
"auto" |
Model decides tool or text | Default; general purpose |
"any" |
Model MUST call a tool, chooses which | Guaranteed structured output |
{"type": "tool", "name": "..."} |
Model MUST call this specific tool | Mandatory operations |
"none" |
Model MUST NOT call any tool | Force text-only response |
Cross-role scoped tools: For simple high-frequency operations, give a restricted tool directly to the subagent instead of routing through coordinator. Avoids round-trip latency for the 85% of cases that are simple.
Scope hierarchy:
| Level | File | Versioned? | Use for |
|---|---|---|---|
| Project | .mcp.json at repo root |
Can be | Shared team integrations |
| User | ~/.claude/settings.json |
No | Personal tools, cross-project servers |
| Managed | System directories | Admin-controlled | Org-wide policy |
Environment variable expansion: .mcp.json supports ${GITHUB_TOKEN} and ${VAR:-default} in command, args, env, url, headers.
MCP resources: Expose content catalogs (issue summaries, doc hierarchies, DB schemas) to give agents visibility into available data without exploratory tool calls.
Build-vs-use decision: Standard integrations (GitHub, Jira, Slack) → use existing community MCP servers. Team-specific workflows that community servers don't cover → build custom. When using community servers, improve tool descriptions in CLAUDE.md to prevent the agent from preferring built-in tools over richer MCP alternatives.
| Tool | Searches | Use for |
|---|---|---|
| Grep | File CONTENTS (patterns, regex) | "Find all files calling processOrder()" |
| Glob | File PATHS (name patterns) | "Find all *.test.tsx in src/" |
| Tool | Purpose | Key detail |
|---|---|---|
| Edit | Targeted text replacement | Fast, precise. Fails if match isn't unique — use more surrounding context |
| Read | Read file content | Always read before editing |
| Write | Create new file or full rewrite | Use Edit for existing files; Write for new |
When Edit fails (non-unique match): fallback to Read (load full file) + Write (rewrite with changes).
Incremental codebase understanding:
- Grep for entry points (function definitions, import statements)
- Read to follow imports and trace flows
- Do NOT read all files upfront — context budget killer
- Use Agent (Explore type) for broad exploration
| Level | Location | Loaded when | Versioned? |
|---|---|---|---|
| User | ~/.claude/CLAUDE.md |
Always, every project | No — personal only |
| Project | CLAUDE.md or .claude/CLAUDE.md at repo root |
In this project | Yes — shared with team |
| Directory | path/to/dir/CLAUDE.md |
Lazy-loaded when Claude reads files in that directory | Yes |
| Managed | System-level paths | Always | Admin-controlled |
Modular organization: @import syntax references external files from CLAUDE.md (relative and absolute paths, max 5 recursion hops). .claude/rules/ directory for per-topic rule files. /memory command to check loaded memory files.
Directory-level CLAUDE.md = conditional context loading. Zero token cost when Claude isn't working in that directory.
| Location | Scope | Shared? |
|---|---|---|
.claude/commands/ |
Project-scoped | Yes (versioned) |
~/.claude/commands/ |
Personal | No |
.claude/skills/ |
Project-scoped, rich YAML frontmatter | Yes |
~/.claude/skills/ |
Personal skills | No |
Commands and skills are unified. A file in .claude/commands/deploy.md and a skill in .claude/skills/deploy/SKILL.md both create /deploy.
Skill frontmatter options:
---
name: research
description: "Explore codebase structure before implementation"
context: fork # Runs in isolated subagent context
allowed-tools: Read, Grep, Glob, Agent
argument-hint: "feature area or file pattern"
model: opus
disable-model-invocation: true # User-only invocation
---Available fields: name, description, context, allowed-tools, argument-hint, model, disable-model-invocation, user-invocable, agent, hooks.
Key distinction: CLAUDE.md = always-loaded universal standards. Skills = on-demand task-specific workflows. Don't put task-specific procedures in CLAUDE.md. Don't put universal standards only in skills.
.claude/rules/ files with YAML frontmatter:
---
paths: ["**/*.rs"]
---
Use Result<T, E> for all fallible operations. No unwrap() in production code.Advantage over directory CLAUDE.md: Glob patterns match files across the ENTIRE codebase. **/*.test.tsx captures every test file regardless of directory. Directory CLAUDE.md only applies to files in that single directory.
Rules without paths: load unconditionally (equivalent to CLAUDE.md content). Also supported at ~/.claude/rules/.
Activation: Shift+Tab (toggles Normal → Accept Edits → Plan), claude --permission-mode plan (CLI flag at start).
Use plan mode: Complex large-scale changes, multiple valid approaches needing trade-off evaluation, architectural decisions, multi-file modifications.
Use direct execution: Well-understood changes with clear limited scope, single-file bug fixes, known correct approach.
Combination pattern: Phase 1: Plan mode → investigate, design, produce plan. Phase 2: Direct execution → implement.
Explore subagent: Isolates verbose discovery output from the main conversation. Returns summaries, preserving main context. context: fork in skill frontmatter achieves the same isolation.
Session isolation insight: An independent review instance catches more problems than self-review in the same session due to retained reasoning context bias.
Technique hierarchy (ordered by effectiveness):
- Concrete input/output examples (2-3 before/after examples): outperforms prose descriptions always
- Test-driven iteration: write tests first, share failures to guide improvements — model corrects code to pass the test, not to match a vague description
- Interview pattern: have Claude ask before implementing — surfaces hidden assumptions
When to bundle vs sequence feedback: Single message when corrections interact (changing a function signature affects three call sites). Sequential iteration when problems are independent.
Example-based communication: When prose descriptions are interpreted inconsistently, switch to concrete examples. Instead of "Generate structs following our serialization conventions", show 2-3 correctly structured code examples. The model generalizes from examples more reliably than from descriptions.
-p flag: Runs Claude Code in non-interactive mode (print mode). Without it, CI jobs hang waiting for interactive input.
claude -p "Analyze this pull request for architectural issues"Structured output for CI: --output-format json (full conversation log), --output-format stream-json (real-time JSON objects), --json-schema '{...}' (validate output against schema, print mode only).
CLAUDE.md for CI: Document test standards, valuable test criteria, available fixtures. Without this, test generation produces low-value boilerplate.
Incremental review context: When re-running reviews after new commits, include prior review findings. Instruct Claude to report ONLY new or still-unresolved issues.
Wrong: "Be conservative." "Only report high-confidence findings." Right: "Flag comments only when stated behavior contradicts actual code behavior. Do not flag style issues."
Define exactly which problems to report vs skip, with concrete code examples for each severity level.
False positive confidence problem: High false positive rates in one category destroy trust in ALL categories. Fix: temporarily disable high-FP categories while improving prompt precision.
2-4 targeted examples showing handling of ambiguous cases with reasoning about why one action was chosen over alternatives.
When to apply: Instructions alone produce inconsistent formatting, model makes inconsistent judgments on ambiguous cases, extraction tasks produce empty/null fields for existing info.
How to build: Target ambiguous scenarios specifically. Each example shows REASONING about why an approach was chosen. Teaches generalization, not pattern-matching.
Hallucination reduction: Few-shot examples showing correct handling of variable document structures (including cases where information is genuinely absent) reduce fabrication.
Reliability hierarchy: tool_use with JSON schemas > prompt-based JSON (may be malformed).
What tool_use does NOT prevent: Semantic errors (line items don't sum to stated total), field misplacement (values in wrong fields), fabrication (model invents values for required fields).
Schema design principles:
- Optional/nullable fields when source data may not contain the value — PREVENTS fabrication
- Enum value
"unclear"for ambiguous cases "other"+ freeform detail string for extensible categorization- Format normalization rules in prompts alongside strict schemas
- Claude generates output
- Validation runs (build, test, schema check)
- If fails: send back original document + failed extraction + specific validation errors
- Model uses error for self-correction
Effective for: Format mismatches, structural output errors, misplaced values, type errors. Ineffective for: Genuinely missing information from source document.
Self-correction flows: Extract calculated_total alongside stated_total to flag discrepancies. Add conflict_detected booleans for inconsistent source data. Let downstream systems handle conflicts instead of silently picking one value.
Message Batches API: 50% cost savings, up to 24-hour processing window, no guaranteed latency SLA, uses custom_id for request/response correlation. Supports all Messages API features including tool use. Does NOT support streaming.
Matching rule: Synchronous API for blocking workflows (pre-merge checks, anything devs wait on). Batch API for latency-tolerant workflows (overnight reports, weekly audits).
Self-review limitation: Same-session model retains reasoning context, less likely to question its own decisions. Independent instance catches more subtle problems.
Multi-pass architecture: Per-file analysis passes (consistent depth per file) + separate cross-file integration pass (captures data flow issues between files).
Confidence-based routing: Model self-reports confidence per finding. Route low-confidence findings to human review. Calibrate thresholds using labeled validation sets (ground truth data). Prioritize limited reviewer capacity on the highest-uncertainty items.
Progressive summarization trap: As conversations grow, Claude Code compresses earlier messages. Numeric values, dates, constraint parameters, file paths get lost or distorted.
Fix: persistent "case facts" block. Extract transactional facts to a file. Reference it in every prompt.
# Session Facts
- API endpoint: /v2/schedules/optimise
- Max batch size: 500 records
- Timeout: 60 seconds
- Required output fields: schedule_id, assigned_resource, start_time, end_time
- Constraint: no resource assigned to overlapping time windows"Lost in the middle" effect: Models process beginning and end of long inputs reliably. Middle findings may be lost. Fix: place key finding summaries at the beginning. Use explicit section headers.
Tool result trimming: When a tool returns 40+ fields and you need 5, trim verbose results to relevant fields BEFORE adding to context.
Three valid escalation triggers:
- User explicitly requests a human — honor immediately
- Policy exceptions or gaps — request falls outside documented policy
- Inability to make meaningful progress
Two unreliable triggers (exam traps): Sentiment-based escalation (frustration ≠ complexity), self-reported confidence scores (model is often incorrectly confident or uncertain).
Ambiguous matching: When multiple records match a query (e.g., multiple customers with same name), ask for additional identifiers. Do NOT select based on heuristics.
Structured error context must include: Failure type, what was attempted, partial results gathered, potential alternative approaches.
Two anti-patterns:
- Silent suppression: Empty results marked as success. Prevents any downstream recovery.
- Workflow termination: Kill entire pipeline on single failure. Propagate structured error context and continue with partial results.
Degradation symptoms: Model references "typical patterns" instead of specific code, re-reads already-read files, makes confident but subtly wrong claims, generates code contradicting earlier decisions.
Mitigation strategies:
- Scratchpad files — Write key findings to a file. Reference for subsequent work. Survives compression.
- Subagent delegation — Spawn subagents for specific investigations. Verbose output fills subagent context, not yours.
- Summary injection — Summarize one phase's findings before spawning subagents for the next.
/compact— Proactively compress when context fills. CLAUDE.md re-injected; file contents from prior reads are NOT.- Fresh sessions at natural boundaries — When finishing one layer and starting another, start a new session.
Structured claim-source mappings: Each finding includes: claim + source URL + document name + relevant snippet + publication date. Downstream agents preserve and merge mappings during synthesis.
Conflict handling: Two credible sources report different stats. Do NOT arbitrarily pick one. Annotate with both values and source attribution.
Temporal awareness: Require publication/collection dates in structured outputs. Different dates explain different numbers (not contradictions).
Content-appropriate rendering: Financial data → tables. Narrative content → prose. Technical findings → structured lists. Don't flatten everything into a uniform format.
The metrics trap: 97% overall accuracy can hide 40% error rate in a specific document type or field segment. Always validate accuracy per document type AND per field segment before automating.
Stratified random sampling: Sample high-confidence extractions for ongoing verification. Detects new error patterns that would otherwise pass unnoticed.
Never ask Claude Code to implement something unspecified. "Build the scheduling system" = project killer. "Implement the optimization model per @doc/specs/scheduling, section 3.2" = project saver.
Every session ends with a commit. Every new session starts from committed state. Bad session → git reset to last good commit → fresh session.
Write the test that proves the feature works. Give Claude the failing test. Claude writes code until the test passes. Deterministic. "Implement the feature" is probabilistic.
Don't let the main agent explore for 20 turns. Spawn a subagent: "Explore how the data pipeline works. Return: entry points, data flow, key types, extension points." Main agent gets a compact summary. Context preserved.
Don't dump all project context in every prompt. Load only what's needed:
- CLAUDE.md: universal rules (always loaded)
- Path rules: per-file-type conventions (loaded when editing matching files)
- Skills: task-specific workflows (loaded on invocation)
- Specs: domain context (loaded via Read when needed)
PROJECT START
├── Session: Research & Spec
│ ├── Read existing code (use Explore subagent)
│ ├── Write spec (durable artifact)
│ └── Create tasks with acceptance criteria
├── Session: Implementation (per component, ≤8 files each)
│ ├── Read spec + task → implement → test → commit
│ └── Update task progress notes
├── Session: Integration
│ ├── Wire components → run full test suite → commit
└── Session: Review (independent instance)
├── Fresh context — no implementation bias
└── Verify against spec and acceptance criteria
Each session references durable artifacts. No session depends on another session's conversation memory. Any session can fail and restart from last commit.
Before starting a complex feature:
- Spec exists as durable artifact (file, doc system, or committed markdown)
- Tasks exist with acceptance criteria
- CLAUDE.md has relevant architectural context for this area
- Directory CLAUDE.md and/or path-specific rules exist for specialized code
- Tests exist (or will be written first) for validation
- Pre-commit hooks enforce build/lint/test
- Decomposition produces tasks touching ≤8 files each
- Each session has a clear, bounded objective
- Critical domain values are in files, not just conversation
| Situation | Action |
|---|---|
| Financial/security/compliance stakes | Programmatic enforcement (hooks), NOT prompts |
| Tool misrouting | Improve descriptions, NOT few-shot examples |
| Partial coverage | Trace to coordinator decomposition, NOT downstream agents |
| CI hangs | -p flag for non-interactive mode |
| Self-review misses problems | Independent review instance |
| Stale session | Fresh start + summary injection, NOT resume |
| Attention dilution | Per-file passes + integration pass, NOT single pass |
| Required schema fields when data may be absent | Use optional/nullable fields to prevent fabrication |
| Multiple record matches | Ask for additional identifiers, NOT heuristics |
| Ambiguous/silent policy | Escalate, NOT attempt autonomous resolution |
| Batch API for everything | Synchronous for blocking workflows, batch for latency-tolerant only |
| Generic "search unavailable" error | Structured error context with failure type, query attempted, partial results |
Based on https://x.com/hooeem/article/2033198345045336559