<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anthony</title>
    <description>The latest articles on DEV Community by Anthony (@anthonyverriest).</description>
    <link>https://dev.to/anthonyverriest</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4230006%2F8da2a593-abd9-49e4-ac94-0e4ca7d861cb.png</url>
      <title>DEV Community: Anthony</title>
      <link>https://dev.to/anthonyverriest</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anthonyverriest"/>
    <language>en</language>
    <item>
      <title>A Study Guide for Building on Claude</title>
      <dc:creator>Anthony</dc:creator>
      <pubDate>Mon, 09 Nov 2026 09:42:23 +0000</pubDate>
      <link>https://dev.to/anthonyverriest/a-study-guide-for-building-on-claude-4ecf</link>
      <guid>https://dev.to/anthonyverriest/a-study-guide-for-building-on-claude-4ecf</guid>
      <description>&lt;p&gt;&lt;em&gt;The knowledge you need to design on Claude.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What this guide covers
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outline&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The API and SDKs:&lt;/strong&gt; the stack, the agent loop, tools and MCP, and settings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery and reliability:&lt;/strong&gt; streaming, batching, rate limits, errors and retries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching and costs:&lt;/strong&gt; what you pay for, how the cache works, and how to pay less&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt engineering:&lt;/strong&gt; the anatomy of a request, techniques and structured outputs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context engineering:&lt;/strong&gt; subagents, compaction, context editing and memory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent patterns:&lt;/strong&gt; from single calls to orchestrators and agents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evals and safety:&lt;/strong&gt; measuring quality, versioning prompts, prompt injection, agent permissions, keys and data residency&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Module 1: The API and SDKs
&lt;/h2&gt;

&lt;p&gt;Every option below makes the same stateless API call. What changes is how much of the work around it your code does.&lt;/p&gt;

&lt;h3&gt;
  
  
  The stack
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5go700mribs5nsrobg4l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5go700mribs5nsrobg4l.png" alt="Each layer moves the line between your code and the platform" width="799" height="573"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each layer moves the line between your code and the platform. The split to remember: with the &lt;strong&gt;Client SDK&lt;/strong&gt; your code runs the loop and the tools. With the &lt;strong&gt;Agent SDK&lt;/strong&gt;, the SDK does.&lt;/p&gt;

&lt;h3&gt;
  
  
  The agent loop
&lt;/h3&gt;

&lt;p&gt;At any layer, an agent is this loop: call the model, read &lt;code&gt;stop_reason&lt;/code&gt;, act on it, call again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1wqkcts0k8m68eo91fg5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1wqkcts0k8m68eo91fg5.png" alt="Your code runs the loop, and stop_reason says what happens next" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Append, never edit.&lt;/strong&gt; Add the assistant turn exactly as returned, thinking blocks included, then one user message with every &lt;code&gt;tool_result&lt;/code&gt;, matched by &lt;code&gt;tool_use_id&lt;/code&gt;. To change the rules later, append a mid-conversation system message: editing &lt;code&gt;system&lt;/code&gt;, &lt;code&gt;tools&lt;/code&gt; or earlier turns misses the cache, and the API can reject the earlier thinking blocks with a 400.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read blocks by &lt;code&gt;type&lt;/code&gt;.&lt;/strong&gt; A reply’s &lt;code&gt;content&lt;/code&gt; is a list of blocks (&lt;code&gt;thinking&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;tool_use&lt;/code&gt;), and it can start with a thinking block. So &lt;code&gt;content[0].text&lt;/code&gt; breaks: loop over &lt;code&gt;content&lt;/code&gt; and keep the blocks whose &lt;code&gt;type&lt;/code&gt; is &lt;code&gt;"text"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cap the loop&lt;/strong&gt; with a turn or token limit. The tool runner (beta) runs this loop for you, capped by &lt;code&gt;max_iterations&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run independent tools in parallel.&lt;/strong&gt; Claude can request several tools in one reply. Run them together and return every &lt;code&gt;tool_result&lt;/code&gt; in one user message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Report tool failures, don’t crash.&lt;/strong&gt; When a tool fails, return its &lt;code&gt;tool_result&lt;/code&gt; with &lt;code&gt;is_error: true&lt;/code&gt; and a short message. Claude can then retry or try another way.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tools and MCP
&lt;/h3&gt;

