Personalize this lesson
Adapt explanations and teaching visuals to your background and preferred voice.
A system-design round asks you to turn ambiguous model-product requirements into a reliable backend. A strong answer is rarely the most complex architecture. It names the product goal, sizes the hard constraint, then adds queues, caches, model routing, eval gates, permissions, or human review only where requirements force them.
Use codebase, model-serving, RAG, support-automation, and deployment examples when you need a concrete neutral domain: code search, incident summarization, policy-gated billing credits, eval dashboards, and internal agent workflows have real latency, privacy, and debugging constraints.
The design round script
Open with:
I will keep the first design simple, size the constraints early, then add queues, caches, sharding, model routing, or eval gates only when the requirement forces them.
Then follow this order:
- Goal: who uses it and what success means.
- Requirements: functional and non-functional.
- Scale: QPS, tokens, tenants, documents, latency, retention.
- API: external contract and important internal interfaces.
- Data model: entities, indexes, isolation, retention.
- Architecture: simplest request path first.
- Reliability: retries, idempotency, backpressure, overload, failover.
- Safety/security: permissions, audit, abuse controls, rollback.
- Observability: metrics, logs, traces, support views.
- Rollout: beta gates, canaries, eval gates, kill switches.
Why start with the goal instead of Kafka, GPUs, or vector databases?
Answer
Because the right infrastructure depends on the product goal and constraints. A support lookup, a long-running coding task, and a model-serving scheduler have different latency, reliability, privacy, and eval requirements.
Design pattern taxonomy
Most frontier AI/backend prompts are combinations of these patterns. Identify the dominant pressure before drawing boxes.
| Pattern | Prompt signal | Architecture moves | Follow-up pressure |
|---|---|---|---|
| Model gateway | multi-provider, teams, API keys, quotas | auth, entitlements, route policy, quota buckets, request log | streaming, fallback semantics, budget caps, support replay |
| Inference scheduler | latency, batching, GPUs, overload | admission queue, batcher, worker pool, KV/cache accounting, fairness | tail latency, starvation, preemption, 429 vs 503 policy |
| Permission-aware retrieval | enterprise docs, ACLs, deletion, citations | source connectors, ACL snapshots, filtered retrieval, audit trail | revocation SLO, fail closed, hybrid search, stale index |
| Agent execution platform | long-running tasks, tools, repo access | task state machine, sandbox, tool policy, event log, artifacts | cancellation, retries, secret handling, human review |
| Eval and rollout gate | quality launch, regressions, red team | offline evals, golden sets, canary gates, rollback triggers | slice failures, noisy judges, metric ownership |
| Data ingestion platform | connectors, freshness, normalization | ingestion jobs, versioned records, dead-letter queue, backfill | schema drift, reprocessing, dedupe, deletion |
| Observability and support | request IDs, incidents, "why did this happen?" | traces, decision records, support views, replayable metadata | privacy-safe debugging, retention, sampling |
| Abuse and safety control | policy, misuse, irreversible actions | policy engine, rate limits, review queues, kill switches | false positives, bypass attempts, emergency disable |
Use this table to avoid architecture soup. A model gateway prompt doesn't need a vector database unless the product asks for retrieval. A retrieval prompt doesn't need autonomous agent planning unless the user asks the system to take actions.
Follow-up response bank
Interviewers often stress the first design with a new constraint. Answer by naming the boundary you'll change.
| Follow-up | Good move | Bad move |
|---|---|---|
| "Traffic spikes 10x" | admission control, queue SLO, tier fairness, explicit 429 or 503 cause | unlimited queues |
| "Permissions change quickly" | ACL freshness SLO, tombstones, fail-closed sensitive sources | retrieve first, filter after generation |
| "Provider is down" | policy-approved fallback, circuit breaker, surfaced degraded mode | silently change model behavior |
| "Users need cancellation" | durable cancel flag, cooperative checks, sandbox termination | best-effort UI button only |
| "Support asks why" | request ID, policy version, route decision, retrieved IDs, trace | raw logs with no decision record |
| "Eval passes but users complain" | slice analysis, online canary metrics, incident cases into regression suite | argue offline eval is enough |
| "Costs doubled" | token accounting, cache hit tracking, model route policy, budget alerts | vague autoscaling |
Scale math checklist
Every design should include one small calculation. It doesn't need perfect precision; it needs to expose the bottleneck.
| System | Minimum math |
|---|---|
| Gateway | requests/minute, tokens/minute, worst-case output cap |
| Retrieval | documents, chunks/document, embedding storage, update rate |
| Scheduler | arrival rate, average service time, queue wait, GPU memory |
| Agent service | concurrent jobs, sandbox time, log/artifact storage, retry budget |
| Eval platform | examples per suite, runs per release, judge/model cost |
| Voice/chat | p95 latency budget split across network, model, tools, synthesis |
| Ingestion | source QPS, backfill duration, dedupe key cardinality |
Use this phrasing:
I will size the constraint that most affects the design. If that assumption changes, the architecture boundary I would revisit is
X.
Serving math you should have ready
For any serving prompt, two numbers decide the design before you draw a single box: how much GPU memory the weights take, and how fast the KV cache grows per request. You don't need exact figures. You need to teach the interviewer that you know where the memory goes.
The weight footprint is the parameter count times the bytes per parameter:
Here is the number of parameters and is bytes per parameter: 2 for FP16 or BF16, 1 for FP8. An 8-billion-parameter model in BF16 is about GB of weights before you serve a single token.
The KV cache is the part that scales with traffic. Every token kept in context stores key and value vectors for each key/value head in every layer:
The leading 2 is for the key and the value. For a whole batch, multiply by the batch size and the sequence length :
Plug in simple numbers so the scale is concrete. The GQA line shows why grouped-query attention (sharing key/value heads across query heads) is the standard memory lever:
1params = 8_000_000_000
2bytes_per_param = 2 # FP16 or BF16
3weights_gb = params * bytes_per_param / 1e9
4print("weights_gb:", weights_gb)
5
6n_layers, n_kv_heads, d_head, p_bytes = 32, 32, 128, 2
7kv_bytes_per_token = 2 * n_layers * n_kv_heads * d_head * p_bytes
8print("kv_bytes_per_token:", kv_bytes_per_token)
9print("kv_mb_per_token:", round(kv_bytes_per_token / 1e6, 2))
10
11batch, seq = 16, 2048
12kv_gb = kv_bytes_per_token * batch * seq / 1e9
13print("kv_gb:", round(kv_gb, 1))
14
15n_kv_heads_gqa = 8 # grouped-query attention shares KV across query heads
16kv_bytes_per_token_gqa = 2 * n_layers * n_kv_heads_gqa * d_head * p_bytes
17print("kv_gb_gqa:", round(kv_bytes_per_token_gqa * batch * seq / 1e9, 1))1weights_gb: 16.0
2kv_bytes_per_token: 524288
3kv_mb_per_token: 0.52
4kv_gb: 17.2
5kv_gb_gqa: 4.3At batch 16 and 2K context, this multi-head-attention cache rivals weight memory. Switching from 32 to 8 key/value heads with GQA cuts it from about 17 GB to about 4 GB. First confirm that weights and runtime overhead fit; then KV cache often sets marginal batch and context capacity.
A serving prompt asks whether you can raise batch size to improve throughput. Which memory term should you check first, and why?
Answer
After confirming that weights and runtime overhead fit, check KV cache. Weight memory is fixed for a loaded model, while KV memory grows with batch size, sequence length, layers, and key/value heads. Doubling batch or context can exhaust remaining HBM.
Decision log habit
When a design has many possible components, keep a visible decision log:
| Decision | Chosen | Rejected | Why | Reversal signal |
|---|---|---|---|---|
| Queue placement | before provider call | inside every adapter | one overload policy | adapter-specific SLO needed |
| Retrieval filter | before rerank/generation | post-generation filter | privacy fails closed | none for sensitive docs |
| Fallback model | policy-gated | automatic on any error | behavior may change | explicit customer opt-in |
| Agent writes | human-reviewed | direct writes | irreversible action risk | narrow, reversible tool scope |
This keeps the conversation inspectable. Interviewers can disagree with a choice and still see that the choice was deliberate.
45-minute board plan
Practice with a visible clock. A strong design round leaves time for follow-ups instead of spending 30 minutes drawing boxes.
| Time | Output |
|---|---|
| 0-4 min | goal, users, success metric, top risks |
| 4-9 min | functional and non-functional requirements |
| 9-14 min | one scale calculation that exposes the bottleneck |
| 14-20 min | API and durable data model |
| 20-28 min | request path with the smallest architecture that works |
| 28-35 min | reliability, overload, permissions, and support/debug flow |
| 35-40 min | rollout, eval gate, and rollback path |
| 40-45 min | tradeoffs, reversal signals, and interviewer follow-ups |
If the interviewer interrupts early, jump to the dominant constraint:
The part that most changes the design is
constraint. I will size that first, then show the request path it forces.
Mock design prompts
Treat each prompt like a 45-minute design round. First write requirements, scale math, API, data model, request path, failure modes, and rollout plan. Then open the solution guide.
Prompt 1: model gateway for enterprise teams
Design an API gateway for teams calling multiple LLM providers through one company platform.
Prompt details:
- Each organization has workspaces, users, API keys, and model access rules.
- The gateway must enforce requests/minute, tokens/minute, and monthly spend limits.
- Support needs a request ID that can explain which route, model, policy decision, and overload state happened.
- Some models are beta-only and must be gated.
- Traffic can spike 10x during customer-support incidents.
Clarifying questions to ask:
- Are clients streaming responses, batch jobs, or both?
- Should spend limits be hard stops, soft alerts, or tier-dependent?
- Which decision must support explain first: auth failure, quota failure, route choice, or provider failure?
Solution guide
Strong answer shape:
- Goal: reliable multi-model access with debuggable controls.
- API:
POST /v1/responses,GET /v1/requests/{id}, admin endpoints for keys and limits. - Data model: organization, workspace, key, route policy, model entitlement, usage bucket, request log.
- Request path: gateway -> auth -> quota estimate -> route policy -> admission queue -> provider adapter -> stream.
- Overload: return
429withRetry-Afterwhen this tenant, key, or client exceeds its limit. Return503with a retry hint when healthy callers can't be admitted because fleet or provider capacity is exhausted. Route to an approved fallback only when product policy allows the behavior change. - Observability: request ID, model, token estimate, queue wait, provider latency, error class, policy version.
- Rollout: canary new routes, kill switch beta models, regression tests for auth and quota bypass.
Common misses: no support path, no versioned policy record, no overload rejection, and no separation between authentication and authorization.
Follow-up guide
If asked about streaming, reserve quota from an estimate before admission, cap output length, then reconcile actual tokens at the end of the stream. If asked about fairness during a 10x spike, split queues by organization or tier so one incident can't starve everyone else. If asked about beta models, answer with a versioned entitlement check and a kill switch:
The gateway should log
policy_version,entitlement_id,route_id, andquota_bucket_idon every request so support can explain both accepted and rejected traffic.
Prompt 2: permission-aware enterprise retrieval
Design retrieval for an internal assistant that answers employee questions from company documents.
Prompt details:
- Documents come from multiple systems with different ACL formats.
- Permission changes and deletions must take effect quickly.
- Answers must cite sources.
- The assistant must not retrieve and then filter private documents after generation.
- Admins need auditability for "why did this answer use this document?"
Clarifying questions to ask:
- What is the revocation target: seconds, minutes, or hours?
- Can indexes be physically separated by tenant, or must filters enforce isolation?
- Should the assistant fail closed when ACL freshness is unknown?
Solution guide
Strong answer shape:
- Ingestion: connector workers pull content plus ACL snapshots and write immutable document versions.
- Indexing: tenant or workspace isolation plus ACL metadata filters; deleted docs move to a tombstone state.
- Query path: auth context -> eligible corpus filter -> hybrid retrieval -> rerank -> answer with citations.
- Freshness: connector lag metric, ACL refresh jobs, deletion queue, and emergency purge path.
- Audit: query ID, user identity, eligible filters, retrieved doc IDs, citation IDs, policy version.
- Evals: recall slices by source, permission-denied tests, deletion tests, faithfulness checks.
- Failure mode: if ACL state is stale or unknown, fail closed for sensitive sources.
Common misses: filtering after generation, no deletion story, no citation IDs, and no way to explain eligibility.
Follow-up guide
If asked about deletion, describe a fast tombstone path first, then slower compaction of embeddings and chunks. If asked about stale permissions, define a freshness SLO per source and fail closed for sensitive sources when the ACL snapshot is too old.
For auditability, store enough to replay the eligibility decision: user identity, groups, source ACL version, query filters, retrieved chunks, citations shown, and model response ID. That lets an admin answer "why this document?" without exposing unrelated private documents.
Prompt 3: long-running coding agent service
Design a service that accepts repository tasks and runs a coding agent asynchronously.
Prompt details:
- Users submit a repo, branch, task prompt, and tool permissions.
- Jobs can run for 30 minutes and may need retries or human review.
- The agent can create commits, run tests, and leave artifacts.
- Users need live progress, cancellation, logs, and final diff review.
- Secrets and production systems must be protected.
Clarifying questions to ask:
- Is the service allowed to push branches, or should it only produce a patch artifact?
- Which tools need network access, and which should run offline?
- What happens when a user cancels during a tool call or test run?
Solution guide
Strong answer shape:
- API: create task, get task status, stream events, cancel task, list artifacts.
- State machine: queued, provisioning, running, blocked, review, failed, canceled, complete.
- Execution boundary: sandbox with scoped repo checkout, network policy, secret redaction, time and disk limits.
- Persistence: task row, run attempts, tool calls, logs, artifacts, branch/commit refs, cancellation flag.
- Recovery: checkpoint workspace state, retry transient infra failures, never replay unsafe writes without idempotency.
- Observability: per-tool latency, test results, token use, queue time, sandbox exits, reviewer actions.
- Rollout and safety: permission presets, allowlisted tools, audit log, kill switch by tool or model route.
Common misses: no cancellation semantics, no sandbox boundary, no artifact model, and no distinction between retrying a read and replaying a write.
Follow-up guide
If asked about retries, separate infrastructure retries from agent-action retries. Retrying a sandbox provision is safe; replaying a commit, comment, or external API write needs idempotency or human review.
If asked about cancellation, make it cooperative and durable: set a cancellation flag, stop scheduling new tool calls, terminate the sandbox after a grace window, persist partial logs, and mark artifacts as incomplete. If asked about secrets, say the sandbox receives scoped, short-lived credentials and the event log redacts values before storage.
Why is a support/debug path part of the design, not an afterthought?
Answer
Production AI products fail in ways users and operators need to inspect. Request IDs, traces, eligible documents, model choice, policy decisions, and overload state let support debug without guessing.
Drill 1: API gateway and rate-control plane
The gateway is the front door. It authenticates API keys, resolves workspace and organization limits, estimates request cost, routes to a model or queue, and emits a request ID that support can follow.

