Insight
A compliance rule that fails the build.
A wellness app is allowed to help you form a habit. It is not allowed to say it treats anything, cures anything, or has been clinically proven to do either.
Open the diagram description
A diagram in two rows and a band beneath them. Both rows run through the same three numbered stages, left to right: stage one, the strings; stage two, the match; stage three, the build. The upper row is labelled the gate working. Stage one is a solid panel headed every display string, reading: in both languages the app ships, and the wording changes every time somebody writes a new screen. A bright green arrow leads to stage two, a forbidden phrase, which lists the treat family, the cure family, the diagnose family and the clinically-proven claim. A second green arrow leads to stage three, drawn as an emphasised panel outlined in bright green and headed the build stops, reading: it prints the file, the line and the phrase, names the requirement, exits non-zero. The lower row is labelled the same gate, stopped working, and it repeats the identical three positions. Its stage one, the same strings, reads: nothing about the strings changed, something about the check did. A solid green arrow still leads into stage two, but stage two is now an empty box drawn in a broken dashed outline with dimmed text, headed no match, no message, and listing three causes: a pattern narrowed too far, a path filter that misses, a matcher reading an empty list. The line leaving that empty box is dashed and ends in a hollow, unfilled arrowhead rather than a solid one, and a caption dropped below it reads, non-zero exit, not returned. That is the signal the upper row had and this row does not. The lower row ends in a panel outlined in warm tan, headed the build goes green, reading: the release goes out, the only signal is the absence of a signal, exactly what fine looks like. Underneath both rows, an arrow points upward into the bottom of the empty dashed box, labelled catches this, and it rises from a full width emphasised panel outlined in bright green. That panel is titled the self-test, and reads: if it stops catching, this turns red. Beside that title it describes the mechanism: it writes a fixture containing a phrase the policy forbids, runs the real linter against it, and asserts two things, that the exit code is non-zero and that the message names the match.
The difference between those two products is not the code. It is the wording, and the wording changes every time somebody writes a new screen.
The usual answer is a review step: somebody reads the copy before release. That works until the week it does not, and nobody finds out which week that was.
What does the rule look like as code?
A linter that fails the build.
It reads every string the app can display, in both languages the app ships, and matches a list of forbidden constructions: the treat family, the cure family, the diagnose family, the clinically-proven claim. A match prints the file, the line and the phrase, names the requirement, and exits non-zero. CI stops.
That is the whole mechanism, and it is unremarkable. The interesting part is the file next to it.
Why does a check need a check?
Because a check that has stopped working looks exactly like a check that is passing.
The linter has a self-test. It writes a fixture containing a phrase the policy forbids, runs the real linter against it, and asserts two things: the exit code is non-zero, and the message names the match. If the linter ever stops catching that fixture, the self-test turns red.
Without it, every plausible failure is silent. Somebody narrows a pattern to stop a false positive and narrows it too far. A path filter stops matching the directory the copy moved into. A refactor leaves the matcher reading an empty list. In every one of those cases the build goes green, the release goes out, and the only signal is the absence of a signal, which is the same thing you see when everything is fine.
Is this not overkill for a wording policy?
It is cheaper than the review it replaces, and it is the only version that is still working in six months.
A review step costs somebody’s attention every release, forever, and its failure mode is a busy week. A linter costs an afternoon once, runs in two seconds, and its failure mode is a red build. The self-test is what keeps that second sentence true.
The general form is worth stating, because it is not really about compliance copy. Any rule that matters enough to write down is a rule somebody will break by accident, and the way to find out is to make the machine refuse rather than to hope the person remembers.
A check nobody has watched go red is not a check. It is a comment with a process id.
I build the same shape into client work: the rule that must not be broken becomes a gate, and the gate gets a fixture that proves it can still fail.
Rules that only a person remembers
Which rule is held up by somebody remembering?
Tell me what must never go out wrong, and who checks it today. I will tell you whether it can be a gate instead.
Ask Dan on WhatsApp (opens in a new tab)or email dan@burdetsky.xyz