Software Engineering Foundations
Apply everyday engineering practice to Claude applications: robust REST and JSON handling, correct async code, disciplined version control, Claude Code in CI, rigorous review of AI-written code, and safe small- and large-scale refactoring.
Key points
- 1
REST retry safety: GET, PUT (full replacement) and DELETE are idempotent. POST is not, so send an idempotency key or deduplicate on the server before retrying automatically after a timeout.
- 2
Status codes decide how to recover. On a 400
invalid_request_error, fix the request and do not retry it unchanged. On a 429 or 5xx/529, retry with exponential backoff that respectsretry-after. The official SDKs retry these automatically, twice by default (max_retries). - 3
Prefer cursor (keyset) pagination over a stable sort key. Offset pagination over a changing dataset produces duplicates and gaps. Claude list endpoints return
has_moreandlast_id, and you passlast_idasafter_id; the SDKs can also paginate automatically. - 4
Treat model output as untrusted input. For JSON, constrain the format with structured outputs or a tool
input_schema, then validate against the schema in code. Neverevalmodel output or clean it up with regexes and hope. - 5
Evolve JSON contracts additively. Add new fields, keep deprecated ones populated during a migration window, version the schema, run consumer contract tests in CI, and have consumers ignore unknown fields.
- 6
Async: in async frameworks use
AsyncAnthropic, because a synchronous client blocks the event loop. Stream withasync with client.messages.stream(...)andasync for text in stream.text_stream. - 7
Limit concurrency with a semaphore or worker pool sized to your rate limits. An unbounded
asyncio.gatherover thousands of calls causes 429s and memory spikes, and by default its first exception propagates to the caller. - 8
Design batch jobs so that one bad item cannot abort the run. Handle errors per item, record permanent failures by ID, and make reruns skip items already completed.
- 9
Streaming: the HTTP 200 arrives before generation finishes, so errors such as
overloaded_errorcan arrive as anerrorevent mid-stream. Never save partial text as a complete answer. - 10
Version control: keep PRs small with one intent each, and keep behavior-preserving refactors separate from bug fixes. Run parallel Claude Code sessions in separate git worktrees (
--worktree) so their edits cannot collide. - 11
In CI and SDLC, use the Claude Code GitHub Action or headless
claude -pwith scoped--allowedTools, and optionally--output-format json. Keep the API key in a secret (e.g.ANTHROPIC_API_KEY), never in the workflow file.--bareskips loading local hooks, plugins and CLAUDE.md for scripted runs. - 12
Code review with Claude: give it explicit criteria (for example in CLAUDE.md) plus the diff and surrounding code, and keep a required human approval. A fresh-context reviewer beats the authoring session. Be suspicious when test assertions change in the same PR as a fix.
- 13
Small refactors: confirm the tests are green, change structure without changing behavior, and commit the refactor separately. Large refactors: add tests where coverage is thin, plan the work (plan mode), migrate incrementally in reviewable PRs, and record conventions in CLAUDE.md.
- 14
Fan out codebase modernization: have Claude generate the task list, loop
claude -pper file with--allowedTools, pilot on 2–3 files to refine the prompt, then run the full set./batchspreads a change across subagents, each in its own worktree.
Read the source
Test yourself on Software Engineering Foundations
Ten questions, with the answer and explanation after each one.