Design checklist:
- API keys map to workspace and organization.
- Limits apply across requests/minute, tokens/minute, model, and tier.
- Request IDs appear in responses and logs.
- Tenant or client limits return
429; fleet or provider overload returns503. Both carry a request ID and a bounded retry hint instead of silent queue growth. - Beta features are gated by explicit version or feature flags.
- Rollout has canaries, kill switches, and regression checks.
Use Python to sanity-check rate math before drawing capacity boxes:
1requests_per_minute = 4_000
2avg_input_tokens = 1_200
3avg_output_tokens = 450
4tokens_per_minute = requests_per_minute * (avg_input_tokens + avg_output_tokens)
5tokens_per_second = tokens_per_minute / 60
6
7print("tokens_per_minute:", tokens_per_minute)
8print("tokens_per_second:", round(tokens_per_second))1tokens_per_minute: 6600000
2tokens_per_second: 110000The estimate is an admission-time reservation, not the final bill. A streaming response needs a maximum output budget, then a reconciliation step when the stream ends:
1remaining_before = 10_000
2reserved_tokens = 1_800
3actual_tokens = 1_520
4
5remaining_after_admission = remaining_before - reserved_tokens
6remaining_after_reconcile = remaining_after_admission + (reserved_tokens - actual_tokens)
7
8print("after admission:", remaining_after_admission)
9print("after reconcile:", remaining_after_reconcile)1after admission: 8200
2after reconcile: 8480Drill 2: inference scheduler
For LLM serving, the scheduler is where latency, cost, and fairness meet. NVIDIA documents in-flight batching as a way to interleave context and generation work so GPUs are used more efficiently while latency stays under control.[1]
Serving physics first
Before any scheduler diagram, separate the two work phases:
| Phase | What happens | Typical bound | Metric it drives |
|---|---|---|---|
| Prefill | Process the full prompt into first-token KV | Often compute-bound | TTFT (with queue wait) |
| Decode | Emit one token at a time using cached KV | Often memory-bandwidth-bound | ITL / TBT and TPOT |
That split is why dual SLOs matter: a design can meet mean tokens/sec while still failing stream feel (bad ITL) or first-token UX (bad TTFT). Chunked prefill, separate prefill/decode pools, and prefill/decode disaggregation are answers to this physics, not optional jargon.
Metrics to name in the lab loop
- Time to first token (TTFT): arrival → first output token (queue + prefill + first token).
- Inter-token latency (ITL) / time between tokens (TBT): each gap after the first token; use the distribution (including tail) for stream feel.
- Time per output token (TPOT): mean decode pacing after the first token (not a synonym of ITL).
- Queue wait time before admission or first scheduled work.
- p95 and p99 end-to-end latency, plus p95/p99 of TTFT and ITL when the product streams.
- Fleet goodput / admitted RPS under dual TTFT and ITL SLOs (raw tokens/sec without SLO attainment is not capacity).
- Tokens per second only with a label: per-request decode rate, fleet aggregate, or prefill rate. Undifferentiated TPS is not a substitute for ITL or TPOT.
- GPU utilization.
- Error rate and overload rejections.
- Cost per successful request.
Serving toolkit (when to reach for each)
| Lever | What it fixes | Reach for it when |
|---|---|---|
| PagedAttention / paged KV | KV fragmentation and rigid block allocation | Long contexts or high concurrency thrash contiguous KV slots |
| Continuous (iteration-level) batching | Mid-flight admit/finish without waiting for the slowest sequence | Static batches waste GPU on finished or padded slots |
| Prefix / radix cache | Reuse shared prompt prefixes | Many requests share system prompts or multi-turn history |
| Speculative decoding | Extra draft tokens validated in parallel | Decode is bandwidth-bound and a smaller draft model is cheap |
| Chunked prefill | Cap prefill burst length so decode keeps streaming | Long prompts spike TTFT and stall in-flight decode |
| Prefill/decode disaggregation | Separate pools for the two phases | One pool fails to meet dual TTFT and ITL SLOs together |
Continuous batching and paged attention are orthogonal: continuous batching decides when sequences join or leave the batch; paged attention decides how KV blocks are laid out and freed. Name both mechanisms separately in an interview.
Model-fit scale math
If weights plus KV don't fit one GPU, say the next move in interconnect language: tensor parallelism (TP) for layer shards over high-bandwidth links (often NVLink within a node), pipeline parallelism (PP) across stages (more tolerant of inter-node links, pays bubble cost), and expert parallelism when MoE routing requires it. Always state the communication cost you accept for the fit.
Start capacity planning with a measured workload-specific fleet capacity, not a generic GPU estimate:
1measured_capacity_tokens_per_second = 125_000
2expected_demand_tokens_per_second = 110_000
3
4headroom = measured_capacity_tokens_per_second - expected_demand_tokens_per_second
5headroom_percent = headroom / measured_capacity_tokens_per_second * 100
6
7print("headroom_tokens_per_second:", headroom)
8print("headroom_percent:", round(headroom_percent, 1))1headroom_tokens_per_second: 15000
2headroom_percent: 12.0For an interview-sized overload policy, estimate queue wait before admission. This approximation deliberately stays simple: it treats tokens as homogeneous. A real scheduler tracks two resources separately (prefill token backlog vs active decode sequences / free KV blocks) and measured tail latency for each SLO.
Keep rejection ownership visible. 429 Too Many Requests says the caller exceeded a tenant, key, or client policy and could succeed after its quota window resets. 503 Service Unavailable says the service fleet or an upstream provider lacks capacity for an otherwise eligible request. A full tenant bucket can produce 429 even when GPUs are idle; a saturated fleet can produce 503 for a tenant that is under quota.
1def admit(queued_tokens: int, service_tokens_per_second: int, max_queue_wait_seconds: float) -> bool:
2 estimated_wait = queued_tokens / service_tokens_per_second
3 return estimated_wait <= max_queue_wait_seconds
4
5def admit_two_resource(
6 prefill_backlog_tokens: int,
7 prefill_tokens_per_second: int,
8 active_decode_sequences: int,
9 max_decode_sequences: int,
10 free_kv_blocks: int,
11 kv_blocks_needed: int,
12 max_prefill_wait_seconds: float,
13) -> bool:
14 """Sketch dual capacity: prefill backlog and decode/KV slots both must clear."""
15 prefill_wait = prefill_backlog_tokens / prefill_tokens_per_second
16 decode_ok = active_decode_sequences < max_decode_sequences
17 kv_ok = free_kv_blocks >= kv_blocks_needed
18 return prefill_wait <= max_prefill_wait_seconds and decode_ok and kv_ok
19
20print(admit(queued_tokens=20_000, service_tokens_per_second=125_000, max_queue_wait_seconds=0.25))
21print(admit(queued_tokens=50_000, service_tokens_per_second=125_000, max_queue_wait_seconds=0.25))
22print(
23 admit_two_resource(
24 prefill_backlog_tokens=10_000,
25 prefill_tokens_per_second=80_000,
26 active_decode_sequences=48,
27 max_decode_sequences=64,
28 free_kv_blocks=120,
29 kv_blocks_needed=16,
30 max_prefill_wait_seconds=0.25,
31 )
32)1True
2False
3TrueWhen should the system return 429 or 503 instead of queueing more work?
Answer
Reject before queueing violates latency or fairness. Use 429 when the caller's own quota or rate policy blocks admission. Use 503 when the shared fleet or provider is overloaded despite the caller remaining eligible. Include Retry-After when the service can estimate a safe retry window.
Drill 3: permission-aware retrieval
For enterprise retrieval, the critical rule is: don't retrieve private data and filter it after generation. Permission constraints must be part of candidate selection, ranking, and auditing.
Architecture pieces:
- Connector ingestion workers with backpressure.
- Per-document ACLs or delegated auth checks.
- Tenant-isolated indexes or strict metadata filters.
- Hybrid retrieval plus reranking.
- Citation output with source IDs.
- Deletion and retention jobs.
- Offline evals for recall and answer faithfulness.
- Support traces that show which documents were eligible.

