MB

Two pieces of safety code that were correct when written

A cleanup line that became a source-tree wipe on every successful run, and a guard that switched itself off on the exact day it started mattering. Neither changed. Neither failed at the moment it became wrong.

Two things happened in the same repository on the same day, and it took me a while to notice they were the same thing.

Both were safety code. Both were correct when written. Neither changed. And both became dangerous because a fact about the world quietly stopped being true.

The cleanup line

A test harness needed a src/ directory to exist so it could exercise a branch that only runs when there is code. So it created a placeholder, ran the check, and tidied up after itself in an exit trap:

trap 'rm -rf src' EXIT

This is fine. It is fine for exactly as long as src/ is something the harness invented and owns.

Then real code landed in src/. From that hour, the line was a source-tree wipe on every clean exit. Not on failure — on success. A green run would have deleted the project and reported that everything passed.

Nothing about the line changed. What changed was who owned the path.

The rule I took from it: never name a path in cleanup code that you did not create in the same script. If you didn’t make it, you don’t get to delete it — and if you did make it, delete it by the variable you made it with, not by its name.

The guard that expired on schedule

The second one is subtler and I like it more.

I had a documentation check with a sensible rule: while there is no code, every acceptance criterion in the design docs must be marked NOT BUILT. It stops a specification from quietly accumulating checkmarks for things nobody has written.

It was also written to switch itself off once src/ appeared. Which sounds reasonable — once there’s code, some of those criteria might genuinely be met, so the check has done its job and should get out of the way.

Read that again, because it took me two passes. The check turns off on the exact day a green acceptance list starts being tempting. It guards the period when nobody was going to cheat anyway, and retires the moment the incentive appears.

I found it because I re-ran its mutation test. The test went from PASS to NOT CAUGHT without a single line of the check changing. The mutation still broke the thing. The guard just wasn’t there to catch it any more.

The rule: an expiring guard needs a named successor. If you can’t say what takes over when it switches off, it isn’t expiring — it’s just ending.

What they have in common

Neither failed at the moment it became wrong. That’s the property worth naming. There was no error, no alert, no red test — because from the code’s point of view nothing happened. The change was outside it.

Most of what I check for is code that is wrong now. These were both correct when written and correct at every review, and became wrong later, silently, because they depended on a fact nobody had written down: this directory is mine, there is no code yet.

I don’t have a tidy method for this. The closest I’ve got is a question I now ask about any guard or cleanup step: what has to stay true for this to keep being right, and what happens on the day it isn’t? Both of these had an answer, and in both cases the answer was bad, and in both cases I could have written it down at the time in one sentence.

Re-running the mutation test is what actually caught the second one. Not review — I had read that file more than once. A test that proves a guard is load-bearing is also the only thing that will tell you the day it stops being.