skip to content

questions

5

In a JSON request body, what is the difference between an absent field, a null field, and an empty-string field?

level: juniorimportance: must knowfreq 72%

answer

  1. three states, not two
  2. silence versus a stated no-value
  3. empty is a value, not a gap
  4. presence is separate from content
  5. decoding usually flattens the three

basics

~20 s

Absent means the sender said nothing about the field. Null means the sender said it has no value. Empty means it has a value that happens to be zero-length. Three different statements, and many encodings collapse them.

solid answer

~40 s

They are three different messages. An **absent** key is silence: the sender made no statement about that field at all. A key present with `null` is an assertion — the sender says this field has no value. A key present with `""` is an ordinary value that happens to have zero length, and so is `0` or `false` for other types. A self-describing text encoding can represent all three, so the distinction survives as long as nothing re-encodes the body along the way. The difficulty is that most decoding layers throw the distinction away, mapping both absent and null onto the same in-memory nullable, which is exactly the information a partial update needs.

go deeper

for a junior

Be able to name the three states out loud and give one example of each in a request body. Knowing that absent and null are different messages is the whole of the junior expectation.

for a middle

Explain what happens to the three states during decoding, and why a nullable in-memory field cannot hold all of them. Name at least one mechanism that keeps presence separate from content.

for a senior

Show that you have debugged this: an intermediary that re-encodes and drops nulls, or an encoder that omits default-valued fields, silently turning an assertion into a silence partway down the path.

for a principal

Frame it as a contract decision rather than a coding one. Decide which of the states an endpoint accepts at all, write that into the contract, and reject the rest instead of letting each reader invent its own reading.

## Three states, not two A field can reach a reader in more states than most code models. Take a profile record carried as a JSON object: - **Absent** — the key is not in the object at all. The sender made no statement about this field. - **Explicitly null** — the key is present and its value is the null literal. The sender states, on the record, that the field has no value. - **Empty** — the key is present with a zero-length value: `""`, `[]`, `{}`. The field has a value, and that value is empty. - **Default-valued** — the key is present with the type's zero: `0`, `false`. Another ordinary value, and a very common one. The first is a statement about the **message**; the others are statements about the **value**. Everything else follows from that split. Absence is silence, null is an assertion of no-value, and empty and default are values that merely look like nothing. ## Why the difference is worth money The distinction pays off wherever a message is allowed to be partial. If a client sends only the fields it is changing, then absence has to mean *leave this alone* — otherwise the client could never change one field without resending the whole record. But once absence is spoken for, the request needs some other way to say *clear this field*, and an explicit null is the obvious candidate. Collapse the two and the endpoint becomes unable to express one of its two most ordinary operations. It also matters for validation. A rule like "nickname must be at most 30 characters" applies to a present value; it says nothing about an absent field. A rule like "a nickname, once set, may not be blanked" is about the null case only. Writing both against a single nullable variable produces a validator that cannot tell a client who is changing nothing from a client who is clearing something. ## Which encodings keep which distinction | Encoding class | Absent vs present | Explicit null | Empty vs unset | |---|---|---|---| | Self-describing text (JSON-style objects) | Kept — the key is there or it is not | Kept — a first-class literal | Kept — `""` is a present value | | Schema-driven record encodings with a fixed field list | Not expressible within one schema version; absence exists only across versions | Only if the field's type is a union that admits null | Kept, because every field is written | | Tag-based binary encodings that skip default values | Kept via the tag's presence, unless the value equals the type's default | Usually not a value; modelled by a wrapper or a presence bit | Collapsed for default-like values unless presence is tracked separately | The row that surprises people is the last one. If an encoder omits any field whose value equals the type's default, then a field set to the empty string is simply not written, and the reader reconstructs the empty string from the schema. Unset and empty arrive identical, because on the wire they *are* identical. ## What the decode step does to it Even when the bytes carry all three states, the layer that turns bytes into an in-memory object usually flattens them: 1. A missing key becomes the same nullable value as an explicit null. 2. That nullable is handed to business code, which now sees two states where three arrived. 3. The information is not recoverable later — by the time the object exists, the bytes are gone. The fix is to keep presence and content as two separate facts: a presence flag alongside the value, a wrapper object whose own presence is the signal, or a list of the field paths the sender intends to update. Each keeps the reader able to answer *did they mention it?* separately from *what did they say?* A second trap is the intermediary. A service that decodes a body, maps it to objects and re-encodes it will emit whatever its encoder emits — and an encoder that drops nulls turns an explicit null into an absence somewhere in the middle of the path. The distinction is only as strong as the weakest hop. ## What to watch for - Do not treat empty as a synonym for absent. An empty string is a value someone chose to send. - Do not assume every encoding can say null; in some, null is a type you must opt into. - Decide, once, what each state means for a given endpoint, and write it into the contract rather than leaving it to each reader. - If only two of the three states are meaningful for a field, say so in the contract and reject the third instead of silently reinterpreting it.

  • Why does the layer that maps a request body into an in-memory object so often destroy this distinction?
    Because most object models have exactly one way to say nothing: a nullable field. A mapper reading a body sets that field to null when the key is missing and also when the key is present with the null literal. The two inputs converge on one output, and nothing downstream can separate them again unless the mapper is asked to track presence alongside the value.
  • Is an empty string ever a legitimate way to mean cleared?
    Yes, if the contract says so for that field and the field has no other use for the empty string. It is a sentinel, and it works only while the empty string is not a value a user could legitimately want. The moment it becomes legal input, the sentinel and the real value are indistinguishable, which is why an explicit null or a presence flag is the more durable choice.
  • If a field is absent, can the reader conclude the sender's contract version lacks it?
    No. Absence has several possible causes: the sender omitted it deliberately, the sender's contract version does not define it, or an intermediary dropped it while re-encoding. The bytes carry the absence but not its reason. If the reason matters, the contract must carry it explicitly — for example as a list of fields the sender intended to set.

On a paper form, a blank line, a line with the word none written in it, and a line reading N/A are three different answers. Only the blank one leaves the clerk free to keep whatever was already on file.

saying these in an interview costs you the question

  • Treats null and a missing key as the same input
  • Says an empty string means the field was never set
  • Assumes every encoding can represent an explicit null
  • Thinks a missing field always means clear this value
  • Believes the decoder preserves absence without being asked to
open as a page

In a partial-update request, how does a server tell clear the nickname from leave the nickname alone?

level: middleimportance: must knowfreq 58%

basics

~20 s

By tracking presence separately from value. Absence means leave it alone; an explicit null, a wrapper whose presence is the signal, or a list of the field paths being updated means the sender touched the field and wants it cleared.

open as a page

When designing a new wire contract, what do you commit to by declaring a field required rather than optional?

level: seniorimportance: should knowfreq 44%

basics

~20 s

You commit every writer, forever, to supplying the field, and every reader to failing the decode when it is missing. Required removes the reader's ability to express unknown, and relaxing it later means updating every reader first.

open as a page

Once a profile field can arrive absent, null, or set, what does that three-valued shape force on every downstream consumer?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Every consumer inherits a three-branch decision and a three-valued logic where comparisons return unknown. The containment is to resolve the three states once at the boundary into one decided action, and pass that on instead of the raw states.

open as a page

In a partial-update request, why is clearing a list field harder than clearing a scalar field like a nickname?

level: seniorimportance: nice to knowfreq 26%

basics

~20 s

Because an empty list is already the cleared value, so null adds nothing a scalar's null adds, and encoders that omit empty collections make set-to-empty look identical to never-mentioned. The fix is to state presence separately.

open as a page