TLDR
The Claude Messages API is a plain REST API that accepts and returns JSON - POST /v1/messages with a stateless request/response contract - so the ordinary software-engineering discipline you already use for any HTTP service (JSON contracts, async I/O, version control, code review, disciplined refactoring) is exactly what a production Claude integration needs, not a special category of its own. Claude docs: messages/create The exam trap is treating the API as a black box; the statelessness trap is assuming the server remembers anything - conversation memory is your app's responsibility, every call resends the full turn history.
What it is
This objective is general software-engineering discipline, applied specifically to shipping production Claude apps. The load-bearing insight for developers: the Claude Messages API *is* a REST API that accepts and returns JSON, so the same practices used for any HTTP service - resource-oriented verbs and paths, JSON contracts, asynchronous I/O, version control, code review, and disciplined refactoring - are exactly what a well-built Claude integration needs. Concretely, "Create a Message" is POST /v1/messages, a stateless call: you send prior conversational turns and the model returns the next one. REST's defining constraint - stateless client-server interaction over a uniform interface, per Fielding's original dissertation - is exactly how the Messages API's multi-turn design behaves.
None of this is Claude-specific machinery to relearn from scratch. An engineer who already knows how to design a resource, validate a JSON contract, reason about blocking vs. non-blocking I/O, keep a codebase under version control with reviewed pull requests, and refactor without changing observable behavior already has the core toolkit this objective tests - the job is recognizing that a Claude integration is an instance of that toolkit, not an exception to it.
How it works
REST + JSON contract. The request body is JSON with required fields model, max_tokens, and messages (an array of {role, content} turns alternating user/assistant); the response is a JSON Message object with id, type, role, content (an array of content blocks), model, stop_reason, and usage (input_tokens/output_tokens). Because the API is stateless, the server does not remember prior turns - you resend the full conversation history on every call; state lives entirely in your application, not on Anthropic's servers. This is the single most common source of "my app forgot what the user said" bugs: it isn't a bug in the API, it's a missing array element in the request your app sent.
Asynchronous programming. The official Python SDK ships both a synchronous Anthropic client and an AsyncAnthropic client; async calls use await client.messages.create(...), letting an application fan out concurrent requests - parallel tool calls, multiple users, batch workloads - without blocking on each one sequentially. Async matters most when the workload is inherently concurrent or when a call streams; a single simple integration with light traffic gets no meaningful benefit from async machinery it doesn't need.
Streaming. Setting "stream": true returns the response incrementally as Server-Sent Events instead of one blocking payload; SDKs expose client.messages.stream(...) with helpers like get_final_message() to assemble the complete response once streaming finishes. The concrete engineering reason to reach for this: it prevents client/proxy timeouts on long generations or high max_tokens requests that would otherwise sit silent until the full response is ready. Streaming is a latency/UX and timeout-avoidance tool, not a cost-reduction one - token billing is identical whether or not you stream. For the full mechanics of parsing stream events, see the dedicated streaming concept page - see Study Next.
Version control and code review. Use git for history and branching, and treat the pull-request review flow - propose changes on a branch, get them reviewed before merge - as standard practice for anything that ships to production. The Claude-specific implication: system prompts, tool schemas, and orchestration code are source, not configuration to be hand-edited live. A system-prompt change that skips code review is exactly as risky as an unreviewed change to any other production code path, and arguably riskier, since prompt behavior is harder to unit-test deterministically than most application logic.
Refactoring. Restructuring code without changing its observable behavior is the discipline that keeps an evolving prompt/tool/agent codebase maintainable as it grows - renaming and reorganizing tool definitions, splitting an orchestration loop into smaller functions, or consolidating duplicated system-prompt fragments, all without altering what the agent actually does. The distinction that matters for a Claude app specifically: a change is a refactor only if the observable behavior (the agent's outputs, tool-call sequences, eval scores) is provably unchanged - anything that shifts behavior, even a prompt wording tweak intended to be "just cleanup," is a behavior change requiring re-evaluation against the eval suite (see the systems-life-cycle-management page's test-phase discipline), not a refactor exempt from testing.

