Founder Notes
A True Answer to a Narrower Question
I spent part of a morning certain our CI had stopped working altogether. I wrote
it down as the highest-priority infrastructure problem we had. It was not a
problem at all. The project had ten pipelines and every one of them had passed.
What happened is small, and I think extremely common.
I ran the command that lists a project's pipelines and it came back saying none
were available. I read that as no pipelines existing for the project, and went
looking for the reason a repository would stop creating them entirely. Runner
configuration, project settings, whether pushes were instantiating anything at
all.
The command had not failed. It had not timed out, nothing was misconfigured, and
it had not lied to me. It had answered the question I actually gave it, which
was scoped to the branch I happened to be standing on. That branch had no
pipelines, and it was never supposed to have any: every job in our config runs
only for merge requests and for the default branch, so a plain push to a feature
branch matches nothing and no pipeline gets created. Correct behaviour,
correctly reported.
The mistake lived entirely in the step after the answer. I took a result that
was true of one branch and carried it forward as true of the project. Nothing
objected, because there was nothing wrong with the result.
That is what makes this class of error expensive. A broken tool leaves evidence
behind. A tool that answers a narrower question than the one you meant leaves a
clean, confident, correct-looking record, and you reason from it for an hour.
I also did it twice in the same session. Earlier that day I had described our
default branch as having diverged from what I had locally, on the strength of a
reference I had not refreshed. Same shape: a narrow source, a conclusion a
category wider. Both times the repair was one more command. Both times the cost
was not the command I skipped, but everything I built on top of the answer
before running it.
So the thing I actually got wrong was not a fact about our CI. It was that I had
been treating the scope of a query as a detail of how I obtained an answer,
rather than as part of the answer.
There is a reason empty results are the worst case for this. When a query
returns rows, the rows carry their own context. Three pipelines come with
branches and dates and names attached, and you can notice that all three are
from one place and ask what else there might be. Zero rows carry nothing.
Nothing looks identical at every scope, so an empty result is the one answer
that gives you no way to see how narrowly you asked.
The generalisable form is just this: an answer is only ever as wide as the
question, and when you carry the answer forward you have to carry the question
with it. No pipelines on this branch and no pipelines in this project are
different sentences. One of them was true and I acted on the other.
The useful habit is not more checking, which is cheap to recommend and
expensive to do. It is to state the result with its scope still attached before
you act on it. If the scope sounds like a qualifier you can safely drop, that is
exactly the moment you are widening a claim for free, and nothing downstream
will ever tell you that you did.