skip to content

questions

page 1 of 2

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

A content-addressed store keys artefacts by a digest of their encoded bytes, so why can two services encoding the same value produce different ids?

level: middleimportance: must knowfreq 62%

basics

~20 s

Most encodings give the encoder freedom — member order, whitespace, number spelling, escapes, omitting defaults — so one value has many valid byte sequences. A canonical encoding fixes every one of those choices so the digest depends only on the value.

open as a page

What happens to unknown fields when an older reader decodes a newer producer's message and re-emits it?

level: middleimportance: must knowfreq 66%

basics

~20 s

They are usually dropped. Decoding keeps only the fields the reader's own schema version names, and the output is rebuilt from that smaller in-memory value, so the newer producer's data disappears with no error anywhere. Preserving it requires a deliberate side buffer.

open as a page

Why does framing by a delimiter byte force the writer to escape or re-encode the payload?

level: middleimportance: must knowfreq 54%

basics

~20 s

A delimiter means 'the message ends here', so any occurrence of that byte inside the payload would end the frame early. The writer must escape it, or restrict the payload to an alphabet that excludes it.

open as a page

Framing turns a byte stream into messages: on a connection whose reads never align with message boundaries, what must the reader's framing loop do?

level: middleimportance: must knowfreq 66%

basics

~20 s

A stream connection carries bytes, not messages, so the reader keeps a buffer across reads: append each chunk, test whether a complete message is present from its length prefix or delimiter, consume exactly that message, and retain the remainder.

open as a page

What does an IDL as the source of truth give a team that a code-first, reflection-derived wire contract does not?

level: middleimportance: must knowfreq 68%

basics

~20 s

An IDL makes the wire contract an artefact that exists before and apart from any implementation: it is versioned, reviewed and compiled into stubs for every consumer, so no single codebase's types can silently redefine it.

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

Why can a 64-bit ledger entry identifier lose its last digits when the consumer decodes every number into a binary floating-point value?

level: middleimportance: must knowfreq 70%

basics

~20 s

A 64-bit binary floating-point value carries only 53 bits of significand, so whole numbers above 2^53 (about 9.0 quadrillion) round to the nearest representable neighbour. The identifier decodes to a different number, with no error raised.

open as a page

Why should a ledger entry's monetary amount not cross the wire as a binary floating-point field?

level: middleimportance: must knowfreq 75%

basics

~20 s

Binary floating point represents values as fractions over powers of two, so amounts like 0.10 have no exact representation and every decode introduces a tiny error. Carry money as an integer count of minor units, or as a decimal string, with the currency.

open as a page

An intermediary decodes each message into objects and re-encodes it before forwarding; why does that break a downstream integrity check over the bytes?

level: seniorimportance: must knowfreq 58%

basics

~20 s

Re-encoding rebuilds bytes from a decoded model, which has already dropped what the check covered: member order, spacing, number spelling, presence. The check is over bytes, so identical meaning still yields different bytes and fails.

open as a page

How do you choose the default for a new field so older readers that ignore it stay correct?

level: seniorimportance: must knowfreq 55%

basics

~20 s

Pick the value that reproduces the behaviour the system had before the field existed, and check that the same value is safe when the field is lost in transit. If no single value satisfies both, the state does not belong in a defaulted field.

open as a page

A team renames a field on a domain type during a refactor and consumers break — why did a code-first serializer let that happen?

level: seniorimportance: must knowfreq 58%

basics

~20 s

Because the serializer derives the wire key from the identifier at run time, the rename edited the contract. Nothing in the producer's build knows the difference between an internal refactor and a wire change, so no gate fired.

open as a page

Why can a reader that substitutes implicit defaults not tell a writer's explicit zero from an unset field?

level: middleimportance: should knowfreq 45%

basics

~20 s

Because the default is materialised during decoding: an absent field and a field carrying the type's default produce the identical in-memory value. Presence information is destroyed before any application code runs, so no later check can recover it.

open as a page

Which constructs would you keep out of a shared IDL contract that several ecosystems generate readers from, and why?

level: middleimportance: should knowfreq 42%

basics

~10 s

Keep out anything whose meaning is borrowed from one ecosystem's type system: runtime-typed polymorphism, deep or recursive nesting, collections whose ordering or key rules differ, and names that collide with a target's reserved words.

open as a page

