Once a profile field can arrive absent, null, or set, what does that three-valued shape force on every downstream consumer?
answer
- three states, three branches, everywhere
- comparisons gain an unknown outcome
- negation of unknown stays unknown
- consumers disagree rather than crash
- resolve to one action at the edge
basics
~20 sEvery 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.
solid answer
~40 sThree states mean three branches, and the branches multiply. Any code that compares two such fields now has a third outcome — *unknown* — so equality, sorting and boolean combinations stop behaving the way the author assumes, and a condition that is neither true nor false takes whichever path the language's falsy rules happen to give it. Storing the three states and letting each consumer interpret them spreads the problem across the system. The containment is to resolve intent once, at the edge where the message is decoded: turn absent, null and set into a single decided action — *keep*, *clear*, *set to X* — and hand that to everything downstream. The three-state shape then lives in one place, where it can be tested, instead of in every consumer.
code
pseudocode · 8 linesfor each field in contract.fields:
if not request.mentions(field):
continue # absent: touch nothing
value = request.value_of(field)
if value is null:
stored.clear(field) # explicit clear
else:
stored.set(field, value) # ordinary setgo deeper
Recall that a field with three possible states needs three branches, not two, and that an if-else written for two states quietly sends the third somewhere the author never chose.
Explain what three-valued logic does to equality, negation and sorting, and why a condition that is neither true nor false still takes some branch at runtime.
Show the system-level consequence: unresolved states spread to every consumer and surface as two surfaces disagreeing about one record, not as an exception anyone gets paged for.
Set the boundary policy. Decide that intent is resolved once at the edge, that the domain models a genuine unknown explicitly, and that no service downstream receives raw wire states.
## Where the third value comes from Overloading absence gives a field three inhabitants rather than two: not mentioned, mentioned as no-value, and mentioned with a value. That is a reasonable thing for a message to carry, because the sender genuinely has three things to say. It is an unreasonable thing to carry through an entire system, because every piece of code that touches the field inherits a three-way decision. ## What three values do to ordinary logic With two states, a comparison is true or false. With three, a comparison involving the unknown state is neither, and the usual rules fall apart in ways that are easy to miss: - **Equality stops being reflexive in the way people expect.** Comparing two fields that are both in the unknown state does not sensibly yield true — you do not know that they are equal, only that you know nothing about either. - **Negation stops flipping cleanly.** The negation of unknown is still unknown, so a condition and its negation can both fail to fire, and code written as *if A ... else ...* silently takes the else branch for a reason the author never considered. - **Boolean combinations need a truth table, not intuition.** An unknown combined with a definite false may resolve, while the same unknown combined with a definite true may not. - **Sorting and grouping need a stated position.** Where does the unknown state sort, and do two unknowns group together? Every answer is a policy decision, and unstated policies differ per consumer. None of this is exotic; it is the ordinary behaviour of three-valued logic. The trouble is that most application code is written as if there are two values, so the third takes whichever branch the surrounding language happens to give it, silently. ## Why storing all three states makes it worse The tempting move is to keep all three states, on the grounds that information should not be thrown away. What that actually does is hand the same unresolved decision to every consumer: the reporting job, the search indexer, the audit trail, the notification service. Each one now needs its own reading of what absence meant, and each will make a slightly different choice. The bug that follows is not a crash; it is two surfaces disagreeing about the same record, months later, with no single place to fix it. ## Resolving once, at the edge The containment is a translation step at the boundary where the message is decoded. Take the three wire states and the endpoint's contract, and produce one decided action: 1. **Absent** becomes *keep the stored value*. 2. **Present and null** becomes *clear the stored value*. 3. **Present with a value** becomes *set it to that value*. Downstream, nothing sees three states — it sees an action it must apply. Three properties make this pay off: - The translation is one small, fully testable function, and the three cases are easy to enumerate. - Everything after it is two-valued again, so ordinary logic behaves as its author expects. - The decision is recorded rather than re-derived, so the audit trail can say what the request asked for instead of guessing. | Approach | Where intent is decided | Failure mode | |---|---|---| | Pass all three states onward | In every consumer, independently | Consumers quietly disagree about the same record | | Reject one state at the boundary | Nowhere — one operation becomes inexpressible | Clients cannot clear a field at all | | Resolve into one action at the edge | Once, in a testable place | The translation is the only thing that can be wrong | ## The state that still deserves to live One caveat keeps this honest. If *unknown* is genuinely part of the domain — a value the business has not learned yet, as opposed to a field this request did not mention — then it belongs in the stored model as its own value, and should be modelled explicitly rather than smuggled in as an absence. The rule is not that three-valued data is forbidden; it is that the *wire's* three states are about the message, and must not be confused with a domain state that happens to look similar. ## What an interviewer is listening for The weak answer stops at *handle nulls carefully*. The strong one names the multiplication — every consumer, every comparison — and then proposes the boundary translation, with the caveat about a genuine domain unknown. Mentioning that the unresolved version fails as a silent disagreement rather than as an exception is the detail that marks someone who has cleaned one of these up.
- Why does the unresolved version usually surface as a disagreement rather than as an exception?Because each consumer picks a reading that is locally reasonable and never fails. The reporting job treats absence as no change, the indexer treats it as empty, and both run cleanly for months. The defect only becomes visible when someone compares two surfaces and finds different answers for the same record, by which point there is no single place to fix it.
- If the domain genuinely has an unknown value, where should it live?In the stored model, as an explicit state with its own name, distinct from the wire's absence. The wire's three states describe what the message said; a domain unknown describes what the business knows. Conflating them means a request that simply did not mention a field becomes indistinguishable from a deliberate assertion that the value has not been learned yet.
- Does resolving at the edge lose information a reader might later need?Only if you discard the resolution. Record the decided action rather than the raw states and the audit trail keeps what the request asked for, which is usually the question people actually have. If the raw request must be retained for compliance, keep the original bytes alongside the action, not a partially interpreted three-state object in the domain model.
saying these in an interview costs you the question
- Assumes a comparison involving the unknown state yields false
- Thinks negating an unknown condition makes it definite
- Stores all three states and lets each consumer interpret them
- Says handle nulls carefully without naming the third state
- Confuses the wire's absence with a domain-level unknown value