Prompt and scope
This question tests whether a backend engineer can give “batch” precise execution and result semantics. Assume a customer wants to update order labels or user settings in bulk; items may be independent or share quotas, versions, or dependencies, and a network timeout can occur after the server has completed some items. You must define synchronous and asynchronous boundaries so a client can continue safely.
It fits backend engineers, API designers, and platform engineers. Focus on atomicity, per-item state, request identity, idempotent retry, authorization and resource limits, response compatibility, and recovery rather than a specific REST, gRPC, or queue choice. State the default behavior, how partial success is explicitly selected, and the difference between complete failure, partial failure, and an unknown outcome.
What the interviewer is assessing
A strong answer asks whether items depend on one another before choosing atomic processing, opt-in partial success, or an asynchronous operation. It does not return only HTTP 200 and a failure count; it maps every input to a stable result, error class, and retryability. Google AIP-234 notes that changing an existing synchronous batch method to partial success can break clients and recommends detailed per-index failure state; Stripe’s idempotency guidance binds retries to the same parameters and preserves the first result. Cover limits, audit, and monitoring too.
Clarifying questions to ask
- Are there ordering or transactional dependencies between items? Must the business be all-or-nothing?
- Does processing create external side effects, and can an idempotency key make replay safe?
- Does the client need a synchronous result, or can it receive an operation and poll or subscribe to progress?
- What are the batch limit, body-size, timeout, tenant quota, and fairness requirements?
- Which errors are retryable, which require input changes, and how can an unknown outcome be queried?
A 30-second answer framework
“I would separate atomic batches from partial-success batches and default to the safer behavior; partial success is enabled only when the client opts in and the business allows it. Every input gets a stable index or client request ID, while the server records batch-level and item-level idempotency state, result, and error class. Synchronous work is bounded; larger work creates an operation that runs in shards and exposes progress. The response distinguishes success, permanent failure, transient failure, and unknown state, and retries reuse the same item key. I would validate the design with rate limits, audit records, metrics, and fault injection to prove that side effects are not duplicated or lost.”
Step-by-step answer
Step 1: Choose the atomicity model
If items share an indivisible business invariant, such as both sides of a transfer succeeding together, use an atomic batch transaction or reject the batch interface. If items are independent, consider partial success. Do not hide the model behind “best effort”; the client must know whether all succeeded, all failed, some completed, or the server cannot confirm the outcome.
Step 2: Define request and item identity
Include a batch ID, item array, and client-generated item request ID. An item ID is stable within its business scope. A duplicate submission with the same ID must compare important parameters; different parameters should produce a conflict rather than a silent overwrite. A batch ID supports tracing but cannot replace item idempotency because a partial retry may contain only failed items from the original batch.
Step 3: Set the synchronous/asynchronous boundary
Small batches can return final per-item results synchronously, with a total time and resource budget. Large batches or operations that call external systems should return an operation ID, execute in bounded shards, and persist progress. A client queries completed, processing, retryable, and permanent-failure counts; after a disconnect it queries instead of starting side effects again.
Step 4: Design a parseable response
The response must let a client find a result by input identity, even if the server reorders work. Use a stable index and client ID; include a machine-readable error code, retryability, possible state change on retry, and a safe user message. For example:
{
"batch_id": "b_123",
"status": "PARTIAL",
"results": [
{"index": 0, "request_id": "r_0", "status": "SUCCEEDED"},
{"index": 1, "request_id": "r_1", "status": "FAILED", "error": {"code": "VERSION_CONFLICT", "retryable": false}}
],
"next_page_token": null
}Google AIP-234 recommends a failed_requests map from input index to detailed status for asynchronous batch updates. It avoids forcing the client to maintain a request-ID-to-request map and avoids echoing sensitive request bodies. If an existing synchronous API needs partial-success semantics, publish a new version or negotiate with an explicit field so old clients do not interpret a success status as completion of every item.
Step 5: Handle idempotency, retry, and unknown outcomes
The server may reject invalid parameters before side effects begin without saving an idempotent result. Once execution starts, save the result or a queryable in-progress state. After a network timeout, the client must not guess and repeat the whole batch; it reuses the batch and item keys, queries unknown items, and retries only clearly retryable items. Use backoff and jitter for transient errors, require input changes for permanent errors, and return a conflict when a reused key carries different parameters.
Step 6: Isolate resources and ordering
Split work into bounded shards and cap concurrency per tenant, batch, and dependency. Execute dependent items by a topology or explicit phase; run independent items in parallel with a shared retry budget to prevent a failure storm. Check authorization and quota before each item, so a batch endpoint cannot bypass the policy of the single-item API.
Step 7: Define error, cancellation, and recovery semantics
Classify input validation, authorization, version conflict, quota, transient dependency, and unknown-result errors. Cancellation stops items that have not started; completed side effects cannot be pretended to roll back. If the business needs reversal, provide a separate compensation operation. Persist background tasks, results, and the original request so a restart resumes from item state rather than executing every item again.
Step 8: Verify consistency and operational visibility
Test all-success, all-failure, mixed outcomes, duplicate requests, parameter conflicts, timeout-then-query, dependency flapping, cancellation races, and worker restarts. Monitor batch success, per-item error classes, unknown states, retry amplification, queue age, processing latency, quota rejects, and duplicate submissions. Audit the batch, item, actor, authorization decision, and final result so support staff can explain exactly which items completed.
Design trade-offs and boundaries
Partial success is not automatically the more advanced choice. It fits independent items that customers can repair one by one; balances, inventory, and cross-resource invariants are safer with atomicity or an explicit workflow. HTTP 207 Multi-Status can carry several resource statuses, but RFC 4918 defines it for WebDAV. A general JSON API should not assume clients understand partial results merely because it returns 207. Put item semantics in a stable response body and choose status codes with existing-client compatibility in mind.
When should you choose atomic batches?
Choose atomicity when any item failure makes the aggregate state invalid or compensation is unacceptable. Use a transaction, preflight, or workflow, while acknowledging that sharded work and external side effects do not share a database transaction; they may require reserve, commit, and compensation phases.
When should you choose an asynchronous operation?
Return an operation when duration is unpredictable, the batch is large, external calls are involved, or a client should not hold a connection open. Its state must be re-queryable, and results should be paginated. Progress must not call “accepted” “completed.”
Failure drills and evolution plan
Pilot with a limited tenant set, record item states and retry behavior, and then raise the batch limit gradually. Inject a network break after item 30 succeeds, a dependency that returns 503 continuously, the same key with different parameters, and an operation-worker restart. Expand quotas or enable partial success only after clients can query unknown outcomes and the server proves it does not duplicate side effects.
How do you evolve a synchronous API to partial success?
Keep the old version’s atomic semantics and add a version that returns an operation and per-item failures. Alternatively, require an explicit returnpartialsuccess field and preserve the old behavior when it is absent. Document status codes, response fields, retry rules, and deprecation dates so clients do not silently change their interpretation.
How do you evaluate client correctness?
Observe whether clients persist item IDs, retry only retryable failures, query unknown outcomes, and avoid duplicate side effects and invalid retries. Provide parsing and query helpers in important SDKs, while the server still tolerates unknown fields and duplicate requests.
Common mistakes and follow-ups
Returning HTTP 200 with a failure count
An old client may treat partial completion as complete, and it still cannot know which items to retry. The response needs overall state, item identity, error class, and next action.
Retrying the entire batch on any failure
That can repeat side effects that already succeeded. Query batch and item idempotency state first, retry only explicit transient failures, and use a new business request ID when parameters change.
How should results be ordered?
Associate results by input index or stable request ID, not completion order. Keep the identity when results are paginated so the client can merge pages safely.
Can you roll back after one item succeeds and the batch is cancelled?
Cancellation affects only items that have not started. External side effects already made need a compensation API or manual handling; “batch cancelled” is not a rollback guarantee.
How do you stop the batch endpoint bypassing authorization?
Check tenant and operation permission at batch level, then recheck resource ownership, version, and field permissions before each item. Batching changes scheduling, not authorization scope.
How do you explain the final partial failure?
Provide an auditable batch ID, item ID, status, error code, retryability, and timestamp. Give support a safe business explanation and retain trace and dependency errors for engineering diagnosis.