Missing is not zero
There's a station in Slovenia's national weather feed — Vojsko — that reports no temperature. Not zero degrees; no reading at all. Every clean-API instinct I've absorbed says to smooth that over: drop the key, or default the number. Both moves manufacture a bug and hand it to someone else.
Zero is a measurement
In a Slovenian winter, 0 °C is an ordinary reading. A consumer that receives temperature: 0 has to be able to act on it — and it can't, if 0 is also the encoding for "the station didn't say." Coercing absence to a default creates the bug because it maps two different facts onto one value, and only the producer ever knew which fact it was. Dropping the key is the same trick in different clothes: now an absent field means either "not measured" or "you typo'd the name," and the consumer is back to guessing about facts the boundary threw away.
I didn't get this right the first time. The commit log carries the correction — a fix to the mapper for the temperature field specifically — from before the contract question had been thought through. The station was already out there not reporting; the code just hadn't decided what that meant yet.
Required, nullable, converted at the edge
The contract the API settled on keeps temperature and humidity in every station object — the key is always present — and allows the value to be null. Null means exactly one thing: the upstream had no reading. The key stays required because presence is the contract's promise that the consumer never has to distinguish a missing key from a missing value. The boundary enforces the meaning: a toNullableNumber() helper converts the feed's empty and whitespace values to null, never to 0. That's the whole pattern — typed absence: "not measured" carried as a first-class value instead of a hole the consumer falls into.
The decision record even keeps its own embarrassment on file. Expressing "number or null" cleanly needed newer OpenAPI syntax than the spec file declares, and reconciling that is explicitly deferred. Contract correctness beat spec purity, and the mismatch is written down rather than hidden.
The Monday version: grep your serializers for ?? 0 and || 0 on measured quantities. Every hit is a place where a consumer will someday act on a measurement that never happened.
What null doesn't carry is why the reading is missing — a sensor that's down looks identical to a sensor that was never installed. One kind of absence has been enough for this API so far. I suspect that the day it isn't, the fix won't be a second kind of null; I just don't know yet what it will be.