Where you'll see it
Production Claude chatbot
Each turn POSTs JSON (model, max_tokens, messages) to /v1/messages and parses the JSON Message back; because the endpoint is stateless, the app owns the conversation array and resends it every turn. Under concurrent load it switches to AsyncAnthropic and streams via SSE so long generations don't block or time out.
Prompt and tool-schema change management
System prompts and tool definitions live in git and go through the same pull-request code-review flow as any other production source; a claimed 'refactor' to the orchestration loop is verified against the eval suite before merge, not assumed behavior-safe by inspection.
Side-by-side
| General SWE practice | Ordinary REST service | Claude Messages API equivalent |
|---|---|---|
| Resource-oriented endpoint | One verb per resource action, e.g. POST /orders | POST /v1/messages - one endpoint, one resource: message creation |
| Request/response contract | JSON body in, JSON body out | JSON in (model, max_tokens, messages) → JSON Message out (id, content[], stop_reason, usage) |
| Statelessness | Server holds no client session state by default | Server holds no conversation state at all - the full turn history is resent every call |
| Async I/O | Non-blocking client for concurrency under load | AsyncAnthropic + await client.messages.create(...) for concurrent/parallel calls |
| Long-running response handling | Chunked transfer or SSE to avoid client timeouts | stream: true → Server-Sent Events; client.messages.stream(...) + get_final_message() |
Decision tree
Is a client-visible bug where the model 'forgot' something the user said earlier in the conversation?
messages array on every call - the API is stateless, so a missing turn in the request is a common cause to check first, not necessarily a model failure.Does the workload need to issue multiple concurrent or parallel Claude calls (fan-out, batch, multi-user)?
AsyncAnthropic with await client.messages.create(...) so calls don't block each other sequentially.Is a single call's max_tokens or expected generation long enough to risk a client/proxy timeout?
stream: true and consume Server-Sent Events (or the SDK's .stream() helper) so the client receives output incrementally instead of waiting on one blocking payload.Is a proposed prompt, tool-schema, or orchestration-loop change intended as pure cleanup with no behavior change?
Is a system prompt or tool schema being edited directly against production without a reviewed pull request?
Question patterns

A production chatbot's users report that Claude periodically "forgets" facts they stated two messages earlier, even though the app never restarts. What's the most likely root cause, given the Messages API's design?
messages array on every call - the API is stateless, so any turn missing from the request is invisible to the model, indistinguishable from never having been said. The named distractor "the model's context window is too small for this conversation" is a real failure mode in general, but the stated symptom (forgetting recent, nearby turns in an ordinary chat) points to a missing-turn bug in the request payload long before context-window exhaustion becomes the explanation.A backend needs to process 200 independent customer support tickets through Claude as fast as possible, where each ticket's request is fully independent of the others. Sequential synchronous calls take too long. What's the fix?
AsyncAnthropic and issue the 200 calls concurrently with await client.messages.create(...), since the requests are independent and don't need to block on one another. The named distractor "switch to streaming for each request to speed things up" misapplies streaming - streaming reduces time-to-first-token for a single long response, it does not parallelize independent requests; concurrency needs async, not streaming.A report-generation endpoint sets `max_tokens` to 8,000 and users' browsers are timing out waiting for the full response before anything renders. What's the correct architectural fix, and what does it NOT solve?
stream: true (or use the SDK's client.messages.stream(...) helper) so output arrives incrementally as Server-Sent Events, preventing the client/proxy from timing out on a long blocking wait. It does NOT reduce token cost - billing is identical streamed or not. The named distractor "reduce max_tokens to avoid the timeout" treats the symptom by truncating legitimate output instead of fixing the actual timeout mechanism.An engineer renames several tool-definition variables, splits a large orchestration function into three smaller ones, and ships it labeled as "just a refactor, no review needed since behavior can't have changed." What's missing from that claim?
"renaming variables and splitting functions can never change behavior, so no verification is needed" overstates the guarantee - a reachable bug in the split (e.g. a dropped parameter) turns a claimed refactor into an unverified behavior change.A team edits their production system prompt directly in the deployment config to fix an urgent issue, without a pull request or code review, planning to "backfill the PR later." What software-engineering principle does this violate, and why does it matter more for a Claude app than it might elsewhere?
"prompts are just text content, not code, so review requirements don't apply" is the exact misconception this objective tests against.Frequently asked
Is the Claude Messages API a special AI-only protocol, different from a normal REST API?
Do I need async and streaming for every Claude API call?
Work this with your AI
Work this concept hands-on with Claude Code, Codex, or claude.ai. Copy a prompt, paste it into your assistant, and practise in tandem. Each one keeps you active (explain it back, get drilled, or build) rather than just reading.
- Drill it like the exam (scenario MCQs)Practice in the exam's scenario-MCQ format with trap awareness.
- Explain it back (Feynman)Build durable, transferable understanding of a concept you can half-state.
- Test me, adapting the difficultyActive recall practice on a concept you think you know.
- Check my prerequisites firstBefore studying a concept that keeps not sticking.
- Find the high-leverage 20%When a domain feels too big and you are short on time.
