The most dangerous build I've ever run didn't fail. It passed.

The setup

I was rebuilding a static site. Nothing exotic: a content pipeline that reads articles out of a SQLite database and renders them to HTML files. The build had run successfully before. I changed something small, rebuilt, and the output said the magic words:

✓ 4 pages built in 616ms

Four pages. No errors. Build complete. If you'd looked over my shoulder, you'd have said "done" and gone to make coffee.

What was wrong

Every page it built was empty.

Here's the trap, and it's a good one: the build script located the database using a runtime convenience variable — the kind that says "the directory this file lives in." Inside the dev server, that resolves one way. Inside the bundled build process, it resolves another way entirely. Same code, two different meanings, depending on who is asking.

So the build opened a database at a path where no database existed. SQLite, obligingly, created a fresh empty one. The build queried it, got zero articles, rendered zero content into four perfectly-formed HTML shells, and reported unqualified success.

The lie wasn't in any error message. The lie was that everything worked exactly as designed — the design was just pointed at the wrong thing.

How it got caught

Not by the build. Not by the exit code. It got caught because I checked the artifact against reality: with the real database in place, the finished site should render six pages. The build produced four. The count didn't match. That gap — between what the data says should exist and what the output actually contains — was the only signal anything was wrong.

The fix was almost boring: stop deriving the database path from a runtime variable, anchor it to something the build process and the dev server agree on. One line. The debugging took vastly longer than the fix, which is the normal ratio and worth internalizing.

Why this one stuck with me

Because the failure mode generalizes far past static site generators:

  • The backup that completes with "OK" — into a folder that got silently remounted empty.
  • The replication that reports healthy — while pointed at a database nobody writes to anymore.
  • The sync job that finishes in record time — because it had nothing to do.

All of these report success. All of them are lying. The common thread: a process exit code tells you the process ran. It tells you nothing about what the process actually touched. Verification that stops at the exit code is verification of the wrong thing.

The fix that matters isn't technical. It's the habit of asking, after every green build: does the output match what the input says should be there? Count the rows. Check the file sizes. Diff against expectation. It takes thirty seconds, and it's the difference between "the build passed" and "the site works."

The rule I keep

The exit code tests the process. Only the artifact tests the result. Never trust a ✓ you haven't independently checked — especially the ones that arrive suspiciously fast.