Prompt and context
You own a team-profile API. A client may change only displayName or submit a complete profile, and mobile retries are common. Explain when to use PUT or PATCH, how to define omitted fields and null, and how to handle conflicts, atomicity, idempotent retries, and compatibility.
Greenroom's 2026 backend interview list explicitly includes the difference between PUT and PATCH, idempotency, and method choice. RFC 5789 defines a PUT entity as a new complete representation of the resource, while a PATCH entity contains instructions to apply to the current resource. This question is not tied to a specific company.
What interviewers assess
An average answer memorizes “PUT is full, PATCH is partial.” A strong answer defines the resource contract, says whether omitted fields stay unchanged, says whether null clears a field, explains If-Match, and states whether a failed request leaves the entire change unapplied. Follow-ups usually cover duplicate requests, lost responses, unknown fields, audit events, and old clients.
The core signal is connecting HTTP semantics to database updates and concurrency control instead of treating PATCH as a smaller PUT.
Clarifying questions
- Can PUT create a resource? This prompt updates an existing profile; if creation is allowed at a stable URI, define ownership and duplicate-request behavior.
- Does the client send a complete resource or a change document? Use PUT for a complete representation and PATCH for field operations or a partial representation.
- What do omission and
nullmean? Here omission preserves the value andnullclears nullable fields; non-nullable fields reject it. - Can concurrent updates overwrite one another? This prompt rejects silent overwrite and requires
ETag/If-Matchor a database version condition. - Are there side effects? Search indexing, audit events, and notifications must follow the committed state; asynchronous effects are not part of HTTP atomicity.
30-second answer
“I define PUT as replacing a resource with a complete representation, for clients that own a full snapshot. PATCH carries partial changes such as a displayName update. PATCH must define omission versus null and reject stale versions rather than letting an old form overwrite new data. The server validates the whole document and applies it atomically in one database transaction, then emits a versioned asynchronous event. Clients retry with the same request semantics and If-Match. If the change is a command rather than a resource update, I use an action endpoint instead of overloading PATCH.”
Step-by-step answer
Step 1: Write both methods as resource contracts
| Dimension | PUT | PATCH |
|---|---|---|
| Request meaning | The body is the resource's new complete representation | The body is a change instruction or partial representation applied to the current resource |
| Omitted field | Usually means the client supplied complete state; it must not silently preserve old fields | Must explicitly mean preserve or invalid |
| Idempotency | Repeating the same representation should reach the same resource state | Not guaranteed by the method, but a specific patch document can be idempotent |
| Typical use | Sync a complete editor snapshot or replace configuration | Change one field with JSON Merge Patch or JSON Patch |
The key property of PUT is not body size; it is the client's claim that the representation is complete. If a client only knows a few fields but sends PUT, the server may interpret missing fields as deletion or defaults. PATCH is not automatically safe: validation, authorization, and side effects still apply.
Step 2: Define the PATCH document and three field states
PATCH /v1/teams/t_123/profile HTTP/1.1
Content-Type: application/merge-patch+json
If-Match: "profile-v17"
{"displayName":"Design Platform","avatarUrl":null}This prompt uses a JSON Merge Patch style: displayName is replaced, avatarUrl: null clears a nullable field, and an omitted field stays unchanged. If the business needs element-level array operations, moves, or tests, use a constrained JSON Patch operation list instead.
Parse the document first, then validate an allowlist, types, lengths, authorization, and domain invariants. Never map arbitrary client JSON paths directly to database columns. Unknown fields may be rejected or ignored within a versioned contract, but the behavior must be fixed and documented.
Step 3: Prevent silent overwrite with a version condition
GET /v1/teams/t_123/profile HTTP/1.1
ETag: "profile-v17"
PATCH /v1/teams/t_123/profile HTTP/1.1
If-Match: "profile-v17"
Content-Type: application/merge-patch+json
{"displayName":"Design Platform"}The database update carries a version predicate: only version 17 may write and advance to 18. A mismatch returns 412 Precondition Failed; the client rereads, shows a conflict, or regenerates its patch. It cannot force an old version over newer data. If If-Match is mandatory and missing, 428 Precondition Required can make the concurrency policy explicit.
PATCH must also be atomic as a document: field A succeeding while field B fails is not an acceptable half-update. The transaction, validation phase, and unique constraints decide whether the change commits. Side-effect events should be written to an outbox and published after commit with the resource version.
Step 4: Handle duplicate requests and unknown outcomes
Repeated PUT requests with the same complete representation converge to the same state. PATCH has that property only when the operation is repeatable: setting displayName is idempotent, while increment seats by 1 is not. A non-idempotent PATCH needs a request ID, a version condition, or an operation that expresses a target state.
After a network failure, the client does not know whether the server committed. Use a stable request ID with a recorded fingerprint and result, or retry with If-Match and a target state. Do not blindly replay a patch that appends side effects. After the resource commits, event consumers handle indexing and notifications and deduplicate by event ID.
Step 5: Decide when PUT/PATCH is the wrong shape
“Publish the profile,” “recalculate permissions,” and “send an invitation” are commands, not replacement or partial resource representations. An action endpoint such as POST /profile:publish expresses permissions, audit, retries, and asynchronous state more clearly. A PATCH-shaped command makes clients misunderstand duplicate execution and side effects.
High contention or cross-resource transactions may also need a domain command. Changing a member from editor to owner requires quota and audit checks; changing one string with PATCH does not express those invariants.
High-quality sample answer
“I would expose both PUT and PATCH with different contracts. PUT accepts a complete team-profile representation; an omitted field is a contract error or an explicit default, never an accidental ‘leave unchanged.’ PATCH accepts a restricted Merge Patch and only allowlisted fields; omission preserves a value and null clears it only when the field is nullable.
“Both methods use ETag and If-Match. The database performs a version-conditional update and returns 412 on a mismatch, so an old client cannot overwrite newer data. The PATCH document is validated completely and applied in one transaction; an outbox publishes a versioned indexing event after commit. A set-field PATCH can be idempotent; increments need a request ID, a condition, or a target-state operation. Publishing and inviting are POST actions because they have explicit side effects. I would test duplicates, lost responses, omission/null, conflicts, unknown fields, partial failures, and old-client compatibility.”
Common mistakes
- Symptom → Calling PUT “update any fields” → Why it fails → Callers cannot know whether omitted fields disappear → Fix → Make PUT a complete representation and use PATCH for partial changes.
- Symptom → Claiming PATCH is inherently idempotent → Why it fails → RFC 5789 does not guarantee it; repeated increments change state → Fix → Make only target-state patches repeatable and add conditions or deduplication to other operations.
- Symptom → Mapping arbitrary JSON keys to columns → Why it fails → Field authorization, type validation, and cross-field invariants are bypassed → Fix → Use an allowlist and explicit domain validation.
- Symptom → Letting the last writer win after a version conflict → Why it fails → An old form silently overwrites new data → Fix → Use
If-Matchand a version predicate, returning 412 on conflict. - Symptom → Calling the search service synchronously after commit → Why it fails → A lost response or process crash splits the resource and index → Fix → Write an outbox in the transaction and publish asynchronously with event-ID deduplication.
Follow-ups and responses
What if the product wants omitted PATCH fields to clear values?
That turns PATCH into another complete-representation contract and blurs it with PUT. Use PUT, or define a clearly named field-set replacement media type; the important part is making the consequence of an omitted field explicit.
What if two clients both read version 17 and edit different fields?
Return 412 by default and let the client merge and retry, because the server cannot assume the edits are independent. Field-level merging is safe only when the domain explicitly allows it and the patch includes field versions or test operations.
What if PATCH triggers billing or notifications?
Separate the resource update and side effect into auditable steps: commit the resource and outbox together, then let an idempotent consumer process the event. If the side effect requires explicit user intent, make it a separate POST command with an asynchronous operation status.