&lt;p&gt;A tool has three parts: a &lt;code&gt;name&lt;/code&gt;, a &lt;code&gt;description&lt;/code&gt; and an &lt;code&gt;input_schema&lt;/code&gt; (JSON Schema). Set &lt;code&gt;strict: true&lt;/code&gt; on the tool to guarantee that Claude’s inputs always match that schema, with no missing fields or wrong types.&lt;/p&gt;

&lt;p&gt;Claude decides when to call a tool from its description, which the docs call “by far the most important factor in tool performance.”&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Be detailed.&lt;/strong&gt; Cover what the tool does, when to use it and when not to, what each parameter means, and its limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make tools easy to tell apart.&lt;/strong&gt; Group related actions into one tool with an &lt;code&gt;action&lt;/code&gt; parameter, and prefix names by service, such as &lt;code&gt;github_list_prs&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Kind&lt;/th&gt;
&lt;th&gt;Who runs it&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Client tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Your code: you get &lt;code&gt;tool_use&lt;/code&gt;, you return &lt;code&gt;tool_result&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Your functions, &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;text_editor&lt;/code&gt;, &lt;code&gt;memory&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Anthropic: results arrive in the reply&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;web_search&lt;/code&gt;, &lt;code&gt;web_fetch&lt;/code&gt;, &lt;code&gt;code_execution&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An MCP server&lt;/td&gt;
&lt;td&gt;GitHub, databases, Slack&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;MCP&lt;/strong&gt; (Model Context Protocol) is an open standard for plugging tool servers into an agent.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Connected from&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;th&gt;Limits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Messages API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MCP connector (beta)&lt;/td&gt;
&lt;td&gt;Remote HTTPS servers, tool calls only, you supply the token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent SDK&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;mcp_servers&lt;/code&gt; or &lt;code&gt;.mcp.json&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Local, remote or in-process. Allow each tool in &lt;code&gt;allowed_tools&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With many tools, &lt;strong&gt;tool search&lt;/strong&gt; loads definitions on demand instead of filling the context. It’s on by default in the Agent SDK.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extending an agent
&lt;/h3&gt;

&lt;p&gt;Each extension point changes something different: what Claude knows, what it can reach, or what always happens.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;th&gt;Use it when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CLAUDE.md&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instructions loaded at the start of every session&lt;/td&gt;
&lt;td&gt;Project rules that always apply&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rules&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Markdown files in &lt;code&gt;.claude/rules/&lt;/code&gt;, loaded like CLAUDE.md, or only when Claude reads files matching their &lt;code&gt;paths&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Rules for one part of the codebase, such as the API folder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skill&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instructions, scripts and files Claude loads on demand, name and description first&lt;/td&gt;
&lt;td&gt;A reusable procedure Claude should pick up when it’s relevant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tools, resources and prompts exposed through a standard protocol&lt;/td&gt;
&lt;td&gt;Connecting external systems and data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hook&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A script, HTTP endpoint, MCP tool or prompt, run automatically at events like &lt;code&gt;PreToolUse&lt;/code&gt; or &lt;code&gt;PostToolUse&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Steps that must happen at a fixed point, whatever the model decides. For a hard allow or deny, use permission rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subagent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A separate context with its own prompt and tools&lt;/td&gt;
&lt;td&gt;Noisy side work kept out of the main context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plugin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A package of skills, subagents, hooks and MCP servers, installed from a marketplace (a CLAUDE.md inside it isn’t loaded)&lt;/td&gt;
&lt;td&gt;Sharing one setup across teams&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Settings: who overrides whom
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvd3vg3hktmho8ibnkngy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvd3vg3hktmho8ibnkngy.png" alt="A key set at a higher level wins" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Module 2: Delivery and reliability
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8syvb1pfwndn98h0038v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8syvb1pfwndn98h0038v.png" alt="Pick the delivery mode by who is waiting" width="799" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Streaming
&lt;/h3&gt;

&lt;p&gt;Streaming returns the reply as server-sent events, so users see text as it is generated. The events always arrive in the same order.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxg6exvyafw5047pzd2z5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxg6exvyafw5047pzd2z5.png" alt="A stream is a fixed sequence of events" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The SDK requires streaming when a request may run long. If you only need the final result, use &lt;code&gt;messages.stream()&lt;/code&gt; with &lt;code&gt;get_final_message()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Batching
&lt;/h3&gt;

