Summary. A route returning 200 tells you a response was produced. It says nothing about whether the response was the right one. We found four defects in one week that had all passed their checks, and the pattern connecting them is worth more than any of the individual fixes.
#What a green check is actually telling you
The belief hiding inside almost every smoke test is that a successful status code means the page is correct. It means the request was handled. Those are different claims, and the gap between them is where the interesting defects live.
On this site, the navigation entry for the notes section returned 200 for an extended period while serving the homepage. Not a redirect, not an error page. The homepage, at a URL that was supposed to be an index of posts, linked from the primary navigation on every page. Every status check we ran passed, because a response was being produced. It was simply the wrong one.
A status code is a statement about the request. Every check we owned was asserting on transport, and the defect was in content.
#Four defects, one shape
| What broke | What the check saw | The reflex | Actual root cause |
|---|---|---|---|
| A nav destination served the homepage | 200 on every probe | Add more routes to the smoke test | The host answers unmatched extensionless paths, so 404 was never available as a signal |
| A seed importer would have stored published documents as hidden drafts | Rows inserted, no errors | Validate the import file's schema | A boolean-ish string coerced instead of parsed, so the value survived as its own opposite |
| Saving a guest article silently reattributed it | Save succeeded, 200 | Remind people to check the author field | The form omitted the field, and an omitted field is indistinguishable from an intentional blank |
| One colour token failed contrast site-wide at 3.07:1 | Nothing; the site rendered | Fix that one token | A token is a claim about every page, and rendering is not testing |
Read the last column downward and the reflex column stops looking careless. Each fix in the third column is locally correct and would have prevented nothing, because in every case the check was asking a question the defect could answer honestly.
#A route with no file is not a missing route
Clean URLs guarantee a response, which is why they remove your best signal. Extensionless paths are handled by the host rather than by a file, and an unmatched path falls back to something rather than to nothing. That is the feature working as designed. It also means the one response that would have told us the route was broken, a 404, was not on the menu.
The notes route was the only navigation destination with no matching file. Every other link resolved to a real page, so the fallback never fired anywhere else and the configuration looked complete.
The fix was not a smarter rewrite rule. It was to emit a real file at the canonical path during the build, so the route stops depending on host behaviour to exist at all. A rewrite that works is indistinguishable from a rewrite that is missing, right up until the moment it isn't; a file is either there or the deploy fails.
The cost asymmetry is what makes this worth a section. A wrong page at a nav link is invisible to everyone who already knows the site, because we click through to the posts we are looking for by direct URL. It lands almost exclusively on someone arriving for the first time and exploring the navigation. That makes it an acquisition defect wearing a routing defect's clothes, and it fails the way acquisition defects always fail: nobody files a bug for wrong page, they form an impression and leave.
#Absence and intent are the same shape on the wire
A save that sends the whole object cannot tell unchanged from unset. The editor form omitted the author field. Submitting it sent no author, the server substituted its default, and a guest-written article was reattributed to the site's own byline. The save returned success, because from the server's position nothing anomalous happened: a field was absent, and absent fields get defaults.
This one is worse than a broken page and reads as smaller. A wrong page is embarrassing until it is fixed. Misattributed authorship rewrites the record, propagates into feeds and structured data, and the only person likely to notice is the guest whose name came off. Attribution is not a field. It is the thing that makes a library citable, and quietly reassigning it is a trust event rather than a bug.
The general form: any endpoint that accepts a whole object needs to distinguish a field that was not sent from a field that was deliberately cleared. Until it can, every partial form on top of it is a silent-overwrite waiting for a slow week.
#A coercion is not a parse
!!"False" is true, and so is !!"0". The seed importer read boolean values that had arrived as strings and coerced them to booleans by truthiness. Every non-empty string is truthy, so a value meaning not a draft became draft, and documents that should have been public would have imported hidden.
The direction of that failure was luck. Published material becoming invisible is the survivable polarity; the identical bug, with the field or the default inverted, publishes drafts. A defect whose severity depends on which way a boolean happened to be spelled is not a small defect that happened to be low impact. It is a total-blast-radius defect that landed on its harmless side.
Values crossing a boundary, a form post, an environment variable, a JSON import, arrive as strings. They have to be parsed into the type you want, with the accepted spellings written down, and everything else rejected loudly. Coercion answers a question you did not ask, and answers it confidently.
// A parser states its vocabulary and its default. Both are decisions,
// and both are now visible to the next person reading the code.
const toBool = (value, fallback = false) =>
value === undefined || value === null || value === ''
? fallback
: typeof value === 'boolean'
? value
: /^(true|yes|1|on)$/i.test(String(value).trim())
What that shape buys is not correctness on the happy path, which truthiness also delivers. It is that "False", "0" and "no" all land on the false branch by the rule rather than by accident, and an unrecognised spelling becomes visible rather than silently true.
#A token is a claim about every page
Rendering is not testing, and the eye adapts. A single colour token measured 3.07:1 against its background. WCAG AA wants 4.5:1 for body text. The site rendered, looked deliberate, and had looked that way to us for months, which is the entire problem: contrast is a measurement, and we were substituting a look.
Design tokens have the same property as shared constants everywhere. They are not one value in one place; they are a claim asserted on every page that references them. The blast radius of a token defect is the whole site by construction, and accessibility defects land hardest on the readers least likely to report them.
#What a check should have looked like
The check we had, and the check we needed:
// what we were asserting: a response exists
const ok = (await fetch(url)).status === 200
// what we should have asserted: the response is this page
const res = await fetch(url)
const page = await res.text()
assert(res.status === 200)
assert(titleOf(page) === EXPECTED[url]) // every page identifies itself
The second form buys one specific thing: a fallback response now fails the assertion instead of satisfying it. Any host behaviour that answers an unmatched path with some other page produces the wrong title, and the wrong title is a failure. It costs one line per route and a map of expected titles, which every site already has in its markup.
That map is also the cheapest fixture in testing. It needs no framework, no browser, no fixtures directory. It is a dictionary of URL to title, and it converts the entire class of served the wrong thing from invisible to loud.
#What we got wrong
We wrote the check that produced the false green, and we kept it because it was green.
That is the mechanism, and it generalises further than this site. A check that has never failed is indistinguishable from a check that cannot fail, and after enough passing runs it stops being read as evidence and becomes a ritual performed before deploying. Green is a claim about the system and a claim about the check, and we were only ever reading the first one.
The only thing that actually establishes a check works is having watched it fail. We had never seen these fail, and we treated that as reassurance rather than as an unanswered question. The remedy is unglamorous: when you write an assertion, break the thing it guards, watch the assertion go red, then fix it. A check you have never seen fail is a check you have not finished writing.
#Before you trust your own checks
| Question | If yes | If no |
|---|---|---|
| Does your host answer unmatched paths with a fallback page? | 404 is not available to you; assert on content | A status check may genuinely be enough |
| Does every page identify itself in the response, by title or a stable heading? | Assert on it today | Add it first; it is the cheapest fixture you will write |
| Can you name the last time each check failed? | It is a check | It is a ritual, and it is telling you nothing |
| Do your save endpoints distinguish an absent field from a cleared one? | Partial forms are safe | Every form is a silent overwrite waiting for a slow week |
| Is every boolean-ish string parsed with accepted spellings written down? | Bad input fails loudly | You have a coin-flip defect with a total blast radius |
| Has each colour pair been measured against a ratio, not looked at? | You have an accessibility claim | You have an impression |
| Have you ever deliberately broken production to watch a check go red? | You know your checks work | You know your checks run |
The last row is the one everybody skips, and it is the one that makes the rest of the table worth filling in.
#When to skip this
- A genuinely static site with real files and a real 404. If an unmatched path returns a 404 because there is nothing there, status checks are telling you the truth and content assertions add ceremony.
- A prototype whose routes change weekly. The title map becomes maintenance on a structure that has not settled. Wait until the navigation stops moving.
- The cheaper alternative that would have caught most of this. Not a test harness, just an assertion on the title of every link in the primary navigation. Five URLs, one dictionary, ten minutes. It would have caught the wrong-page defect immediately and it would have caught nothing else here, which is the honest trade: it fixes the class that is easiest to detect and leaves the data-integrity and accessibility classes untouched. Those need their own instruments.
- Our evidence is one small site. Four defects in one codebase is a pattern worth naming, not a measured base rate. Treat the mechanism as transferable and the frequency as unknown.
#Key takeaways
- Assert on content, not on status. Give every page a stable title and check the title, because a fallback response will pass any status check you write.
- Establish whether 404 is even reachable on your platform before designing checks around it. Clean-URL hosting quietly removes the signal most smoke tests depend on.
- Parse boolean-ish strings, never coerce them. Write down the accepted spellings and reject everything else loudly.
- Make save endpoints distinguish a field that was not sent from a field that was cleared, before you build a partial form on top of one.
- Measure contrast rather than looking at it. A token is a claim about every page that references it.
- Break the thing each check guards and watch the check go red. Until you have, you know your checks run, not that they work.
Drawn from four defects found in one week on this site, each of which had passed every check it was subject to.