James Carl SitsitGet in touch

Adjectives aren't a spec

I judged an image pipeline for months by whether the output looked right, until the metric that said PASS started quietly disagreeing with the pixels.

I have a pipeline that turns a photo into a structured, printable output. What it makes doesn't matter for this post. What matters is that for months I judged its work the same way every time: I looked at it. If it looked clean, it passed. If something looked off, I tuned until it stopped looking off.

That worked right up until it didn't, and the way it broke taught me something I should already have known.

The word I never defined

My entire quality bar was adjectives. Clean. Recognizable. Watertight. No confetti. Every one of those felt obvious when I was staring at a good result, and just as obvious when I was staring at a bad one. The trouble lived in the middle. Handed a borderline output, I could argue myself into either verdict, and I usually argued for the one that meant I was done for the night.

None of those words was written down as anything a machine could check. "Watertight" meant the region borders form closed loops with no gaps between neighbors. Fine. But I had never actually asserted zero gaps anywhere. I'd looked at the picture and decided the gaps were small enough not to care about. That is not a spec. That's a mood.

The bug that called itself a pass

Then a metric lied to me and I believed it for days.

By that point I'd wired up some automated checks. Enough that a run would print a little report with a pass/fail verdict at the bottom. One output came back green. Passed. I nearly shipped it. When I actually opened the rendered image, there was an obvious defect sitting in the most important part of the frame, the part a person's eye goes to first. The report said fine. The pixels said broken. And because I had started trusting the report, I had stopped looking.

That is the failure that reframed the whole thing for me. A number is not automatically more honest than my eyeball. It is only as honest as the distance between what it measures and what actually matters. My check was measuring something next door to quality and reporting it as quality. It wasn't lying, exactly. It was confidently answering a slightly different question than the one I'd asked, and I hadn't noticed the swap.

Rewriting "done"

So I stopped writing adjectives and started writing gates.

The rule I set: a quality claim is a command, plus a field, plus a threshold, and the result is a boolean. Not "the borders look watertight." Instead, run the topology check, assert zero gaps and zero unshared edges, across every image in the set, on every run. Zero is a number. I can't negotiate with zero at eleven at night.

Some claims turned objective the moment I forced them to. What I'd been calling confetti, the output shattering into regions too small to hold a legible number, became a literal count of those regions, with the target set to zero rather than "not too many." "Deterministic" became: run it twice, diff the bytes, the diff has to be empty. Either it is or it isn't. No mood in it.

The uncomfortable ones were the genuinely visual claims. Does this output actually look like its subject? You cannot fully collapse that into a scalar, and pretending you can is exactly how you rebuild the bug I just described: a proxy metric wearing a quality costume. My compromise was to keep those checks binary, defect present or defect absent, never a score out of ten, but to require two independent sign-offs before "absent" counts. The metric has to say clean, and a separate pass looking straight at the pixels has to agree. When they disagree, the disagreement is the finding. The point of the number was never to end that argument. It was to force it to happen at all.

The part I didn't expect: it learned to say no

Once "done" was a set of gates, a second thing fell out almost for free. The system could finally refuse.

Before, it would take any input, however hopeless. Too dark, too blurry, the subject too small to work with. It would grind the thing through the full pipeline anyway and hand back a confident result built on garbage. Now there's a gate at the front door that turns bad input away before the expensive work runs, with a plain-language reason a person can do something about. "This photo is too blurry to trace cleanly," not a stack trace, and definitely not a junk output wearing a smile. Honest rejection turned out to be the same muscle as honest acceptance. Both are just refusing to claim more than I can check.

What actually bit me

I used to assume the risk in a project like this lived in the hard algorithm. The geometry, the color math, the parts that are genuinely difficult to get right. Those were hard, but they were honest-hard: when I got them wrong, they broke loudly and stayed broken until I fixed them.

The thing that actually bit me was soft. A definition of "good" that lived only in my head and drifted with how tired I was.

A claim you can't check isn't a standard. It's a hope with good posture. The gates didn't make a single output prettier on their own, and a passing gate is not a good result, it's just a result I can no longer quietly lie to myself about. Turns out that was most of the fight.