&lt;p&gt;When nobody is waiting, the Batch API runs requests asynchronously at half price.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm349bih967kdgnbaupy0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm349bih967kdgnbaupy0.png" alt="A batch goes from submit to results within 24 hours" width="799" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Results can come back in any order. Give each request a unique &lt;code&gt;custom_id&lt;/code&gt; and match results on it, never on position.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rate limits
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Limit&lt;/th&gt;
&lt;th&gt;Counts&lt;/th&gt;
&lt;th&gt;Good to know&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RPM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requests per minute&lt;/td&gt;
&lt;td&gt;Set per model class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ITPM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Input tokens per minute&lt;/td&gt;
&lt;td&gt;Cache reads don’t count on most models, so caching raises throughput too&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OTPM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Output tokens per minute&lt;/td&gt;
&lt;td&gt;Counts tokens generated, not your &lt;code&gt;max_tokens&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;On a 429, wait the seconds in &lt;code&gt;retry-after&lt;/code&gt;.&lt;/strong&gt; A 429 without it means the spend cap is reached, and retrying won’t help.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the headers.&lt;/strong&gt; &lt;code&gt;anthropic-ratelimit-*&lt;/code&gt; show each limit, what’s left and when it resets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limits are per organization&lt;/strong&gt; and refill continuously (a token bucket), not once a minute. Workspaces can have lower limits of their own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scale gradually.&lt;/strong&gt; Limits rise with your usage tier, and a sudden spike can hit acceleration limits.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Errors: transient vs. permanent
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3eepfcq847kdg6bucrla.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3eepfcq847kdg6bucrla.png" alt="Retry transient errors, fix permanent ones" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Back off with jitter.&lt;/strong&gt; Retry transient errors only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cap it.&lt;/strong&gt; Limit both attempts and total time, and set &lt;code&gt;max_retries=0&lt;/code&gt; if you add your own loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Degrade under load.&lt;/strong&gt; Log the &lt;code&gt;request-id&lt;/code&gt;, and under sustained 529s queue or shed work instead of hammering the API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Module 3: Caching and costs
&lt;/h2&gt;

&lt;p&gt;You pay per token, and output costs more than input. Prompt caching is the biggest single lever on input cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is billed
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Counts&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Input tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Everything you send: system, tools, history, documents&lt;/td&gt;
&lt;td&gt;The base rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output tokens&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Everything generated, including thinking you never see&lt;/td&gt;
&lt;td&gt;5 times the input rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache writes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The prefix you mark, the first time&lt;/td&gt;
&lt;td&gt;More than input, once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache reads&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The same prefix, reused&lt;/td&gt;
&lt;td&gt;A small fraction of input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web search&lt;/td&gt;
&lt;td&gt;Per search, plus its tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt caching
&lt;/h3&gt;

&lt;p&gt;Mark a &lt;code&gt;cache_control&lt;/code&gt; breakpoint, and on the next call everything before it is served from cache.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuewmnf4g8avnuq0xvftp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuewmnf4g8avnuq0xvftp.png" alt="Everything before the breakpoint is served from cache" width="798" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A hit needs an identical prefix.&lt;/strong&gt; The API looks back up to 20 blocks from each breakpoint for a match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Short prompts fail silently.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic mode&lt;/strong&gt; (a top-level &lt;code&gt;cache_control&lt;/code&gt;) moves the breakpoint forward as the conversation grows. It uses one of the 4 slots.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A write costs more than no cache,&lt;/strong&gt; so it pays off only if the prefix is read before it expires. Caching stacks with the batch discount.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What breaks the cache
&lt;/h3&gt;

