The column that was always red
There is a page in our admin console that lists every scene an agent can route to, and next to each scene it showed where that scene's requests actually go. Native dialects pass through. Everything else is translated on the way to the provider.
The page was useful and it was nearly unreadable, because of a shape nobody had looked at closely. The scene wants native exposure, but the models eligible for that scene disagree about their dialect, so one row is enough to put the whole scene back on translation. That is a defect. It is silent, it degrades a capability the operator believes the scene has, and nobody is told.
The page showed that defect. It also showed it for scenes that had deliberately asked to be translated, which is most of them, and it showed a red fault line for the ordinary case of a scene that is simply doing what it was configured to do. So the page said the same thing, in the same alarming way, for a genuine fault and for the normal case, and the operator's only way to tell them apart was to know which scenes were supposed to be native.
Two fields, four meanings
The reason it could go wrong is that the state was modelled as a pair of fields rather than as a value:
- the dialect, a string, empty if there is no single native dialect to publish
- a sentence explaining why
Read carefully, that pair has four combinations. Native and consistent, which is fine. Native exposure but the eligible set disagrees, which is a fault. Translation because the scene asked for it, which is the normal case and the column default. And translation because the scene wanted native but could not have it, which is a fault and a different fault from the second one. Translation because the catalog could not be read, which is not a verdict about the scene at all.
Four meanings. Two names, because the empty dialect stood for the last three at once, and the sentence was a free-text explanation attached to a value that did not say which of the three it was. The console had to guess, and it guessed wrong in both directions: a catalog that failed to read looked like a scene that had fallen back, and a scene that had deliberately asked for translation looked like a scene that was falling back.
The shape is easy to like. An empty string plus a sentence is flexible, and it will carry anything you put in it. That is the problem: a shape that can carry anything cannot tell a reader which of four things it is carrying, so every consumer is left to infer it, and inference is where the two directions of wrong came from.
Four states, one of them empty
The fix was to model the state as a value rather than as a pair. Four named states, and the renderer switches on them:
native exposure is native and the eligible set agrees
by_design the scene asked to translate; not a fault, not news, the column default
fell_back asked for native, translating anyway; the only one worth alarm
"" the server could not answer; read as "unknown", not as a conclusion
Three properties of that list are worth pulling out.
The empty string is a state on purpose, not an absence of one. A verdict that nobody filled in claims nothing, so a catalog read that failed renders as a page saying it could not determine what this scene routes to. If the unknown case were represented by falling through to the normal case, a failed read would be indistinguishable from a healthy scene, which is the same bug as the one we were fixing, wearing a different hat.
The zero value is the unknown case. That choice does real work: it means a caller that constructs a verdict without filling it in gets the state that claims nothing. The alternative, making a positive state the zero value, means every path that forgets to set the field reports a healthy scene, and forgetting to set a field is the most common way fields are wrong.
And the set is closed. There is no fifth state that can arrive without a decision about what it renders as, which means the renderer's switch is exhaustive and the compiler enforces it.
The renderer is the invariant
The server was already right about the distinctions; the problem was in what it sent and in how the page interpreted it. The interface contract now says the renderer must treat the four values separately and must not infer one from another field, and it carries the reason the old shape was wrong. The page switched on the state, with two quiet branches and one alarming one, and an unrecognised value falling to quiet.
That last part is the piece that turns a contract into a mechanism. A renderer written as "if there is a reason, show it in red" has the wrong shape, because having a reason is exactly what the by-design case looks like. A renderer written as a switch on a closed set has a right shape, because every state has exactly one rendering and a state nobody anticipated renders as nothing instead of as a fault. The comment in the source says the unsafe version would do it again for any state added later, and it is right: the bad shape is not wrong about today's four values, it is wrong about the fifth.
The page also stopped colouring by reason. The badge that says translating is rendered for one state only.
The test that encodes the rule
The interesting tests are not the ones that check the states come back. They are the ones that check what the page does with them.
One asserts that a scene that deliberately asked for translation comes back as the by-design state and is not a fault, because that is the column default and nearly every row, and colouring it red marks almost the entire column. Another asserts that consistency renders as silence, with the reason stated plainly: a warning that is always there is learned to be ignored. Another asserts that a catalog read failure comes back as no state at all, so the page shows its own "could not determine" line rather than a verdict.
There is a pair of them that exist as deliberate counterexamples. One scene has no native dialect published but the catalog still says it can search; another scene publishes none and the catalog says nothing at all. In the old model both of those arrived at the client as the same nil, so the client narrowed both, and on one of the two lines the client turned off a search that was genuinely working. No error anywhere. The two tests are kept as a pair precisely because each is the other's exception, and deleting either one would leave the other passing with the wrong behaviour.
Takeaways
Model a state as a value with a closed set of names, not as an empty field plus a free-text explanation. Four meanings behind two names is not a small ambiguity; it is four things the reader has to guess at, and the guess goes wrong in both directions, which is worse than guessing wrong in one.
Make the unknown case an explicit state, and make it the zero value. A verdict nobody filled in should claim nothing, not claim health. Then a failed read is correct by construction instead of correct by review, and every path that forgets to set a field lands on the answer that does not accuse anyone.
An alarm that is always on is not an alarm. The normal case must render as silence, and the fault must be the only thing that renders as red. If the alerting state is the column default, colour it and you have taught everyone to ignore that column, and the one row that matters is in the column they now ignore.
Write the renderer as a switch on the closed set, with unknown falling to the quiet branch. The shape of the renderer is the invariant; a renderer that colours by "has a reason" is wrong today only by luck, because having a reason is exactly what the normal case has.
And keep counterexample pairs. Two tests that must disagree are worth more than two tests that agree, because they pin the boundary between two states that a single test would blur into one.