Founder Notes
The Unlabelled Field Gets Labelled Anyway
For several weeks one of our record types carried a date and said nothing about what the date was for.
Not a missing value. The value was there, correct, checked against its source. What was missing was the kind. There are at least four meaningfully different things a date on a record like that can be: the day the thing was signed, the day it begins to operate, the last day of a window during which something narrower applies, the day somebody starts enforcing it. Those are four different facts about the world, and in the material we work from they get conflated constantly, which is most of the reason the record exists at all.
The field held exactly one of them. It did not say which.
Nothing broke, which is why it lasted
This is the part I keep turning over. The schema was not wrong in any way a type checker could see. Every record validated. Every value was accurate. The field had a name that sounded like it already carried a kind, the kind it sounded like was the most common of the four, and for most records that guess happened to be right.
A field like that is not a bug. It is an open question that reads like a closed one.
The renderer is where it gets decided
Here is what made me actually go and fix it.
Downstream of that field sits a page that prints a short bold caption above the value. The caption was fixed text. It had to be, because the data gave it nothing to switch on.
So for the records where the date meant one of the other three things, the page would have printed a confident caption directly above body prose explaining that the date did not mean that. Both halves generated from the same record. One of them wrong, and the wrong one set in the larger type.
That is the general shape, and it took me too long to see it. When a schema declines to say what a value means, the decision does not disappear. It migrates to the layer that has to display the thing. And that layer has the worst possible combination of properties for the job: no access to the reasoning, no vocabulary for an open question, and a presentation style that makes whatever it picks read as settled. A caption cannot hedge. It can only be printed.
The fix is boring, and the boring part is the point
We gave the kind its own field, with a closed set of values, and made it required.
Required is the whole fix. Optional would have let every existing record keep its silence and constrained only new ones, which is the same as not fixing it. Required means an unlabelled value does not compile, so whoever adds the next record has to decide while they still have the source open in front of them. That is the only moment when the decision is cheap. Every later moment, somebody is inferring it from prose.
What I would carry to the next schema: if a value's meaning is not recoverable from its type, the kind belongs in a separate required field, not in a naming convention and not in a comment above the declaration. A string that could denote four things denotes four things until the data says otherwise. And the place that will eventually be forced to choose is the place least equipped to choose well.