All entries
#product#engineering#process

A Vague Acceptance Criterion Is a Bug You've Agreed to Ship

Writing the requirements as well as the code changed what I build. The clearest signal a criterion is broken is that you can't write a test from it without asking someone a question.

For most of the work I do now I write the context, the user story and the acceptance criteria before writing any code. That wasn't always the arrangement, and taking it on changed my output more than any technical practice I've picked up in the same period.

The reason is narrow. A requirement you can't write a test from is a requirement nobody has finished thinking about.

The tell

Take "the page should load quickly."

Everyone nods. It goes in the ticket. It gets built, reviewed, shipped. Three months later someone reports the page is slow and there's an argument, because "quickly" was never a shared belief. It was three private beliefs that happened not to collide during review.

Now take "the page reaches interactive in under 1.5 seconds at the 95th percentile, measured as a system administrator against a client with at least 40,000 records."

That one is testable, so it's falsifiable, so it can be wrong in a way everyone recognizes. It also forced four decisions the vague version let us skip. Which measurement, which threshold, which percentile, and the one people always forget: which user. Performance for a superuser who sees everything and performance for someone scoped to a single organization are different questions with different answers.

None of that is technical work. It's deciding what we mean while deciding is still cheap.

Write the test first

The habit: before agreeing to a criterion, try writing the test case from it. Not the code, just the steps. Set this up, do this, expect this.

If you can't get through that without asking someone a question, it isn't done. And the question you need to ask is almost always the interesting one. The edge case, the ambiguous state, the thing two people were quietly assuming differently.

Finding that during requirements costs a message. Finding it in code review costs a rewrite. Finding it after release costs a support conversation, a bug ticket, and some of the client's confidence.

The threshold that matters

The version of this that paid off most wasn't personal discipline. It was making the criteria good enough that QA could generate test cases directly from them.

That's a real bar. If someone who didn't attend the discussion can read a criterion and turn it into concrete test steps without interpretation, it's specific. If they have to come and ask what was meant, it wasn't, and now the ambiguity has been found by the person least equipped to resolve it, at the point where resolving it costs the most.

So "could QA write tests from this without me?" became the completion check, and I built tooling to pressure-test criteria against that standard before work starts. Same shape as the conclusion I keep reaching elsewhere: the goal isn't being good at the task, it's arranging the work so it doesn't route through you.

Requirements arrive as solutions

Most requirements show up as a fix rather than a problem. Add a filter for X. Make it real-time. Raise the limit to 5,000.

Each is someone's proposed solution to a problem they haven't stated. Writing the criteria means asking what happens if we don't do it and who's stuck right now, and reasonably often the answer shows the requested change wouldn't have solved anything. Someone asking for a real-time dashboard usually wants to know whether to trust the number on screen. Someone asking to raise a row limit sometimes needs the upload not to overwrite what they already sent.

You only find that by writing down what done means before starting. Which is why I'd rather write the acceptance criteria than be handed them. Not because I want the extra work, but because that document is where the decisions get made. Everything after it is implementation.