Study guides / CCDV-F / Domain 1

Applications & Integration · Lesson 7 of 11

1.7 - Claude Application Design Patterns

Recognise the handful of shapes most Claude applications take, and pick the one that matches the actual latency and control requirements.

Most Claude-integrated applications are one of a few recognisable shapes: a synchronous chat turn (one request, one response, low latency budget, a human waiting), a batch/offline job processing many independent items where per-item latency doesn't matter but throughput and cost do, and an agentic loop (Domain 3 goes deeper on this) where Claude drives multiple tool calls toward a goal with no fixed number of turns. Picking the wrong shape shows up as either a sluggish chat UI built on top of a batch-style flow, or an over-engineered agent loop for what was really a one-shot classification task with no dependency between items.

Where the Batches API fits

For the batch shape specifically, a dedicated batch-processing endpoint exists for exactly this: submit many requests together as a single job, get results back asynchronously once the whole batch completes, at a meaningfully lower cost per request than the same volume sent as individual synchronous calls. It trades latency (results arrive later, not instantly, and a batch can take up to a set processing window rather than seconds) for throughput and price — the right trade for a nightly summarisation job, a large one-time reclassification of historical data, or a periodic content-moderation sweep, and the wrong one for a live user-facing chat where someone is staring at a loading spinner.

The decision isn't just latency

Two more questions cut through most ambiguous cases. First: does any step depend on the output of a previous step? If yes, that's the agentic shape's territory, even if the overall interaction feels quick to a user — a single "look this up and summarise it" request might still be one synchronous call, but "look this up, then decide what to look up next based on what you found" is a loop. Second: are the items genuinely independent of each other? Ten thousand support tickets that don't reference one another are a batch; ten thousand tickets where resolving one changes how you'd triage the next are not.

Common exam distractor

An answer suggesting an agentic loop for a task that's really independent per-item classification (no dependency between items, no multi-step reasoning needed) is over-engineering — a straightforward batch of single calls is the simpler, cheaper, more reliable fit. The exam rewards recognising when the simpler shape is correct, not defaulting to the most sophisticated-sounding one.

Mixed shapes in one product

A single product commonly uses more than one shape for different features: a live chat surface (synchronous) that occasionally hands off a long-running research task to an agentic loop running in the background, while a separate nightly job re-scores every conversation from the day before using the Batches API. Recognising that these are three different problems, each best solved by a different shape, rather than trying to force one architecture to cover all three, is itself the skill being tested — not memorising a single "correct" pattern.

Where the Agent SDK fits versus the raw Messages API

For the agentic shape specifically, you have a further choice: hand-roll the loop directly against the Messages API (as in Lesson 1.5), or build on a higher-level framework like the Claude Agent SDK, which provides the loop, tool-execution wiring, and hook points (Domain 3 covers hooks in depth) as a maintained layer instead of code you own and debug yourself. The raw-API loop gives you full control and is the right choice when your application's needs are simple or highly custom; the Agent SDK trades some of that low-level control for a faster path to a production-grade loop with built-in patterns for the exact failure modes — premature termination, unkeyed tool results, unbounded iteration — this domain covers. The exam expects you to recognise both exist and reason about which fits a described scenario, not to treat one as universally correct.

A worked comparison

Consider three requests to the same team in one week: (1) "users should get an answer to a question about their account within two seconds" — synchronous, low latency budget, single independent turn; (2) "re-tag every support ticket from the last year with an updated category taxonomy" — batch, no per-item latency requirement, items independent of each other; (3) "investigate this production incident by pulling logs, correlating timestamps across three services, and drafting a root-cause summary" — agentic, because the next tool call genuinely depends on what the previous one returned and the number of steps isn't known in advance. Same team, same underlying model, three different architectures, because the shape follows the requirements, not the other way around.

Key concept

When a scenario describes a task, ask two questions before picking an architecture: does a human need this result right now, and does any step depend on what an earlier step returned? Those two answers, not the sophistication of the tooling involved, determine the correct shape.

Exam traps

Practice question

A team needs to classify 50,000 support tickets by category overnight, with results ready by morning; nobody is waiting on any single result in real time. Which application shape fits best?

  • A A synchronous chat interface, called once per ticket in a loop.

    Synchronous calls are optimised for low per-request latency in an interactive setting, not for cost-efficient high-volume offline throughput.

  • B An agentic loop with tool use, so Claude can look up related tickets before classifying each one.

    Nothing in the task requires multi-step tool-driven reasoning per ticket - this adds complexity and cost with no stated benefit.

  • C A batch job submitting all 50,000 classification requests together for asynchronous, lower-cost processing. Correct

    Independent items, no real-time latency requirement, high volume - this is exactly the batch shape's sweet spot.

  • D Extended thinking enabled on every request to maximise classification accuracy.

    A simple categorical classification task rarely needs multi-step reasoning; enabling thinking here mostly adds cost without a matching accuracy need.

Build exercise: Match five application briefs to the right shape, then implement one as a real batch call

Beginner · 30 minutes

You'll practice:

  1. Write down five real or hypothetical Claude use cases you know of, and for each, note the latency requirement and whether any step depends on the result of a previous step.

    Those two questions - latency budget, and step dependency - are the actual decision factors, not gut feel.

    You should see: Each use case cleanly maps to synchronous, batch, or agentic once you've named its latency budget and dependency structure.

    Hints
    1. For each use case, ask: is a human waiting right now, and does step 2 need to know what step 1 returned?
    2. "A live user waiting on a reply" is almost always synchronous. "Many independent items, results needed by some later deadline" is almost always batch. "The next step depends on what the last tool call returned" is the tell for agentic.
    3. Example table row: Use case: nightly re-tagging of 10k articles | Latency: next morning | Dependency: none between articles -> Batch
  2. Pick one of your batch-shaped use cases and construct a small batch request with at least three independent items, each as its own set of Messages API parameters within the batch.

    Seeing the actual request shape for a batch job - an array of individually-parameterised requests, not one big prompt - is the concrete skill behind the conceptual sorting exercise.

    You should see: A batch submission accepted by the API, returning a batch id you can poll for status.

    Hints
    1. How is a batch request structured differently from a normal Messages API call - is it one request body, or a collection of them?
    2. Each item in a batch has its own custom_id and its own full Messages API params object; the batch endpoint accepts an array of these, not a single shared prompt.
    3. await client.messages.batches.create({
        requests: [
          { custom_id: "ticket-1", params: { model: "claude-sonnet-5", max_tokens: 50, messages: [{ role: "user", content: "Classify ticket: 'Login page is broken'" }] } },
          { custom_id: "ticket-2", params: { model: "claude-sonnet-5", max_tokens: 50, messages: [{ role: "user", content: "Classify ticket: 'Please cancel my subscription'" }] } }
        ]
      });
  3. Poll the batch's status until it completes, then retrieve and print the per-item results, matching each result back to its custom_id.

    This is the asynchronous half of the batch shape - results don't come back in the original call, and matching them back to the source item by id is the part that trips people up.

    You should see: A completed batch status and a set of results, each identifiable by the custom_id you assigned when submitting.

    Hints
    1. If a batch's results come back in a stream or a list, how do you know which result belongs to which original ticket?
    2. Each result in the batch output carries the same custom_id you set on the corresponding request - use that to map results back to your original items rather than assuming order is preserved.
    3. const results = await client.messages.batches.results(batchId);
      for (const r of results) {
        console.log(r.custom_id, "->", r.result.message.content[0].text);
      }

Sources