Writing a spec an engineer doesn't have to decode

The first technical spec I wrote that I was genuinely proud of came back with eleven questions attached.

Not objections. Questions. What is the grain of this table. What happens if the source sends the same record twice. Does “last 30 days” mean rolling or calendar. Every one of them was something I knew and had simply not written down, because after two weeks inside a problem the obvious parts stop registering as information.

That was the useful part. The job of a spec is to move what you thought into someone else’s head without a meeting. I had been writing for thoroughness, which is a different goal and much easier to fake. So I started counting. Nothing formal, no spreadsheet, just noticing. Eleven the first time. Then six. Then a good one at two, and both of those were problems I hadn’t considered, which is the kind of question you want to get.

What I put in now, in roughly this order:

The data flow, before anything else. Where this comes from, where it lands, how often it moves, what sets it off. I draw it badly in boxes and arrows and paste the picture in. At a capital firm I spent a long stretch documenting lineage, business definitions and KPI logic for a warehouse, and the thing that surprised me was how often the lineage picture ended an argument by itself. People disagree about numbers mostly because they disagree, silently, about where the numbers came from.

Field-level definitions, especially for the fields that look self-explanatory. Source, type, nullable or not, and one sentence of business meaning that someone outside the team would recognize. A column called revenue tells a reader nothing they weren’t already assuming. “Invoiced revenue, net of refunds issued in the same period, USD at the invoice-date rate” tells them something. It takes under a minute to write and buys back an afternoon later.

I learned that one the tedious way. A spec of mine said the model should refresh daily. It did, faithfully, in UTC. The people using it were in California and spent most of a week convinced the pipeline was losing the last several hours of every day, which from where they were sitting it was. One word carrying an assumption I never noticed I was making.

What the consumer is promised. Grain, rough row volume, refresh cadence, and what the downstream report should do when the data is late or missing. That last one gets skipped constantly. If you don’t say, someone decides for you at 7am on a Monday, and what they usually decide is to display the stale number without mentioning that it’s stale.

Edge cases written as questions with the answers next to them. Duplicate records, late arrivals, backfills, a source that goes quiet, a category value nobody has seen before. Writing this section feels like inventing problems, and it is also the section that decides whether the build takes one week or three.

Acceptance criteria specific enough that a machine could check most of them. Row counts reconcile against the source within tolerance. These keys are unique. This metric matches the finance number for last quarter. “The dashboard looks right” doesn’t qualify. That’s a hope with a checkbox next to it.

Then there are the parts I’ve learned to cut. The background essay explaining why the project matters, which the engineer has already accepted or they wouldn’t be reading. Anything about phase two. The org chart. And above all, implementation instructions. When I catch myself specifying how a thing should be built, it’s almost always because I have a requirement I haven’t articulated and I’m smuggling it in as a technical preference. Better to say the requirement out loud and let whoever maintains the thing pick the mechanism.

At the agency I interned at, most of the job was turning what stakeholders said into something buildable, across a lot of accounts with a lot of different vocabularies. My early drafts were long and reassuring. The good ones got shorter. That felt backwards for a while, until I worked out that the length had mostly been me reassuring myself. Specific things are short. Vagueness is what needs paragraphs, because vagueness requires a lot of words to hide in.

A spec has the same failure mode as documentation, which I’ve written about elsewhere. It gets read at one moment, by one person, with one question in mind, and if the answer isn’t there in the vocabulary they happen to be using, the document may as well not exist. Nobody reads a spec front to back. They ctrl-F it and then they DM you.

I have never gotten a spec to zero questions, and I’m not convinced I should. But “what does this column actually mean” has mostly stopped being one of them, and the questions I get now tend to be the sort I would have been faintly embarrassed to have an answer for in advance.