DevOpsInterviewPrep logo
← ⚙️ Infrastructure at Scale
Foundational

REST API contracts: HTTP methods, conditional updates and compatibility

Explain REST API contracts through method semantics, ETag preconditions, structured errors and pagination. Work through concurrent updates and compatibility failures without confusing idempotency with identical responses.

TL;DR: An API contract tells clients which effect a request has, how to detect conflicts and how to recover from uncertain outcomes. Combine HTTP method semantics with explicit preconditions and application rules; a resource-shaped URL alone does not make a reliable interface.

Choose methods that match the intended effect

HTTP defines safe methods as read-oriented in their intended semantics, while idempotent methods have the same intended effect when repeated. GET is safe; PUT and DELETE are idempotent without being safe. Logging each request does not violate these definitions. A repeated DELETE can return a different status while leaving the same resource absent. See HTTP semantics.

POST usually needs an application-specific retry contract when creating effects. The existing idempotency concept covers operation identity and uncertain external effects. PATCH requires equally explicit semantics: setting a field to a value and incrementing that field are different operations. RFC 5789 does not make PATCH inherently idempotent.

Contract choiceUseful behaviorFailure to prevent
PUT with a full representationReplace defined resource stateSilently treating omitted fields as unchanged
PATCH with a documented media typeApply a specific partial changeAssuming every patch can be repeated safely
Conditional updateReject a stale editing basisLast writer erases another client's work
Stable pagination contractContinue a bounded traversalDuplicates or omissions under concurrent change

Use a precondition for concurrent editing

Imagine two automation jobs reading the same deployment configuration. One changes the replica count; the other changes a timeout. Both submit a replacement based on the old document. Without a precondition, the later replacement can erase the earlier change.

An entity tag identifies the representation the client read. If-Match makes a state-changing request conditional on a matching current tag, using strong comparison. A stale precondition normally yields 412. The client then reads the current representation and decides whether its intended change still applies.

rendering diagram…

The following Python model isolates the compare-and-update rule. It is single-threaded teaching code; a real service needs an atomic database condition or transaction so another write cannot slip between comparison and update.

state = {"version": 7, "replicas": 3}

def replace(expected, replicas):
    if expected != state["version"]:
        return 412
    state.update(version=expected + 1, replicas=replicas)
    return 204

assert replace(7, 4) == 204
assert replace(7, 8) == 412
assert state == {"version": 8, "replicas": 4}

Never fetch a new tag and blindly resend the old full document. That satisfies the precondition syntactically while recreating the lost update. Reapply the intended change against current state, or ask the caller to resolve the conflict.

Give failures a usable representation

Problem Details supplies the application/problem+json format. A problem type identifies the class of failure; an occurrence identifier can help support correlate one request. Keep machine-readable fields stable and avoid embedding private stack traces or credentials in explanatory text.

For example, a deployment API might return a conflict explaining that the target environment is already running a migration. The client should use the defined problem type and retry guidance, not parse the English sentence. A validation failure, authorization denial and transient overload need different client actions, even if all appear on the same dashboard as unsuccessful requests.

Define traversal and evolution explicitly

For a changing collection, offset pagination can skip or repeat items when earlier rows are inserted or deleted. A cursor can encode a stable ordering boundary, but snapshot consistency is a separate promise. Specify sort order, tie-breaking, filters and token expiry. Google's pagination design guidance also warns that a continuation token is not authorization; every request still needs access checks.

Adding an optional response field can be compatible for tolerant readers yet break a client that rejects unknown fields. Changing an enum's possible values, pagination order or timeout behavior can be equally disruptive. Exercise supported client versions against candidate responses and document the actual compatibility contract. A version number cannot compensate for absent consumer tests.

Self-check: a job receives 412 while trying to set replicas to five. Should it retry unchanged until it succeeds? No. Retrieve current state, determine whether five replicas is still the desired change, and submit it with a current precondition. Keep retry limits and the overall operation deadline bounded.

RELATED CONCEPTS
PRACTICE THIS IN REAL QUESTIONS