Engineering

Types green. Tests green. Artifact wrong.

Across four products we have shipped almost no broken code. What we have shipped, over and over, is a passing check over a wrong result — a canonical URL pointing at our own routing table, a search box absent from the server-rendered HTML, a button in a component nothing imported, a downloadable file containing our error page.

Every one passed CI. Every one was found by a human opening the thing. These are the disciplines we adopted in response, and the specific incident behind each.

Four layers, by cadence

write merge deploy live. Each layer catches what the one above it structurally cannot.

on save

Unit tests

jest over pure functions in lib/ — parsers, pricing, matching

blind to: anything about whether the function is called, or what the page does with it

every PR

CI gates

typecheck · lint · verify:assets · config declarations · a real next build

blind to: anything true only of the deployed render — every failure on this page passed here

on demand

Render gate

headless Chromium on the published URL: visible copy, order, computed contrast

blind to: whatever nobody thought to point it at — it runs on the URLs you name

on a sibling’s cron

AI Personasby HiveJournal

AI personas visit public pages with a first-time-visitor goal and file triage claims

blind to: everything private, and anything a plausible-sounding wrong claim can bury

The amber row is the honest part. The layer that caught the most expensive bugs here is the one still triggered by a person deciding to run it — so its real coverage is whatever we remembered to check. Every other row runs whether anyone is paying attention; that one does not, which makes it the least reliable and the most valuable at the same time.

Rule 1

Verify the received artifact, not the inputs

Assertions about state, props and database rows are assertions about what we intended. The visitor gets a rendered page. Our render gate loads the published URL in headless Chromium and asserts on what a person would actually see: visible copy, reading-order position, and computed contrast.

A shared wrapper hard-coded text-white, and a survey of its eleven call sites found not one passed the prop that would have overridden it. Every block built on it painted white text regardless of the site’s theme. It was caught on a published résumé page showing forty bullet points with no text beside them — white on a white card. The skills were all in the DOM; innerText returned them. Invisible to tsc, obvious in a screenshot.

The same rule caught a fee disclosure rendering below the control that took a visitor’s money on two of three variants — text order and DOM index both said it was above.

Rule 2

A guard that cannot go red is decoration

A check is a claim about the world, and an unfalsifiable claim is worth nothing. Our asset verifier ships a --selftest that spawns it against known-bad fixtures and asserts the exit code — so a guard that is defined but never wired fails the build rather than being blessed by the check it was meant to strengthen.

We wrote a guard for “a settings panel nothing imports”. It asked whether anything imported the file — and a dead file counted as something, so the orphaned panel passed. Being referenced by a corpse is not being reachable.

Rule 3

A scan that matches nothing reports success

Every sweep asserts a non-empty target set before asserting anything about it. A grep that silently matches zero files is indistinguishable from a clean codebase, and it stays green forever after someone moves a directory.

Rule 4

A check that fires on correct code is worse than no check

False positives train people to skip the output, which is the same silence-looks-like-success failure one level up. We treat a crying-wolf guard as a defect in the guard, not an inconvenience.

A rule banning opaque light fills flagged bg-white/90 — an alpha tint, which is the pattern the neighbouring rule recommends. It failed two correct files on its first repo-wide run. Twice more since, a test matched a word inside the comment explaining why the code was right.

Rule 5

Freeze debt as a number that can only shrink

Where a rule arrives after the violations, we record the count and forbid growth, rather than either failing the build or quietly excluding a directory. Our env-var declaration test carries a frozen baseline of 109 known-undeclared keys, with a companion test asserting the baseline never grows — a baselined key that gets declared must be removed from the list, or it rots into a permanent allowlist.

⚠️ The baseline must be measured, not chosen. A ratchet set above the real count sits green while the number climbs to meet it.

Rule 6

Fail loudly at boot when config is incomplete

A feature flag turned on with two of its three variables set is not off and not on — it is silently broken. CONFIG_GATES declares 14 features with their required env, evaluated at startup, and a public /status endpoint answers “is this actually live?” from the running process rather than from a config file.

