A Study Guide for Building on Claude
The knowledge you need to design on Claude. What this guide covers The Claude Certified Developer – Foundations (CCD-F) exam is scenario-based. You’re the lead engineer on a new project with constraints (late
The knowledge you need to design on Claude.
What this guide covers
The Claude Certified Developer – Foundations (CCD-F) exam is scenario-based. You’re the lead engineer on a new project with constraints (latency, volume, budget, compliance, autonomy), and you choose the model, API features and architecture. The right answer is the simplest design that meets every constraint.
Outline
- The API and SDKs: the stack, the agent loop, tools and MCP, and settings
- Delivery and reliability: streaming, batching, rate limits, errors and retries
- Caching and costs: what you pay for, how the cache works, and how to pay less
- Prompt engineering: the anatomy of a request, techniques and structured outputs
- Context engineering: subagents, compaction, context editing and memory
- Agent patterns: from single calls to orchestrators and agents
- Evals and safety: measuring quality, versioning prompts, prompt injection, agent permissions, keys and data residency
Module 1: The API and SDKs
Every option below makes the same stateless API call. What changes is how much of the work around it your code does.
The stack
Each layer moves the line between your code and the platform. The split to remember: with the Client SDK your code runs the loop and the tools. With the Agent SDK, the SDK does.
The agent loop
At any layer, an agent is this loop: call the model, read stop_reason, act on it, call again.
-
Append, never edit. Add the assistant turn exactly as returned, thinking blocks included, then one user message with every
tool_result, matched bytool_use_id. To change the rules later, append a mid-conversation system message: editingsystem,toolsor earlier turns misses the cache, and the API can reject the earlier thinking blocks with a 400. -
Read blocks by
type. A reply’scontentis a list of blocks (thinking,text,tool_use), and it can start with a thinking block. Socontent[0].textbreaks: loop overcontentand keep the blocks whosetypeis"text". -
Cap the loop with a turn or token limit. The tool runner (beta) runs this loop for you, capped by
max_iterations. -
Run independent tools in parallel. Claude can request several tools in one reply. Run them together and return every
tool_resultin one user message. -
Report tool failures, don’t crash. When a tool fails, return its
tool_resultwithis_error: trueand a short message. Claude can then retry or try another way.
Tools and MCP
A tool has three parts: a name, a description and an input_schema (JSON Schema). Set strict: true on the tool to guarantee that Claude’s inputs always match that schema, with no missing fields or wrong types.
Claude decides when to call a tool from its description, which the docs call “by far the most important factor in tool performance.”
- Be detailed. Cover what the tool does, when to use it and when not to, what each parameter means, and its limits.
-
Make tools easy to tell apart. Group related actions into one tool with an
actionparameter, and prefix names by service, such asgithub_list_prs.
| Kind | Who runs it | Examples |
|---|---|---|
| Client tools | Your code: you get tool_use, you return tool_result
|
Your functions, bash, text_editor, memory
|
| Server tools | Anthropic: results arrive in the reply |
web_search, web_fetch, code_execution
|
| MCP tools | An MCP server | GitHub, databases, Slack |
MCP (Model Context Protocol) is an open standard for plugging tool servers into an agent.
| Connected from | How | Limits |
|---|---|---|
| Messages API | MCP connector (beta) | Remote HTTPS servers, tool calls only, you supply the token |
| Agent SDK |
mcp_servers or .mcp.json
|
Local, remote or in-process. Allow each tool in allowed_tools
|
With many tools, tool search loads definitions on demand instead of filling the context. It’s on by default in the Agent SDK.
Extending an agent
Each extension point changes something different: what Claude knows, what it can reach, or what always happens.
| Mechanism | What it is | Use it when |
|---|---|---|
| CLAUDE.md | Instructions loaded at the start of every session | Project rules that always apply |
| Rules | Markdown files in .claude/rules/, loaded like CLAUDE.md, or only when Claude reads files matching their paths
|
Rules for one part of the codebase, such as the API folder |
| Skill | Instructions, scripts and files Claude loads on demand, name and description first | A reusable procedure Claude should pick up when it’s relevant |
| MCP server | Tools, resources and prompts exposed through a standard protocol | Connecting external systems and data |
| Hook | A script, HTTP endpoint, MCP tool or prompt, run automatically at events like PreToolUse or PostToolUse
|
Steps that must happen at a fixed point, whatever the model decides. For a hard allow or deny, use permission rules |
| Subagent | A separate context with its own prompt and tools | Noisy side work kept out of the main context |
| Plugin | A package of skills, subagents, hooks and MCP servers, installed from a marketplace (a CLAUDE.md inside it isn’t loaded) | Sharing one setup across teams |
Settings: who overrides whom
Claude Code and the Agent SDK read settings from up to five files, so the same key can be set in more than one place. The higher level wins.
Module 2: Delivery and reliability
Module 1 covered a single call. In production you also choose how replies arrive, and you plan for limits and failures. Pick the delivery mode by who is waiting.
Streaming
Streaming returns the reply as server-sent events, so users see text as it is generated. The events always arrive in the same order.
The SDK requires streaming when a request may run long. If you only need the final result, use messages.stream() with get_final_message().
Batching
When nobody is waiting, the Batch API runs requests asynchronously at half price.
Results can come back in any order. Give each request a unique custom_id and match results on it, never on position.
Rate limits
| Limit | Counts | Good to know |
|---|---|---|
| RPM | Requests per minute | Set per model class |
| ITPM | Input tokens per minute | Cache reads don’t count on most models, so caching raises throughput too |
| OTPM | Output tokens per minute | Counts tokens generated, not your max_tokens
|
-
On a 429, wait the seconds in
retry-after. A 429 without it means the spend cap is reached, and retrying won’t help. -
Read the headers.
anthropic-ratelimit-*show each limit, what’s left and when it resets. - Limits are per organization and refill continuously (a token bucket), not once a minute. Workspaces can have lower limits of their own.
- Scale gradually. Limits rise with your usage tier, and a sudden spike can hit acceleration limits.
Errors: transient vs. permanent
- Back off with jitter. Retry transient errors only.
-
Cap it. Limit both attempts and total time, and set
max_retries=0if you add your own loop. -
Degrade under load. Log the
request-id, and under sustained 529s queue or shed work instead of hammering the API.
Module 3: Caching and costs
You pay per token, and output costs more than input. Prompt caching is the biggest single lever on input cost.
What is billed
| What | Counts | Price |
|---|---|---|
| Input tokens | Everything you send: system, tools, history, documents | The base rate |
| Output tokens | Everything generated, including thinking you never see | 5 times the input rate |
| Cache writes | The prefix you mark, the first time | More than input, once |
| Cache reads | The same prefix, reused | A small fraction of input |
| Server tools | Web search | Per search, plus its tokens |
Two traps: thinking is billed as output even when hidden, and newer models use a tokenizer that produces up to about 35% more tokens for the same text.
Prompt caching
Mark a cache_control breakpoint, and on the next call everything before it is served from cache.
- A hit needs an identical prefix. The API looks back up to 20 blocks from each breakpoint for a match.
- Short prompts fail silently. Below the minimum (512 tokens on the newest models, 1,024 to 4,096 on older ones) nothing is cached and no error is returned.
-
Automatic mode (a top-level
cache_control) moves the breakpoint forward as the conversation grows. It uses one of the 4 slots. - A write costs more than no cache, so it pays off only if the prefix is read before it expires. Caching stacks with the batch discount.
What breaks the cache
The prefix runs tools, then system, then messages. A change invalidates its own level and every level after it.
Other levers
| Lever | What it saves | How |
|---|---|---|
| Smaller model | Every token costs less | Route simple, high-volume inputs to a smaller model and keep the larger one for hard cases (Module 6) |
| Lower effort | Thinking tokens, billed as output | Defaults differ by model, so set effort yourself and lower it until your evals show a drop |
| Batch API | Half the price of input and output | Send anything nobody is waiting for. The discount stacks with caching |
| Less context | Input you resend on every turn | Trim history, clear old tool results, hand side work to subagents (Module 5) |
| Caps | A runaway loop or reply |
max_tokens caps each reply. In the Agent SDK, max_turns and max_budget_usd cap each run |
Measuring spend
| Field | Tells you |
|---|---|
| Token counting | Input tokens, before you send |
input_tokens |
Tokens after the last breakpoint, full price |
cache_creation_input_tokens |
Tokens written to the cache, write price |
cache_read_input_tokens |
Tokens read from the cache, read price |
output_tokens |
Output, thinking included |
total_cost_usd |
Cost of a whole Agent SDK run |
Both cache fields at 0 on a repeated call means the prefix changed or is under the minimum.
Module 4: Prompt engineering
Write the prompt as a spec for a contractor who has never seen your codebase. If a colleague with no context would be confused, Claude will be too.
Anatomy of a request
A request has four places to put instructions, and each technique below has a natural home in one of them.
Techniques
| Technique | Do | Avoid |
|---|---|---|
| Clear and direct | Say what done looks like and why each rule exists. Ask for the action: “Change this function to…” | “Format the code properly”, “Can you suggest changes to…” |
| Positive, calm wording | “Write in flowing prose”, “Use this tool when…”. Write the prompt in the style you want back | “Don’t use markdown”, “CRITICAL: you MUST…” |
| Scope | Say how far to go: “Go beyond the basics” or “Keep changes minimal” | An open-ended “improve this” |
| Role and examples | One-sentence role in system. 3 to 5 varied examples in <example> tags |
Near-identical examples Claude copies |
| Structure | Consistent XML tags. Documents at the top, question at the end, quotes first | Data and instructions mixed, the question above long input |
| Reasoning | Raise effort and ask in general terms (“think it through”). If it overthinks: “Choose an approach and commit to it” |
A hand-written step-by-step plan |
| Grounding | “Never speculate about code you have not opened.” “Before you finish, verify against…” | Answers about files it hasn’t read |
| Tools and steps | Tool descriptions say what, when and when not, and each parameter. Separate calls when you must check each step (Module 6) | One-line descriptions, one giant prompt for a multi-stage task |
Putting the question last can improve answers by up to 30% on long, multi-document inputs (Anthropic’s tests).
Structured outputs
| Setting | What it guarantees |
|---|---|
output_config.format with a JSON schema |
The reply always parses against the schema |
strict: true on a tool |
Tool inputs always match the tool’s schema |
additionalProperties: false |
Required on every object in a strict schema |
Still validate before passing output downstream: a refusal or a max_tokens cutoff can leave output that doesn’t match the schema.
Don’t ask the model to write out its reasoning, in a field or in <thinking> tags: it can be declined with a reasoning_extraction refusal. Raise effort instead.
Module 5: Context engineering
Prompt engineering is what you say. Context engineering is what the model sees at each step. The window is limited working memory: every token costs on every turn, and quality drops as it fills.
The toolkit
| Technique | What it does |
|---|---|
| Subagent | Does noisy work in its own context and returns a summary |
| Compaction | Replaces old history with a summary |
| Fresh context | Starts a fresh context between unrelated tasks |
| Context editing | Clears the oldest tool results or thinking blocks |
| Memory tool | Saves notes outside the window to read back later |
| Just-in-time loading | Keeps paths and IDs in context, loads content on demand |
| Progressive disclosure | Loads a skill’s name and description first, its instructions when triggered, its files only when used |
Subagents
A subagent spends its own context and returns only a summary. That costs more tokens overall, so use one when keeping the main context clean is worth it.
Compaction and context editing
-
Compaction is opt-in on the Messages API, and Anthropic’s recommended default for long conversations. In the Agent SDK and Claude Code it runs automatically, and
/compact <focus>steers what’s kept. It loses detail, so anything that must survive goes in memory or CLAUDE.md.
Context editing (beta) runs on the server, so your client keeps the full history.
Module 6: Agent patterns
Anthropic’s rule from Building effective agents: start with the simplest design, and add complexity only when evals show it helps.
The patterns
Each LLM box below is an augmented LLM: one model call with retrieval, tools and memory. Often that single call is enough.
What separates the patterns is who decides the steps. In the first five, the workflows, your code fixes the shape. In the agent loop, the model picks each step. Since any box can itself be an agent loop, the bottom row shows ways to coordinate several of them.
When a lead agent delegates, give each subagent a budget, an output schema and a clear handoff, and check outputs before passing them on.
Pipeline vs. graph
Draw any pattern as boxes and arrows and you get one of three shapes. In a pipeline or a static graph, your code fixes every step and branch before the run starts. In a dynamic graph, the model picks the next step while it runs, so you only see the full shape afterward.
Module 7: Evals and safety
A lead engineer has to answer two questions before shipping: how do we know it works, and what is the worst it can do?
Evals
Good criteria are specific, measurable, achievable and relevant (“accurate sentiment classification”, not “good performance”). Most systems need several: task fidelity, consistency, tone, privacy, latency and price.
| Grading | Use for | Trade-off |
|---|---|---|
| Code-based | Labels, formats, exact answers | Fastest, most reliable and most scalable, but can’t judge nuance |
| LLM-based | Tone, relevance, leaks of private data | Handles complex judgments. Test its reliability first, then scale. Give it a clear rubric and use a different model than the one graded |
| Human | Only what automation can’t judge | Most flexible and highest quality, but slow and expensive. Avoid if possible |
Prefer many auto-graded cases over a few hand-graded ones, and include edge cases: ambiguous, overlong, irrelevant and harmful inputs. Re-run the set when you change the prompt, the model or effort.
For coding agents, ask for a general solution and for wrong tests to be reported, never hardcoded around.
Evals are one layer of testing. The code around the model needs the usual layers too.
| Layer | Tests | When |
|---|---|---|
| Unit | Parsing, tool handlers and loop logic, against a mocked API | Every commit |
| Integration and end-to-end | The Claude integration with real API calls (tools, schemas, streaming, errors), and critical user flows end to end | Before merge, on a fixed set |
| Evals | Output quality on a graded dataset | In CI, on every prompt change and model upgrade |
| Production | Monitoring for drift and real-world failures, user feedback, sampled transcript review, A/B tests for big changes | After launch, ongoing |
| Human review | Calibrate LLM graders, judge subjective output | Periodically |
To debug a change such as new refusals, start from the traces of the failing requests (input, system prompt, context, stop_reason), then compare them with past successes.
Versioning prompts
A prompt is code: it changes behavior, so it needs history, review and rollback. The Console Playground, which replaced the Workbench, doesn’t store prompts or versions, so your repository is the source of truth.
| Version this | Why |
|---|---|
| The prompt text, in git, with a version ID | Review changes in pull requests and roll back a bad one |
The whole request: model ID, effort, tools, schemas |
A prompt only behaves the same with the same settings |
| The model ID, pinned | IDs from the 4.6 generation on are fixed snapshots, but each has its own retirement date |
| The eval score of each version | Promote a version only when its score holds or improves |
The prompt version and request-id in every log line |
Trace a bad answer back to the exact prompt that produced it |
Roll a new version out gradually, behind a flag or a percentage, and keep the previous one ready. Expect a burst of cache writes after each deploy, since a changed prefix starts a new cache. On a model upgrade, adapt the prompt to the new model and re-run evals before switching. When a parameter is deprecated, add regression tests for its current behavior, then migrate call sites in batches validated against them, well before the removal date.
In CI, run Claude Code headless with claude --bare -p "..." --output-format json. --bare skips local hooks, plugins, MCP servers and CLAUDE.md, so every run is the same, and --allowedTools limits what the job can touch.
Prompt injection
The second question is what the worst case is. Jailbreaks and prompt injection try to make Claude ignore your instructions, and each source of attack needs its own defense.
| Threat | Mitigation |
|---|---|
| Direct: the user is the attacker | Screen input with a small model such as Haiku and structured outputs, and treat a refusal from the screen as a harmful verdict. State boundaries and how to refuse in system. Throttle repeat offenders |
| Indirect: content the agent reads is the attacker |
Treat tool content as data, never as commands, and say so in system. Put third-party content only in tool_result blocks, labelled with its source and JSON-encoded. Keep your own instructions in the user turn after it. Screen tool output with a small classifier |
| Too much power | Least privilege: no secrets it doesn’t need, sandboxed tools, narrow permissions. Red-team with planted injections before launch, then monitor outputs for injections that got through |
For rules that must never break, filter Claude’s output in code before it reaches users. A prompt alone is not enforcement.
Agent SDK permissions
The last line of defense is what the agent is allowed to do. In the Agent SDK, every tool call passes these checks in order, and the first that decides wins:
| Mode | Behavior |
|---|---|
default |
Nothing approved by the mode. Unmatched calls go to canUseTool
|
dontAsk |
Anything that would prompt is denied: for headless agents with a fixed tool list |
acceptEdits |
Approves file edits and filesystem commands inside the working directory |
plan |
Explores and plans. File edits and shell writes always go to canUseTool
|
auto |
A model classifier approves or denies prompts |
bypassPermissions |
Approves everything that reaches the mode step. Sandboxes only |
Rules, modes and callbacks interact in ways that are easy to miss. allowed_tools pre-approves the tools you list but doesn’t restrict bypassPermissions, which still approves everything else. Calls approved by a mode or an allow rule never reach canUseTool, so put checks that must always run in a PreToolUse hook, and back hard limits with deny rules. Subagents inherit the parent’s mode, so a bypassPermissions parent hands every subagent full access. And a scoped deny such as Bash(rm *) only blocks calls written that way (/bin/rm gets through), while a bare name such as Bash in disallowed_tools removes the tool entirely. For a locked-down headless agent, pair allowed_tools with dontAsk.
Scope each hook with a matcher to the risky tools, such as Bash, Write or sensitive data access, so safe calls don’t pay for the check.
Permissions stop what the agent can do. In the prompt, also ask it to confirm before anything irreversible or shared, such as git push --force or dropping a table.
Keys and data residency
Give each environment (dev, staging, prod) its own API key, as recommended: a leaked key can then be revoked without stopping the others. Keep keys in a secret manager, never in code or prompts, and rotate them.
The Claude API can keep inference in the US (inference_geo: "us", at 1.1× the price) but has no EU option, and workspace data is stored in the US. If data must stay in the EU, call Claude through Amazon Bedrock or Google Cloud (Google managed) in an EU region, where the endpoint sets the region. Zero data retention is enabled per organization through Anthropic sales, and it doesn’t cover every feature: the Batch API, code execution and the Files API keep data.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.















