What does a web framework send when a handler returns nothing, and how does it tell that from a handler that already wrote the response?
answer
- nothing returned, nothing to serialize
- empty response, but which status
- 204 versus a zero-length 200
- the handled flag skips mapping
- writing and returning is ambiguous
basics
~20 sWith no returned value there is no body model to serialize, so the framework sends an empty response: some frameworks use status 204, others an empty 200. Mapping is skipped entirely when the response is already marked handled.
solid answer
~50 sA handler that returns nothing gives the mapping step **no body model**, so there is nothing to hand a writer. Frameworks then take one of two routes, and they genuinely differ: send a **`204`** to say explicitly that there is no content, or send an **empty `200`** with zero-length framing. The second question is about who already wrote the response. Before mapping anything, the framework checks whether the response has been **marked handled or already started** — the handler was given the response and wrote the status, headers or body itself. In that case mapping is skipped, because appending a second body would corrupt the message. The practical consequence: a handler that writes the response *and* returns a value is an ambiguity, and different frameworks resolve it differently, so do one or the other and never both.
go deeper
Know that returning nothing produces an empty response and that the exact status is a framework default. Do not assume a message body can ride along with a no-content status.
Explain both outcomes, the handled/started check that skips mapping when the handler wrote the response itself, and why doing both in one handler is undefined.
Point out the client-side cost of an inconsistent empty-response convention across a service, and make no-return handlers state their status explicitly so an upgrade cannot shift it.
Decide the empty-response convention once for the whole surface and enforce it, so client libraries need one code path rather than one per team's habit.
## No return value means no body model Return-value mapping exists to turn a value into bytes. When a handler returns nothing — the function has no result, or the framework observes an explicit empty result — there is no value to give a writer. The framework still has to finish the response, so it must decide what an empty result means. Two outcomes are common, and this is one of the places frameworks visibly disagree: - **A no-content status (`204`).** Read as: the request succeeded and there is deliberately no representation to send. The response carries no body, and `204` tells the client not to look for one. - **An empty success (`200`).** Read as: the request succeeded, and the body is simply zero bytes, framed with a zero length. Both are legal HTTP; they say different things to a client. A client that branches on the status sees a clear signal from the first and has to test the body length under the second. ## The second path: the handler already wrote the response A framework hands the handler access to the response as well as the request. A handler can bypass the whole mapping step: set the status, write headers, push bytes and return nothing. If the framework then also ran its empty-result mapping, it would either overwrite a status that was already sent or append a second body — so before mapping it checks a **handled/started signal** on the response. That signal is set when anything has been written or when the handler explicitly declares that it produced the response itself. When it is set, mapping is skipped entirely. (What can and cannot still change once bytes are on the wire is the response object's own concern.) ## The decision table | Handler behaviour | Mapping step | Typical result | |---|---|---| | Returns a value | serializes it | body plus a default success status | | Returns nothing, wrote nothing | nothing to serialize | empty response: `204` or empty `200`, framework-dependent | | Returns nothing, wrote the response itself | skipped | exactly what the handler wrote | | Returns a value *and* wrote the response | ambiguous | framework-dependent: the write may win, the value may be appended, or it may error | That last row is the interesting one and the reason the question gets asked. A handler that does both has stated two intentions, and no framework can honour both. ## Why the empty-response choice matters in practice - **Clients that parse unconditionally.** A client that always parses the body chokes on zero bytes. A `204` lets it skip parsing; an empty `200` does not announce itself. - **A body sent with a no-content status is a protocol error.** If you map to `204`, nothing may write a body afterwards — including a well-meaning later stage that wants to attach a message. - **Delete and update endpoints.** These are where handlers most often return nothing, and where clients most often disagree about whether to expect a payload. Which status such an endpoint *should* answer with is an API-design decision; what the framework does when you say nothing is the mechanism described here. - **Contract drift.** If half your no-return handlers land on `204` and half on empty `200` because some were written with a wrapper and some without, clients grow two code paths for the same shape. ## Making it explicit The fix is the same as everywhere in return mapping: **state the intent instead of inheriting a default**. 1. Return a wrapper that names the status you mean, even when the body is empty. It documents the endpoint and it survives a framework upgrade that changes the default. 2. Decide one convention for empty responses across the service and apply it to every handler that returns nothing. 3. Never mix writing the response directly with returning a value in the same handler; pick one style per handler, and prefer returning values because they are easier to test. ## What a good answer sounds like Say that no return value means no body model, that the framework therefore emits an empty response and that the exact status is a framework default — `204` or empty `200` — not something the language's empty result dictates. Then explain the handled/started check that lets a handler take the response over completely, and finish with the ambiguity of doing both. That covers the mechanism, the variation and the trap.
- Why can a no-content response never carry a body, even a short message?Because the status itself announces that no representation follows, so a client is entitled to stop reading at the headers and reuse the connection. Bytes written after it are either ignored or misframed as the start of the next message. If you need to say something, use a status that permits a body instead.
- A handler writes the response directly and also returns a value. What should you expect?Framework-dependent behaviour, which is why it counts as a defect. Some skip mapping because the response is already marked handled, some append the serialized value to what was written and produce a malformed body, some raise. Choose one style per handler — returning a value is the more testable of the two.
saying these in an interview costs you the question
- Says every framework answers 204 for a handler that returns nothing
- Thinks an empty 200 and a 204 are interchangeable to clients
- Attaches a short message body to a no-content status
- Writes the response directly and returns a value from the same handler
- Cannot say how the framework knows the response was already written