One integration sat inert for five days on one of three variables. A captcha was silently off for weeks on an env-name typo. Neither produced an error.

Rule 7

Say which half you checked

Our most expensive recurring error is not a wrong observation — those fail loudly, because whoever owns that code corrects you. It is a true observation plus an unverified inference, reported as one thing, arriving with identical confidence because the checked half really was checked.

So findings are written in two labelled halves:

Verified: the parser discards the structure (read the source).
Assumed:  that the shopping list consumes it — I did not check.

Verified: must state scope, because that is where over-claiming hides — “I grepped rather than remembered” can be true and still be one directory reported as a repo-wide conclusion.

Asked whether our pages server-render, we checked the marketing pages, got a clean result, and nearly reported all-clear. It was true about the pages nobody was asking about — the customer sites were serving an empty shell to every crawler. A verified check pointed at the wrong instance reads exactly like an answer.

The scope failure is the same shape whether the population is pages or files, and it is very hard to feel from the inside, because the measurement genuinely ran.

This page shipped with that bug in it. The sentence below said “more than 500 test files”. The command behind it excluded the top-level node_modules and missed a second one nested deeper — so 309 of those 507 files were our dependencies’ tests. We were counting zod’s test suite and calling it ours. The real figure is 198. It was caught by the test on this page’s own numbers, going red in CI about twenty minutes after the page went live.

Rule 8

Exploratory agents produce claims, not findings

Our sibling product runs backstoried, cost-capped browsing personas against public surfaces with a first-time-visitor goal. They catch the class scripted tests cannot: a scripted test asserts what you already thought to check.

Three rules make them safe to act on. Findings land at status:'triage', never open — enforced twice, in the route and in a database constraint — because a persona finding is a claim until a human agrees. Attribution lives in the record rather than a UI badge. And evidence is typed: searched_not_found renders differently from encountered, because “I couldn’t find it” is indistinguishable from “I didn’t look”.

A persona reported a contact form above the restaurant list. The DOM said otherwise — an empty wrapper from a different bug had made the page read that way. We diffed the claim against the real DOM before moving anything. That check is why the pipeline is safe to run at all.

A worked example, unflattering

We built a “download your whole site as one file” feature — the artifact a customer keeps if we disappear. It took seven rounds to work:

  1. 1.The button was in a settings file nothing imported — route worked, tests passed, feature unreachable.
  2. 2.The route fetched the wrong host and got a clean 200 of our own 404 page.
  3. 3.It was gated on “published”, asking an owner to publish a real business’s page to test the feature that proves they can leave.
  4. 4.Owner-only refused everybody, because those drafts have no owner yet.
  5. 5.Every image fetch succeeded and every substitution missed — zero inlined images, empty failure list, no error.
  6. 6.The file opened to a full-screen “Loading…” overlay that nothing could dismiss, because we strip the scripts that would have hidden it.
  7. 7.Working: images embedded, no network references, honest banner.

Types were clean and tests passed at every step. Three of those rounds were the fix for the previous round being unverifiable from the inside — confirming that the mechanism responded rather than that the output was right.

Every round was caught the same way: somebody opened the file.

Where this leaves us

More than 190 test files of our own, and not one of them existed in a form that would have caught the seven failures above. That is not an argument against tests — it is an argument about what they are evidence of. A test asserts that the code does what its author believed. The gap we keep falling into is between that belief and the artifact a person receives, and closing it needs a different instrument: load the real URL, read the real bytes, open the real file.

Written up because we keep paying for the same lesson, and writing it down is cheaper than the eighth round.

Rule 6 is checkable from here: /status reports every feature as ready, off, or incomplete — from the running process, not from a config file.

The other half

QuickSites is one of four products built by the same team. HiveJournal’s write-up covers the same discipline from its own incidents — structured data that only existed after JavaScript ran, a generated file whose extension disagreed with its bytes, and the practice of reading your own system before letting a sibling product describe it.

The two were written independently and arrived at the same first rule, which is either evidence or a shared blind spot. We think it is the former, and the honest form of that sentence is this one.