3.1free~6 min

Specification — saying clearly what you want

1. The most under-taught skill

Schools teach you to write code. They don't teach you to write specs. That made sense in the old world — the code was the spec, more or less. Now the spec is the upstream artifact and the code is the downstream artifact, and the quality of the upstream determines the quality of the downstream.

You can spend the next year getting better at one thing. Pick this one. Specification is the lever that moves everything else.

2. Bad spec, bad output

The pipeline is: spec goes in, code comes out. If the spec is fuzzy, the code is fuzzy. If the spec is wrong, the code is wrong. If the spec is incomplete, the code fills in the gaps with whatever the model considers reasonable — which is often not what you considered reasonable.

When code from the model surprises you, it's almost never the model misbehaving. It's the spec leaving room for the model to interpret. The model picked a reasonable interpretation. You wanted a different one. Whose fault is that? Yours, every time.

3. Vague spec vs precise spec

Here's a real example. Both versions describe the same function. They produce wildly different code.

tsVague spec — the model will guess and probably guess wrong
// Write a function that paginates a list of items.

function paginate(items, page) {
// model fills this in
}

The model has to decide: page size? Default? Zero-indexed or one-indexed? What if page is past the end? What if items is empty? What's the return shape? Does it include metadata? It'll pick something reasonable for each — but probably not the same thing your existing pagination uses, which means the function is subtly inconsistent with the rest of the system.

tsPrecise spec — there's only one reasonable implementation
/**
* Slice an array into a page for display.
*
* - Pages are 1-indexed (page 1 is the first page).
* - Page size is fixed at 20.
* - Pages past the end return an empty array, not an error.
* - The result is { items, page, totalPages } — totalPages is ceil(length / 20).
* - Must not mutate the input.
*
* Example:
*   paginate([1..50], 2)
*   -> { items: [21..40], page: 2, totalPages: 3 }
*/
export function paginate<T>(
items: T[],
page: number
): { items: T[]; page: number; totalPages: number } {
// model fills this in
}

The precise version pins down every decision the vague version left open. The model now has one reasonable implementation and produces it on the first try. The function is consistent with the rest of your system because the spec said which conventions to follow.

The vague spec took eight seconds to write. The precise spec took two minutes. The precise spec saves twenty minutes of back-and-forth after, plus an unknown amount of time later when the inconsistency causes a bug.

4. The components of a good spec

A spec good enough to generate from has six parts. You don't need every part for every function — but you need to consciously decide which to skip, not skip them by accident.

Purpose. What this is for, in one sentence. Why it exists.

Inputs and outputs. Exact types. Including the shape of objects, including nullability.

Constraints. What it must do beyond returning the right answer. Complexity bounds, side-effect rules, dependencies it's allowed to use.

Edge cases. The boundary values. Empty. Null. Maximum. Minimum. Duplicates. Unicode. Concurrent calls.

Examples. At least one. Concrete input, concrete output. Examples beat prose.

Conventions. Which patterns from your codebase to follow. Which not to invent new ones for.

Run through that list before you hand a spec to the model. If any item is "the model will figure it out," you're going to get back a function you don't recognize.

5. Specs you write for yourself, too

A useful side effect: writing the spec forces you to know what you want. Engineers sometimes start typing code with only a vague intent, refining as they go. With AI, that doesn't work — fuzzy intent in, fuzzy code out. So you write the spec first, and in writing it, you discover the questions you hadn't answered yet. What happens when…, should this handle…, who calls this…

This is the same upgrade as test-driven development, except the test is now plain English and the model writes the code. Same discipline, different artifact.

6. Where to go deeper

There's a whole course on Codritium about this skill — written for engineers who want to specify well. If specification clicks for you as the bottleneck, that's where you go next: [[vibe-to-spec]].

For the next ten minutes, though: think of one piece of code in your work that surprised you recently. Re-read the prompt or comment that produced it. Find the part of the spec that was fuzzy. That's where the surprise came from. It always is.