The Spec Comes After
“If there’s a spec, the rest is just turning it into code.” I hear this sometimes.
From the outside, programming does look like that. The spec goes in, the code comes out, and programming is whatever happens in between. If that’s the job, then once the conversion is automated, there isn’t much job left.
The reasoning holds up. And it’s true that the work of transcribing settled decisions into settled forms is slowly shrinking.
But that isn’t what programming looks like from where I stand. From the outside, only the conversion is visible. Most of what makes building hard happens before it — in the place where there is no spec yet.
When the spec is fixed, building is actually the easy part. You follow it. It’s the authority, so there’s nothing to agonize over. What to build and how far to take it have already been decided. The constraints are already there.
What makes software hard, most of the time, is that those constraints don’t exist yet. What are we building? How far does it go? What gets left out? Someone has to decide. And it can’t be decided before the building starts.
I do set down provisional constraints — scaffolding to work against. But they’re hypotheses, there to be tested by running something. They’re not the source of truth.
When I build something, I almost never arrive at the goal I first pictured. I move it as I go. Always. So often that it’s hard to recall a single instance.
If I had to pick one, it would be the software I’m building now. It started out looking like a workflow. What I wanted to express was continuity, and laid out as a flow, it looked right.
Then I tried to actually enter data, and I knew it was wrong. A flow is fine to look at. But for writing things in and fixing them, a plain list was far easier to work with. The workflow was a shape for viewing, not for working in.
I still want to express continuity. That hasn’t changed. The shape has. And I couldn’t have known the shape until I built it and put my hands on it.
You can’t know until you build. Which also means a spec written before building is already behind by the time the building is done.
So what’s correct?
What runs, I think — bugs included — is what’s true. Code isn’t the source of truth because it’s correct. It’s the source of truth because it actually runs. Even a bug is a fact: that’s how the system really behaves, and that behavior is what people touch when they use it. A spec, however carefully written, has no way of becoming fact. Nothing runs on it, so it can never be called correct with certainty.
Where, then, does intent live? In the person building, I think. Code and specs are both that intent, written down outside the person. What differs is the order. When I find something while building, it shows up in the code first. It reaches the spec later.
Then the spec can be written from the code. The code holds the newest version of the intent; the spec is the summary that follows. If the summary lags a little, what runs isn’t harmed at all.
Reverse the direction, and things change. If the spec is fixed first and the code produced from it, the intent has been taken out of the person, frozen, and left to conversion. Whatever is found while building then has nowhere to go. And if what’s found can’t be taken in, the thing being made can’t grow. It can’t become more than what was imaginable at the start.
There are worlds where the spec does seem to come first: protocols and standards. For many implementations to talk to one another, the reference has to be the spec, not any single implementation.
But trace them back, and there’s running code at the start there too. HTTP began as Tim Berners-Lee’s implementation; the spec came later, describing behavior already in wide use. Internet standards have long required independent implementations that actually interoperate before a spec can mature. HTML went back to how browsers really behaved and wrote that behavior into the spec. OSI, which tried to settle the design first, gave way to TCP/IP, which was already running.
Following a fixed spec is easy because the work of finding the constraints has already been done. Someone ran it, checked it, agreed on it. What arrives is the crystallized result. Once it sets, so many implementations lean on it that it’s hard to move. But that doesn’t mean the spec was where it started.
Even agreements don’t begin with the spec.
If the constraints are decided up front, a moving goal becomes a failure. Rework. Change requests. Blown estimates.
For me it’s the opposite. A moving goal is a sign that something was found. When I turned the workflow screen into a list, the intent to express continuity stayed. What moved was the shape — and the shape after the move was closer to the one the system was meant to have.
So I treat what runs as the source of truth. The spec comes after. Constraints aren’t something to settle in advance. They’re something found along the way, while building.