What a guardrail must refuse
A written rule is not followed. A coded rule is. Between the two, there is everything that breaks in production — and two days of work reminded us of this brutally enough for us to write it down.
1. The rule we remember protects against nothing
There is a guideline, here, written in black and white at the top of the publication script: we work in the draft, never directly in production. It is clear, it is short, it is in the right place. It was still violated.
Not through negligence: through distraction. Someone — human or agent — had a modification to make on one hundred and nine pages, they did it where the pages were, and they were right to believe it worked. It worked, indeed. Until the next assembly, which regenerates the pages from their components and erases the modification without a word.
The lesson is not "we need to read the instructions better." We had read them. The lesson is that a rule whose respect depends on the memory of a person is not a protection: it is a wish. A protection is something that checks by itself, and refuses.
Our publication script therefore does not trust. It compares the date of each page to that of its components, and if it finds a page more recent than what produced it, it stops. It does not correct, it does not guess: it names the file and hands back control. The work done by hand is not overwritten, and no one learns of the loss three days later.
2. A guard that always shouts is no longer read
Here is the most interesting error of the week, because it is counter-intuitive.
We wrote a check that detects overly exposed services. On the first try, it flagged fifteen problems. Fifteen. The reflex is to rejoice: the check works, it finds things. The reflex is wrong. Looking at each case, only one was truly serious; the other fourteen were already closed elsewhere, and perfectly intentional.
A check that flags fourteen false problems for one real one protects against nothing. It will be read twice, then skimmed, then filtered, then forgotten — and on the day it cries out for real, no one will hear it. Noise doesn't just dilute attention: it turns it against the check itself.
We therefore rewrote it with three levels instead of one. Critical: we must act now, and the script exits with an error. To monitor: the risk exists but nothing is urgent, it's written at the bottom of the report without failing anything. Compliant: we don't talk about it. In automatic mode, it says nothing more as long as everything is fine. Its silence has become information.
This is a quality criterion for any alert: how often does it trigger for nothing? Beyond a handful, it is harmful.
3. Check the result, not the intention
Our tools create pages by cloning an existing page and replacing what needs to change. A first version did this very well, and proudly announced "article written." Except it anchored itself, to find where to start, on the first vector drawing of the page. Now, the first drawing of a page is the menu logo. The tool therefore erased the header, the navigation, and the title — and still announced its success.
"I successfully replaced something" does not prove "the result holds up." This is a distinction that seems theoretical until the day it costs a page online.
Today, the tool verifies that the produced page still has its bones: its header, its article, its title, its footer, its stylesheet. If just one of these pieces is missing, it does not write the file. It refuses. The same principle now protects our generated exercises: a multiple-choice questionnaire whose correct answer points to a choice that doesn't exist would be accepted by any form check — it would mark everyone wrong, silently, forever. We therefore check that the index does point to a real choice, and reject otherwise. We don't patch things up: we reject.
4. Name what we refused
When one of our checks discards something, it says so. The exercise generator returns the list of what it produced and what it threw away, with the reason. The publication script displays the name of the faulty file, not just an error code.
This isn't vanity. A tool that hides its rejects makes it seem like it never makes mistakes, and this belief is exactly what prevents improving it. We prefer a tool that says "I wrote six exercises and threw away two, here's why" to a tool that returns six without saying anything.
5. Restore without asking permission
The last stage of our publication is the simplest to describe and the most difficult to accept. After pushing the pages, the script goes to read the site as it is actually served. If the check fails, it doesn't warn, it doesn't ask: it restores the backup taken thirty seconds earlier and returns production to how it was.
Difficult to accept, because you lose your work without negotiation. But the alternative — leaving something broken online while waiting for a human to arbitrate — always costs more. A failed publication that you redo is a quarter of an hour. A dead page that no one notices is days.
6. What this changes in how we work
These guardrails were not designed in a moment of wisdom. Each one was put in place after a breakage, and each carries, in its own code, the story of what made it necessary. This is deliberate: someone reading the file in six months must understand why this strange check exists, otherwise they will remove it, finding that it complicates everything.
What we take away from this fits in four sentences.
- A rule you have to remember is not a protection. A rule that checks itself is one.
- A check that triggers for nothing ends up ignored, and it takes with it the times it was right.
- "I did something" does not prove "the result holds up." These are two distinct checks, and only the second protects.
- What we refuse must be stated. A tool that hides its errors prevents it from being corrected.
None of these ideas are new. What is new, perhaps, is accepting to pay for them: each guardrail slows down, sometimes refuses wrongly, and forces correction before moving forward. We pay willingly. The price of the other option is always paid later, and with interest.