acceptance-criteria-authoring
verified371fd8e4-09e4-401b-a94d-5df291f86a4d
Write unambiguous, testable acceptance criteria (given/when/then) so "done" is checkable by a machine, not a vibe. Use when defining what "complete" means for any task.
Metadata
Skill file
# Acceptance Criteria Authoring
Use when defining what "done" means for a task, story, or feature. Well-written
acceptance criteria turn "it feels done" into "this command passes, so it is done."
## 1. The Given/When/Then template
Every criterion uses this structure — no exceptions:
```text
Given <precondition>
When <action>
Then <observable result>
```
Worked example:
```text
Given an admin user is authenticated
When they call DELETE /users/{id} with a valid, existing user id
Then the user is removed from the database, their data is cascade-deleted,
HTTP 204 is returned, and an audit log entry is created.
```
Anti-examples (NOT criteria — uncheckable):
| Bad criterion | Why it fails |
|---|---|
| "The system works correctly." | Not observable; not specific. |
| "It should be fast." | Not measurable; no threshold. |
| "Users will like it." | Requires human judgment. |
| "The endpoint is implemented." | Repeats the task, does not define success. |
## 2. Mapping each criterion to an automated check
Every acceptance criterion must have a machine check. Write the check next to each
criterion; if you cannot, the criterion is not concrete enough.
| Criterion (Given/When/Then) | Automated check |
|---|---|
| Given valid input, When processed, Then 200 + JSON matches schema | `pytest -k test_schema` |
| Given invalid input, When processed, Then 400 + error code `INVALID_INPUT` | `curl -X POST ... -d bad && assert response.code == 400` |
| Given duplicate user, When created, Then 409 + message "already exists" | `pytest -k test_duplicate` |
| Given 50 concurrent requests, When processed, Then all return within 2s | `wrk -c50 -d5s ...` or `pytest -k test_concurrency` |
The check can be a `pytest` invocation, a `curl` + shell assertion, a `sql` query,
or a load-test command. What matters: it runs, it returns pass/fail, and it ties
directly to the criterion.
## 3. Boundary criteria: cover more than the happy path
For every feature, write at least these categories:
| Category | Example |
|---|---|
| **Happy path** | Valid input → expected success response |
| **Missing/empty** | Null, empty string, missing field → validation error |
| **Malformed** | Wrong type, over-length, bad encoding → error |
| **Large input** | Max-size payload, boundary values → handled or rejected |
| **Permission** | Wrong role / no auth → 401/403 |
| **Concurrency** | Simultaneous identical requests → one succeeds, other gets conflict |
| **Not found** | Reference to nonexistent resource → 404 |
Not every feature needs every category, but a feature with only the happy path
criterion is **under-specified**.
## 4. The completeness test
Apply this to a set of criteria:
1. Can you run a test/command that definitively says "this criterion passed"?
2. Do the criteria collectively cover at least one happy path and one failure case?
3. Would a new teammate reading only the criteria know when to stop coding?
If "no" to any, add criteria until "yes".
## 5. Acceptance criteria for non-UI work
Given/When/Then works for backend, data, and infra too — just replace "user action"
with "operation":
| Domain | Example criterion |
|---|---|
| Backend API | Given a valid auth header, When POST /orders is called, Then 201 and the order id is in the Location header |
| Data pipeline | Given 1000 input rows, When the job runs, Then 1000 output rows, 0 dropped, and duplicates deduped |
| Migration | Given the old schema, When `alembic upgrade head` runs, Then the new column exists and old data is preserved |
| Infra/config | Given a deploy, When GET /healthz is hit, Then 200 within 5s of startup |
| CLI tool | Given `--verbose`, When the command runs, Then stderr includes per-step timing |
The constant across domains: the "Then" names an observable, machine-checkable
result, never a feeling.
## 6. Reviewing someone else's criteria
When handed criteria to review, run these four checks:
1. **Unambiguous?** Could two people disagree on whether it passed?
2. **Checkable?** Is there a command/test that returns pass/fail?
3. **Complete?** Does it cover failure, empty, and large inputs — not just the happy path?
4. **Minimal?** Are there criteria that repeat, or that are actually non-goals?
Reject criteria that fail #1 or #2 outright; request more on #3; trim on #4.
## Guardrails
- Do **not** write criteria that require human judgment ("looks good", "is
intuitive"). Machine-checkable only.
- Do **not** write criteria that are just the task title rephrased ("the feature
works" is noise).
- Do **not** skip the failure cases — a feature without an error criterion has an
undefined error contract.
- Do write criteria *before* code so they can guide implementation, not after as
a retroactive checkbox exercise.
## Pitfalls
- **"Works correctly"** — the most common acceptance-criteria anti-pattern. It says
nothing and checks nothing.
- **Untestable criteria** — "the user experience is smooth" cannot be automated.
- **Happy-path-only** — ignoring failure cases means the implementation is undefined
when things go wrong.
- **Criteria without a check** — a list of criteria with no verify command per item
is still a vibe.
- **Over-specification** — 30 criteria for a 10-line script is ceremony, not rigor.
## Verify / Checklist
- [ ] Every criterion is written in Given/When/Then format.
- [ ] Every criterion has an explicit automated check (command, test, script).
- [ ] The set includes at least one happy-path and at least one failure/edge criterion.
- [ ] No criterion relies on human judgment.
- [ ] A new engineer reading only the criteria would know when the task is done.
- [ ] Criteria are written and reviewed *before* implementation begins.
Attached files
No attached files.