ka
Khairul Azharsenior software engineer
All writing
InsightsAug 16, 2026 · 4 min

I Plan for My Own Fixes to Fail

Every automation I ship comes with the manual steps for when it breaks. Not if. When. Automation fails on the abnormal day, improvisation under pressure is where real damage happens, and the runbook is a love letter to whoever is on the other end. Usually future me.

KA
Khai
Senior Software Engineer
I Plan for My Own Fixes to Fail

Every piece of automation I ship comes with a document nobody asked for: how to do the same thing by hand when the automation fails.

Not if. When.

People tease me about this. Why write a manual runbook for a deploy script that works? Why document the by-hand steps for a migration that has run cleanly fifty times? I used to answer in theory. Now I answer with three stories, all mine, all real.


The backup that saved nothing

This one still stings. During a repo cleanup, the workflow was textbook: stash the file first as a safety net, then hard-reset the working tree. Backup, then destructive step. The correct order. The professional order.

Except the stash silently created no entry. The output that would have said so was piped away, nobody read it, and the chain moved on because each command exited without complaint. The hard reset ran. Uncommitted work was gone, unrecoverable, no reflog entry, nothing. The safety step had executed and safeguarded nothing.

The lesson was not “be more careful”. Careful was present that day. The lesson was mechanical: never gate a destructive step on the backup command running. Gate it on the backup artifact existing. Check the stash list has a new entry. Check the tar file is non-empty. Touch the thing that is supposed to save you before you need saving. A backup you have not verified is a mood, not a backup.


The installer that verified the wrong thing

One of my macOS apps ships with an installer script. An early version did what most installers do: copied the files, checked the copy succeeded, printed success. Solid green output. Meanwhile, two lines further down its own log, the process it had just installed was dead. Never started. The “success” left behind a broken app that had to be removed and redownloaded.

The script had verified its step instead of the outcome. Files copied is a step. App running is the outcome. Nobody installs software to have files copied.

The rewrite checks what the user actually cares about: is the process alive, is it responding, and it re-checks eight seconds later, because a process that starts and immediately dies passes the first look. When the outcome check fails, the installer rolls back to the previous version on its own. The failure path got designed first, and the success path was built inside it.


The failure that nobody noticed

Just this week, a deploy of this very website failed. Mid-build, a font fetch died inside Docker for no reason that was anyone’s fault. Transient network, bad minute, the usual entropy.

Here is what visitors experienced: nothing. The deploy script is written to verify the new container actually starts and answers before it replaces anything, and when the build failed, it simply kept the old version serving. I retried later, the build passed, done. Total production impact: zero. That boring outcome was purchased years earlier, on the day the script was written to assume its own failure.


The pattern under all three

Automation fails on the abnormal day. Not on the calm Tuesday: it breaks when the disk is full, the token expired over the weekend, the network is flaky because half the region is having a bad day. Which means it breaks exactly when you have the least spare capacity to improvise, and an untested manual fallback invented under pressure at midnight is where the real damage happens. Not from the original failure. From the improvisation after it.

So the habit is: be optimistic about what you build, pessimistic about what will happen to it. Destructive operations state their rollback before they run. Backups prove themselves before anything trusts them. Deploys assume the new version is guilty until it answers on the right port. And every script carries the by-hand steps at the top, because boring instructions are what a stressed brain can follow, and stress will be present at the exact moment those instructions matter.

Planning for my own fix to fail is not self-doubt. It is the opposite: enough confidence in the whole system that no single piece of it, including me, ever needs to be perfect on the first try at 2am.

The runbook is a love letter to whoever is on the other end of the failure. Three stories in, I can tell you exactly who that is. It is future me. He has always been grateful.

Next essay

What Freelance Clients Taught Me That Employers Couldn’t

Nobody buys code, scope is the actual product, feedback arrives with no buffer, and saying no happens with your own mouth. Freelance clients paid me something more expensive than money: unfiltered reality. Lessons employment could never teach.

ka

I design and ship resilient mobile platforms and the backends that keep them honest.

© 2026 · Privacy · v4.2.0 · commit 8a3f12c