&lt;p&gt;The prefix runs tools, then system, then messages. A change invalidates its own level and every level after it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flz5w6rjzvwiigo7wa5tr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flz5w6rjzvwiigo7wa5tr.png" alt="A change invalidates its own level and every level after it" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Other levers
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lever&lt;/th&gt;
&lt;th&gt;What it saves&lt;/th&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Smaller model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every token costs less&lt;/td&gt;
&lt;td&gt;Route simple, high-volume inputs to a smaller model and keep the larger one for hard cases (Module 6)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lower effort&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Thinking tokens, billed as output&lt;/td&gt;
&lt;td&gt;Defaults differ by model, so set &lt;code&gt;effort&lt;/code&gt; yourself and lower it until your evals show a drop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Batch API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Half the price of input and output&lt;/td&gt;
&lt;td&gt;Send anything nobody is waiting for. The discount stacks with caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Less context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Input you resend on every turn&lt;/td&gt;
&lt;td&gt;Trim history, clear old tool results, hand side work to subagents (Module 5)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Caps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A runaway loop or reply&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;max_tokens&lt;/code&gt; caps each reply. In the Agent SDK, &lt;code&gt;max_turns&lt;/code&gt; and &lt;code&gt;max_budget_usd&lt;/code&gt; cap each run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Measuring spend
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Tells you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Token counting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Input tokens, before you send&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;input_tokens&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tokens after the last breakpoint, full price&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;cache_creation_input_tokens&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tokens written to the cache, write price&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;cache_read_input_tokens&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tokens read from the cache, read price&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;output_tokens&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Output, thinking included&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;total_cost_usd&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cost of a whole Agent SDK run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both cache fields at 0 on a repeated call means the prefix changed or is under the minimum.&lt;/p&gt;

&lt;h2&gt;
  
  
  Module 4: Prompt engineering
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anatomy of a request
&lt;/h3&gt;

&lt;p&gt;A request has four places to put instructions, and each technique below has a natural home in one of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzkuabwsy03z9nw1eod6r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzkuabwsy03z9nw1eod6r.png" alt="Where each technique goes in a request" width="799" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Techniques
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Technique&lt;/th&gt;
&lt;th&gt;Do&lt;/th&gt;
&lt;th&gt;Avoid&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Clear and direct&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Say what done looks like and why each rule exists. Ask for the action: “Change this function to…”&lt;/td&gt;
&lt;td&gt;“Format the code properly”, “Can you suggest changes to…”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Positive, calm wording&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;“Write in flowing prose”, “Use this tool when…”. Write the prompt in the style you want back&lt;/td&gt;
&lt;td&gt;“Don’t use markdown”, “CRITICAL: you MUST…”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Say how far to go: “Go beyond the basics” or “Keep changes minimal”&lt;/td&gt;
&lt;td&gt;An open-ended “improve this”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Role and examples&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One-sentence role in &lt;code&gt;system&lt;/code&gt;. 3 to 5 varied examples in &lt;code&gt;&amp;lt;example&amp;gt;&lt;/code&gt; tags&lt;/td&gt;
&lt;td&gt;Near-identical examples Claude copies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Consistent XML tags. Documents at the top, question at the end, quotes first&lt;/td&gt;
&lt;td&gt;Data and instructions mixed, the question above long input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reasoning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Raise &lt;code&gt;effort&lt;/code&gt; and ask in general terms (“think it through”). If it overthinks: “Choose an approach and commit to it”&lt;/td&gt;
&lt;td&gt;A hand-written step-by-step plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Grounding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;“Never speculate about code you have not opened.” “Before you finish, verify against…”&lt;/td&gt;
&lt;td&gt;Answers about files it hasn’t read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tools and steps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tool descriptions say what, when and when not, and each parameter. Separate calls when you must check each step (Module 6)&lt;/td&gt;
&lt;td&gt;One-line descriptions, one giant prompt for a multi-stage task&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Putting the question last can improve answers by up to 30% on long, multi-document inputs (Anthropic’s tests).&lt;/p&gt;

&lt;h3&gt;
  
  
  Structured outputs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;What it guarantees&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;output_config.format&lt;/code&gt; with a JSON schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The reply always parses against the schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;strict: true&lt;/code&gt; on a tool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tool inputs always match the tool’s schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;additionalProperties: false&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Required on every object in a strict schema&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Still validate before passing output downstream: a refusal or a &lt;code&gt;max_tokens&lt;/code&gt; cutoff can leave output that doesn’t match the schema.&lt;/p&gt;

