The defects that pass the build

Jarniel Cataluna ·

Six defects came out of five pull requests on this site. Every one compiled, passed the linter, and built clean. Green is not evidence.

Flat illustration on cream. Five boxes ride a black conveyor belt, each stamped with a green tick, while pink beetles crawl out of a split black folder at the end of the line.

Over five pull requests on this site, review found six defects. Every one of them compiled. Every one passed the linter. Every one built clean, and would have deployed.

None was a typo. None was the kind of mistake that looks like a mistake. They were a corrupted code sample, a comment published into a file it should never have reached, a progress bar covering the skip link, and three documents confidently describing code that did something else.

The gates were green the entire time. Green turned out not to be evidence of much.

They split almost evenly, and the split is the useful part.

Three were false statements about code that was correct. A comment naming a type size the file did not use. A README describing a required field that the parser silently discards. A design document still describing a route that had been renamed, while the identical sentence in the code beside it had been updated. In each case the code was right and the prose next to it was wrong, and prose is not something a linter reads.

The other three were genuine faults in the code, and no gate could have seen those either. A type checker asks whether the code is well-formed. A linter asks whether it matches a pattern. A build asks whether it compiles. Not one of them asks whether a function does the thing its own comment says it does, whether a fixed element covers the first tab stop on the page, or whether a file publishes something that was invisible everywhere it came from.

The comment that described the defect

Take the first of those.

A function here pushes every heading in a post down one level, so a post's own sections nest under its title instead of reading as siblings of the posts around them. Markdown headings and shell comments both begin with a hash, so the function has to know when it is inside a code block. Its comment said exactly that, and gave the reason: demoting a hash inside a fence would rewrite somebody's code sample.

Then it rewrote somebody's code sample.

if (/^\s{0,3}(```|~~~)/.test(line)) {
  inFence = !inFence;
}

One boolean, flipped by either marker. A block opened with backticks and containing a run of tildes closed at the tildes, and the shell comment on the line after it was promoted into a heading — inside a file whose only purpose is to be read by a machine.

The comment was not wrong about what the function should do. It was a statement of intent nobody had checked against the code beneath it, and it had been sitting directly above the defect the entire time, describing it.

The ones outside the field of view

That one was at least legible. Someone reading the function slowly would have caught it.

The next two were not visible anywhere.

This site publishes a plain-text file carrying every post in full, for readers that would rather not strip HTML to find the prose. It ships the raw markdown rather than the rendered output, which means a comment of the kind that vanishes when the page is built survives into it intact. A note written mid-draft and forgotten would arrive in the most quotable artifact on the site. Client names here are withheld under NDA, and the rule covers comments precisely because of routes like that one. It would not show on the page. It would not show in the preview. Nothing would look wrong.

The other only exists while somebody is using a keyboard. A reading-progress bar was given the same stacking order as the skip link — the first tab stop on every page — and sat later in the source, so it won. The bar covered the skip link at the exact moment the skip link was focused, and at no other time. Every screenshot of that page was correct.

Both had the same property. Nothing about looking at the site would have surfaced them. They were not hidden in a corner of the page; they were outside the field of view entirely.

Two passes that cannot see each other

What found them was review, but not review as a single act.

Every chunk was read twice, in parallel, by two passes that never saw each other's output. One read the diff against the repository's own written standards — the design system, the architecture decisions, the glossary, the rule about what may never appear in published text. The other read the same diff against the ticket that asked for the work, answering three questions: what was asked for and is missing, what is here that nobody asked for, and what looks finished but is wrong.

They are kept apart deliberately. Code can follow every convention in a repository and implement the wrong thing. Code can do exactly what the ticket asked while breaking conventions that took a year to settle. Run as one pass, either verdict masks the other — a reviewer who has just confirmed the work matches its spec is not well placed to notice it also violates the type ramp.

Both passes were agents. So was the code they reviewed.

What made the standards pass possible was not the reviewer. It was that the standards existed as text: a design document naming its own rules, decision records giving the reasoning, a glossary listing the words this project does not use. A convention living in somebody's head cannot be reviewed against. It can only be remembered, and only by the person who has it.

Review was also wrong

Three times, in the same five pull requests.

It flagged a summary line for breaking the measure the design system sets for leads. It does break it — and the deviation was decided deliberately before the code existed, for a stated reason, recorded on the ticket. The finding was correct about the rule and wrong about whether it applied.

It asked for a reading time to be restored to the post layout. Nothing in the project derives one, and a reading time invented where the page is rendered is a number the reader has no way to check.

It reported a bug in how a function handled a markdown edge case. The function was right. The test asserting otherwise was mine, and it was the test that was wrong.

None of those is a small error, and two of them would have made the code worse if applied. Which is the part worth keeping: review produces findings, not verdicts. Something still has to hold the decisions that were already made and recognise when a finding contradicts one — and that is not a job you can hand over, because it depends on knowing why the decision was taken, not merely that it was.

This is why the gate here is a person, and why that person reads every finding rather than applying them.

What to change

The reflex, reading a list like this, is to add gates.

It is the wrong lesson. No gate reads a comment and asks whether it is true. None notices that a fixed element sits over the first tab stop, or that a file publishes something invisible everywhere it came from. Two of these six had no observable symptom for anything to observe. A seventh check would have gone green beside the other three.

What did the work was narrower, and duller. The constraints existed as text — a design system naming its own rules, decision records carrying the reasons, a glossary of the words this project does not use. Written down, a constraint can be checked by somebody other than the person who set it. Held in a head, it can only be remembered. And the two passes were kept apart, so a verdict on one axis could not stand in for the other.

The rest is a question rather than a process. The most productive thing asked across five pull requests was not whether the code was correct. It was whether the sentence beside the code was true. Half of what came back was prose asserting something the code did not do — and prose is the part nobody thinks to review, because it is the part that cannot fail.

← All posts