My competition platform keeps a lessons-learned file. On 31 July I wrote a new entry into it: fix the shape, not the instance - when a bug is a pattern, hunt down every copy instead of patching the one that got reported. On 2 August the profile page went down for every athlete with an approved entry - a handful of people at that point, which is the only reason it wasn’t worse. Same bug class I’d fixed a week earlier on the leaderboard: a margin > 0 comparison with no guard, sitting in a second hand-written copy of the same formatting logic that nobody remembered existed.

The lessons file now contains, in bold, “This lesson was written on 31 July and broken on 2 August.”

That stung enough that I went looking through my notes to see if it had happened before. It has. Repeatedly.

The record

The SQLite trap, documented twice. My project README documented the exact deployment trap - “that one character is the difference between a persistent database and one that evaporates on redeploy”. Then I published a post warning about the same trap. The same afternoon I discovered my own app had been silently starting a fresh database on every deploy for days. The README even named SQLITE_DIR as the defence. That was the exact variable I never set.

The path limit I’d already read about. The Johnny Decimal workbook warns about the Windows 260-character path limit. I read that warning before I built my vault, filed it under things that happen to other people, and built a vault whose worst path was 316 characters. It had been quietly breaking tools for months before the vault’s sync incident dragged it into the light - and made that incident worse.

The backup bug, fixed in one app while it sat in another. My app’s backups only ran at container boot, so a healthy long-lived deploy silently stopped taking snapshots. Found it on 28 July, fixed it, wrote the lesson down: absence has no log line - audit the things that are supposed to happen repeatedly, not just the things that error. Five days later I found the identical bug in another app I maintain. Writing the lesson down didn’t make me go looking for the same shape anywhere else.

The secrets I knew how to handle. My own password generator has used secrets properly since August 2025 - the code proves I knew the discipline. Five months later I pasted my GitHub recovery codes into a vault that syncs through GitHub. Knowing was never the problem.

Why writing it down doesn’t work

I get why I keep doing it. Writing the lesson down feels like the responsible ending to an incident. It’s cheap, and it feels like closing the loop - the mistake got processed and filed somewhere.

But a note is read when someone happens to read it. I don’t re-read the lessons file before editing a template, and I definitely wasn’t checking the README’s warning while I clicked through the deployment console. The note and the work live in different places, and the work doesn’t route through the note.

Structure is different. The compiler reads it on every build whether I remember to or not, and so does the test suite. That’s why every one of these stories only actually ended when something structural went in:

  • The formatter got collapsed into one shared definition, plus a test that fails if any template defines its own copy. Now there’s only one copy to get wrong.
  • The backup stopped depending on a boot that might not happen - it checks on the first request of each new day, so there’s no cron job to quietly stop firing and nothing for a human to remember.
  • The recovery codes got regenerated at the source, which killed every copy in every clone’s history at once, and the note that used to hold them now just explains why they can’t live there.
  • The vault got a 13-character root instead of a 73-character one. The paths can’t get near the limit anymore, so there’s nothing to remember.

None of these fixes ask me to be more careful next time. They take remembering out of the job entirely.

What the lessons file is actually for

I’m not deleting it. To be fair to the notes, they partly earned their keep here - the routine-review habit the backup lesson prescribed is exactly what found the second copy of that bug five days later. And this post exists because keeping honest records made the pattern visible enough to count.

So the file stays, with a demotion. It records what happened and explains why the code looks the way it does. It doesn’t stop anything by itself. If a repeat would be expensive, the prevention has to be something the machine reads - one shared definition, or a test that fails when the rule gets broken.

The question I’m taking from all five: when I write “lesson learned” at the bottom of an incident note, what did I change that gets read every time? If the answer is only the note, then nothing actually changed, and I should expect the repeat.