&lt;p&gt;Don’t ask the model to write out its reasoning, in a field or in &lt;code&gt;&amp;lt;thinking&amp;gt;&lt;/code&gt; tags: it can be declined with a &lt;code&gt;reasoning_extraction&lt;/code&gt; refusal. Raise &lt;code&gt;effort&lt;/code&gt; instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Module 5: Context engineering
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  The toolkit
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Technique&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subagent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Does noisy work in its own context and returns a summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compaction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Replaces old history with a summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fresh context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Starts a fresh context between unrelated tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context editing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Clears the oldest tool results or thinking blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory tool&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Saves notes outside the window to read back later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Just-in-time loading&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Keeps paths and IDs in context, loads content on demand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Progressive disclosure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Loads a skill’s name and description first, its instructions when triggered, its files only when used&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Subagents
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F01jbgc0c0rvbj0jbbvua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F01jbgc0c0rvbj0jbbvua.png" alt="A subagent spends its own context and returns only a summary" width="799" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compaction and context editing
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsgpug6jiyvvx64xnttcy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsgpug6jiyvvx64xnttcy.png" alt="Two ways to shrink a full context window" width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compaction&lt;/strong&gt; 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 &lt;code&gt;/compact &amp;lt;focus&amp;gt;&lt;/code&gt; steers what’s kept.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It loses detail&lt;/strong&gt;, so anything that must survive goes in memory or CLAUDE.md.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context editing&lt;/strong&gt; (beta) runs on the server, so your client keeps the full history.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Module 6: Agent patterns
&lt;/h2&gt;

&lt;p&gt;Anthropic’s rule from &lt;em&gt;Building effective agents&lt;/em&gt;: start with the simplest design, and add complexity only when evals show it helps.&lt;/p&gt;

&lt;h3&gt;
  
  
  The patterns
&lt;/h3&gt;

&lt;p&gt;Each LLM box below is an &lt;strong&gt;augmented LLM&lt;/strong&gt;: one model call with retrieval, tools and memory. Often that single call is enough.&lt;/p&gt;

&lt;p&gt;What separates the patterns is who decides the steps. In the first five, the &lt;strong&gt;workflows&lt;/strong&gt;, your code fixes the shape. In the &lt;strong&gt;agent loop&lt;/strong&gt;, the model picks each step. Since any box can itself be an agent loop, the bottom row shows ways to coordinate several of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fovou9ojnv9sv2caov7je.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fovou9ojnv9sv2caov7je.png" alt="Nine patterns, from fixed code paths to teams of agents - A" width="800" height="530"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1jj0dfxlqqipk3xrwvyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1jj0dfxlqqipk3xrwvyf.png" alt="Nine patterns, from fixed code paths to teams of agents - B" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a lead agent delegates, give each subagent a budget, an output schema and a clear handoff, and check outputs before passing them on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pipeline vs. graph
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff6zd7yfsu2zp493fa2ya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff6zd7yfsu2zp493fa2ya.png" alt="Who draws the graph: your code, or the model at runtime" width="799" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Module 7: Evals and safety
&lt;/h2&gt;

&lt;p&gt;A lead engineer has to answer two questions before shipping: how do we know it works, and what is the worst it can do?&lt;/p&gt;

&lt;h3&gt;
  
  
  Evals
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4lx6xg1drchrsb8hecla.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4lx6xg1drchrsb8hecla.png" alt="Evals turn every change into a measured change" width="800" height="187"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Grading&lt;/th&gt;
&lt;th&gt;Use for&lt;/th&gt;
&lt;th&gt;Trade-off&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code-based&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Labels, formats, exact answers&lt;/td&gt;
&lt;td&gt;Fastest, most reliable and most scalable, but can’t judge nuance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LLM-based&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tone, relevance, leaks of private data&lt;/td&gt;
&lt;td&gt;Handles complex judgments. Test its reliability first, then scale. Give it a clear rubric and use a different model than the one graded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Human&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Only what automation can’t judge&lt;/td&gt;
&lt;td&gt;Most flexible and highest quality, but slow and expensive. Avoid if possible&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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 &lt;code&gt;effort&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For coding agents, ask for a general solution and for wrong tests to be reported, never hardcoded around.&lt;/p&gt;