The ACL snapshot and hybrid index both feed candidate selection. That connection matters: unauthorized chunks shouldn't reach the reranker or model context.
For sensitive sources, encode the freshness decision as a fail-closed policy:
1def source_is_eligible(snapshot_age_seconds: int, freshness_slo_seconds: int, sensitive: bool) -> bool:
2 if sensitive and snapshot_age_seconds > freshness_slo_seconds:
3 return False
4 return True
5
6print(source_is_eligible(snapshot_age_seconds=20, freshness_slo_seconds=60, sensitive=True))
7print(source_is_eligible(snapshot_age_seconds=90, freshness_slo_seconds=60, sensitive=True))
8print(source_is_eligible(snapshot_age_seconds=90, freshness_slo_seconds=60, sensitive=False))1True
2False
3TrueFilter tombstones and ACLs before ranking. The tiny fixture below makes the order visible:
1documents = [
2 {"id": "public-access-policy", "groups": {"employees"}, "deleted": False, "score": 0.82},
3 {"id": "finance-plan", "groups": {"finance"}, "deleted": False, "score": 0.99},
4 {"id": "old-handbook", "groups": {"employees"}, "deleted": True, "score": 0.95},
5]
6user_groups = {"employees"}
7
8eligible = [
9 document
10 for document in documents
11 if not document["deleted"] and document["groups"] & user_groups
12]
13ranked_ids = [document["id"] for document in sorted(eligible, key=lambda item: item["score"], reverse=True)]
14
15print(ranked_ids)1['public-access-policy']Drill 4: long-running coding agents
Long-running agent infrastructure has to persist intent, tool calls, artifacts, checkpoints, logs, and permissions. The main design risk isn't just failed execution. It's uncontrolled execution.
Cover:
- Task states: queued, provisioning, running, blocked, review, failed, canceled, complete.
- Checkpoints for resumability.
- Tool permission scopes and audit logs.
- Secret redaction.
- Git branch and conflict handling.
- Streaming progress.
- Cancellation and deadlines.
- Evals for task success and regression.
Write legal transitions down before discussing workers. That prevents a cancellation or retry from jumping into an impossible state:
1ALLOWED_TRANSITIONS = {
2 "queued": {"provisioning", "canceled"},
3 "provisioning": {"running", "failed", "canceled"},
4 "running": {"blocked", "review", "failed", "canceled"},
5 "blocked": {"running", "review", "failed", "canceled"},
6 "review": {"running", "complete", "canceled"},
7 "failed": set(),
8 "canceled": set(),
9 "complete": set(),
10}
11
12def can_transition(current: str, target: str) -> bool:
13 return target in ALLOWED_TRANSITIONS[current]
14
15print(can_transition("running", "review"))
16print(can_transition("complete", "running"))1True
2FalseRetries need a policy boundary too. Reads and sandbox provisioning can retry automatically. External writes need an idempotency key or review:
1def retry_mode(action: str, has_idempotency_key: bool = False) -> str:
2 if action in {"repo_read", "sandbox_provision"}:
3 return "automatic"
4 if has_idempotency_key:
5 return "automatic-with-idempotency"
6 return "human-review"
7
8print(retry_mode("repo_read"))
9print(retry_mode("create_commit"))
10print(retry_mode("post_comment", has_idempotency_key=True))1automatic
2human-review
3automatic-with-idempotencyDrill 5: eval gates and staged rollout
An evaluation monitor isn't just a dashboard. It turns fixed expectations and incident discoveries into a regression suite, then blocks launch when a critical slice fails.
1checks = {
2 "retrieval_recall": (0.94, 0.92),
3 "citation_faithfulness": (0.93, 0.95),
4}
5permission_leaks = 0
6
7failures = [
8 name
9 for name, (observed, minimum) in checks.items()
10 if observed < minimum
11]
12if permission_leaks > 0:
13 failures.append("permission_leaks")
14
15print("launch_allowed:", not failures)
16print("failures:", failures)1launch_allowed: False
2failures: ['citation_faithfulness']Keep online canaries reversible. Offline evals can pass while latency, provider errors, or permission failures regress under real traffic:
1def rollout_decision(error_rate: float, p95_latency_ms: int, permission_failures: int) -> str:
2 if permission_failures > 0:
3 return "rollback"
4 if error_rate > 0.01 or p95_latency_ms > 900:
5 return "hold"
6 return "expand"
7
8print(rollout_decision(error_rate=0.004, p95_latency_ms=720, permission_failures=0))
9print(rollout_decision(error_rate=0.004, p95_latency_ms=720, permission_failures=1))1expand
2rollbackCommon serving misconceptions
| Stale claim | Why it's outdated | Current answer |
|---|---|---|
| "Static batching with fixed padding is fine for LLM inference" | It pads every request to the longest sequence and holds the batch until the slowest one finishes, wasting GPU on padding and blocking short requests behind long ones | Two fixes, not one: continuous (iteration-level) batching admits and finishes sequences mid-flight so the GPU stays busy on real tokens; paged attention lays out KV in non-contiguous blocks so finished sequences free memory without reshaping the whole batch[1][2] |
| "Slurm is the only scheduler for multi-node GPU clusters" | Slurm still fits bare-metal HPC training, but it isn't the only option for serving or enterprise platforms | Kubernetes with KubeRay and Ray Serve orchestrates training and inference on the same substrate, sometimes in a hybrid setup that bursts training onto Slurm[3] |
| "Quantization always wrecks quality" | True for some naive low-bit schemes, not every calibrated method | FP8 roughly halves BF16 weight bytes; 4-bit methods such as AWQ and GPTQ can reduce weight memory further. Quality and speed remain workload- and runtime-dependent, so evaluate both before release.[4][5] |
For each correction, identify old approach's failure and replacement mechanism.
Failure modes to avoid
- Starting with implementation technology before naming user value.
- Caching without invalidation, privacy, or freshness.
- Monitoring without exact metrics.
- Ignoring overload and support/debug needs.
- Treating safety as a slogan instead of evals, permissions, staged rollout, and rollback.
- Forgetting that agent systems need reversible actions and audit trails.