Two producers write the same logical record, one omitting a field and one sending its default; why do their content ids differ?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Both spellings are valid — one omits the field, the other writes it at its default — so the bytes differ and so do the ids. A canonical profile must pick one presence rule and apply it before hashing.

open as a page

Which reader behaviour for unknown fields — strict rejection or lenient ignoring — would you write into a wire contract, and when?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Strict rejection where a human or a tool authored the document and a misspelled field must never be silently discarded; lenient ignoring on machine-to-machine streams with many independently deployed producers. Either way, the contract states which, rather than leaving it to each decoder's configuration.

open as a page

What belongs in a message envelope that an intermediary must read without decoding the payload it wraps?

level: seniorimportance: should knowfreq 49%

basics

~20 s

Whatever a hop needs in order to route, dispatch, drop or trace a message it will never decode: a message identifier, a content type and schema identifier, correlation and causation metadata, a timestamp, the payload length and an integrity check.

open as a page

In a shared contract repository, how can generated client stubs drift from the contract version they claim to implement?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Drift enters wherever generation is not reproducible from a pinned input: stubs built from a working copy instead of a tagged contract, a different generator version, hand-edited generated files, or an artefact whose stamped version is not the one it was built from.

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

Two consumers of one ledger feed render the booked-at timestamp in January 1970 and tens of thousands of years ahead — what single defect explains both?

level: seniorimportance: should knowfreq 52%

basics

~20 s

The contract carries a bare epoch integer without naming its unit. One consumer reads a seconds value as milliseconds and lands days after 1970; the other reads a milliseconds value as seconds and lands far in the future. The factor is one thousand.

open as a page

Why does a UTC offset fully pin a ledger entry's booked instant but not a payment scheduled for 09:00 local next year?

level: seniorimportance: should knowfreq 46%

basics

~20 s

An offset is arithmetic about one moment, so a past instant plus its offset is unambiguous forever. A future local time is defined by rules that can change, so it needs a zone identifier and late resolution, not a frozen offset.

open as a page

Designing a shared envelope for a long-lived message stream, how do you decide which facts belong outside the encoded payload?

level: principalimportance: should knowfreq 41%

basics

~20 s

Promote a fact to the envelope only when a participant that cannot decode the payload still needs it, and when it describes the transfer rather than the business event. Everything else stays in the body, with one authority per fact.

open as a page

Would you publish prebuilt generated stubs from a contract repository, or have each consuming team generate from the IDL at build time?

level: principalimportance: should knowfreq 33%

basics

~20 s

It is a trade between uniformity and autonomy. Publishing stubs centralises the generator version and spares consumers a toolchain, at the cost of the contract repository owning a build pipeline per ecosystem; consumer-side generation reverses both.

open as a page

How do you decide which numeric and temporal scalars in a cross-team wire contract must cross as strings rather than native numbers?

level: principalimportance: should knowfreq 34%

basics

~20 s

Decide per field, not per contract: compare the value's required exactness and magnitude against the weakest consumer's numeric type, and let identities and money cross as text while ordinary quantities stay native. Then enforce it with boundary vectors.

open as a page

Which parts of a wire contract can an IDL's generated types enforce, and which must still be checked by hand?

level: middleimportance: nice to knowfreq 26%

basics

~10 s

Generated types enforce shape: declared fields, declared types, and that undeclared fields cannot be set through the generated surface. Ranges, cross-field rules, units, referential validity and state legality are invariants no generator checks.

open as a page

Why can two payee names that render identically fail an equality check after crossing a wire contract as text?

level: middleimportance: nice to knowfreq 28%

basics

~20 s

Unicode allows more than one code-point sequence for the same rendered text: a precomposed accented character, or a base letter followed by a combining mark. Both encode faithfully, so equality by bytes or code points fails while the display matches.

open as a page

A framed stream starts yielding garbage messages hours into a connection — how do you detect and recover from framing desynchronisation?

level: seniorimportance: nice to knowfreq 29%

basics

~20 s

Detect it with per-frame invariants — a fixed marker at a known offset, a checksum, an implausible declared length — and recover by rescanning to the next marker, or by dropping the connection, since framing state is per connection.

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

When is mandating one canonical encoding across a platform the wrong call for artefacts that must be deduplicated and verified?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

Canonicalisation is a second encoder every implementation must reproduce exactly, forever. Where the received bytes can simply be kept and addressed as they are, keeping them is cheaper and fails safer; mandate a profile only when producers must agree on an id without exchanging bytes.

open as a page

showing 1–30 of 31