&lt;p&gt;Evals are one layer of testing. The code around the model needs the usual layers too.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tests&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Parsing, tool handlers and loop logic, against a mocked API&lt;/td&gt;
&lt;td&gt;Every commit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration and end-to-end&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The Claude integration with real API calls (tools, schemas, streaming, errors), and critical user flows end to end&lt;/td&gt;
&lt;td&gt;Before merge, on a fixed set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Evals&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Output quality on a graded dataset&lt;/td&gt;
&lt;td&gt;In CI, on every prompt change and model upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Production&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monitoring for drift and real-world failures, user feedback, sampled transcript review, A/B tests for big changes&lt;/td&gt;
&lt;td&gt;After launch, ongoing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Human review&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Calibrate LLM graders, judge subjective output&lt;/td&gt;
&lt;td&gt;Periodically&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h3&gt;
  
  
  Versioning prompts
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version this&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;The prompt text&lt;/strong&gt;, in git, with a version ID&lt;/td&gt;
&lt;td&gt;Review changes in pull requests and roll back a bad one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;The whole request:&lt;/strong&gt; model ID, &lt;code&gt;effort&lt;/code&gt;, tools, schemas&lt;/td&gt;
&lt;td&gt;A prompt only behaves the same with the same settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;The model ID&lt;/strong&gt;, pinned&lt;/td&gt;
&lt;td&gt;IDs from the 4.6 generation on are fixed snapshots, but each has its own retirement date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;The eval score&lt;/strong&gt; of each version&lt;/td&gt;
&lt;td&gt;Promote a version only when its score holds or improves&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;The prompt version&lt;/strong&gt; and &lt;code&gt;request-id&lt;/code&gt; in every log line&lt;/td&gt;
&lt;td&gt;Trace a bad answer back to the exact prompt that produced it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In CI, run Claude Code headless with &lt;code&gt;claude --bare -p "..." --output-format json&lt;/code&gt;. &lt;code&gt;--bare&lt;/code&gt; skips local hooks, plugins, MCP servers and CLAUDE.md, so every run is the same, and &lt;code&gt;--allowedTools&lt;/code&gt; limits what the job can touch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt injection
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Mitigation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Direct:&lt;/strong&gt; the user is the attacker&lt;/td&gt;
&lt;td&gt;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 &lt;code&gt;system&lt;/code&gt;. Throttle repeat offenders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Indirect:&lt;/strong&gt; content the agent reads is the attacker&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Treat tool content as data, never as commands&lt;/strong&gt;, and say so in &lt;code&gt;system&lt;/code&gt;. Put third-party content only in &lt;code&gt;tool_result&lt;/code&gt; 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&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Too much power&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;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&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For rules that must never break, filter Claude’s output in code before it reaches users. A prompt alone is not enforcement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent SDK permissions
&lt;/h3&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo3575jxfjnasj6ak14ud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo3575jxfjnasj6ak14ud.png" alt="Each tool call passes six checks in order, and the first that decides wins" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;default&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nothing approved by the mode. Unmatched calls go to &lt;code&gt;canUseTool&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;dontAsk&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Anything that would prompt is denied: for headless agents with a fixed tool list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;acceptEdits&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Approves file edits and filesystem commands inside the working directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;plan&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Explores and plans. File edits and shell writes always go to &lt;code&gt;canUseTool&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;auto&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A model classifier approves or denies prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;bypassPermissions&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Approves everything that reaches the mode step. Sandboxes only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Rules, modes and callbacks interact in ways that are easy to miss. &lt;code&gt;allowed_tools&lt;/code&gt; pre-approves the tools you list but doesn’t restrict &lt;code&gt;bypassPermissions&lt;/code&gt;, 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 &lt;code&gt;bypassPermissions&lt;/code&gt; parent hands every subagent full access. And a scoped deny such as &lt;code&gt;Bash(rm *)&lt;/code&gt; only blocks calls written that way (&lt;code&gt;/bin/rm&lt;/code&gt; gets through), while a bare name such as &lt;code&gt;Bash&lt;/code&gt; in &lt;code&gt;disallowed_tools&lt;/code&gt; removes the tool entirely. For a locked-down headless agent, pair &lt;code&gt;allowed_tools&lt;/code&gt; with &lt;code&gt;dontAsk&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Scope each hook with a matcher to the risky tools, such as &lt;code&gt;Bash&lt;/code&gt;, &lt;code&gt;Write&lt;/code&gt; or sensitive data access, so safe calls don’t pay for the check.&lt;/p&gt;

&lt;p&gt;Permissions stop what the agent can do. In the prompt, also ask it to confirm before anything irreversible or shared, such as &lt;code&gt;git push --force&lt;/code&gt; or dropping a table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keys and data residency
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The Claude API can keep inference in the US (&lt;code&gt;inference_geo: "us"&lt;/code&gt;, 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.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>softwareengineering</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
