# Acceptance Criteria

Testable conditions defining when a requirement is satisfied, written before implementation begins.

**Rests on:** Product Requirements Documents

**Leads to:** Requirements Traceability, Review Gates

*Software Engineering · Spec-Driven Development*


## Why learn this

You are learning this because it is the step that turns a vague request into a testable definition of done. Without them, every feature you build will be judged by opinion, and every disagreement with your reviewer or AI agent will be a fight about taste. With them, you can look at a task and say, 'I am finished because these three conditions hold.' This concept unlocks: Test-Driven Development, where these conditions become automated tests before any code is written; Requirements Traceability, where each condition maps back to a business goal and forward to a test; and Spec-Driven Development as a whole, where written specifications, not vibes, drive the work. You will also need it for Task Cards — a condition is what makes a task small and self-contained enough to hand to an agent or a teammate.


## The intuition

Think of a time someone asked you to 'make something nice' or 'improve the app a bit.' What did they mean? You had no way to know if you were done, because there was no definition of done. Acceptance criteria are the cure for that. They are a list of specific, measurable conditions that must be true for the work to count as finished. They are not a description of the work itself — they are the tests you will run against the work to prove it is done. If a condition says 'the login button is blue,' you can look at the button and say 'yes' or 'no.' If a condition says 'the user can log in,' you can click it and try. If a condition says 'it feels good,' you cannot test it at all — that is an opinion, not a criterion. Writing acceptance criteria before you start building forces you to think about what 'done' means, and it exposes all the hidden assumptions that live in a vague request. It also makes handoff possible: you can hand a task with clear criteria to anyone or any AI agent, and they can know when they are done without asking you.


## An analogy

A recipe. Suppose your friend asks you to 'make something for dinner.' You have no idea what to cook, how much to make, or when it is ready. Now suppose your friend gives you a recipe: 'Make a vegetable curry. The sauce should be thick enough to coat a spoon. It should serve four people. It should be ready in forty minutes.' Now you know exactly what to do and when it is done — the sauce thickens, the portion serves four, the timer rings. Acceptance criteria are the 'thick enough to coat a spoon' part. The recipe itself is the requirement — what to cook. The criteria are the checks you run against the finished dish to know you succeeded. Where does the analogy stop working? A recipe is a series of steps, while acceptance criteria are not steps — they are conditions on the final result. You don't check 'cook for forty minutes' as a criterion; you check 'the curry is done when the timer rings AND the sauce is thick' — the timer is a step, the thickness is a criterion. Also, a recipe can be subjective (what does 'thick enough' mean?) — a good acceptance criterion is so precise that two different people reading it would agree, without needing a taste test. 'Thick enough to coat a spoon' might still be argued over; 'the sauce leaves a thin film on a spoon that does not drip for five seconds' is unambiguous.


## Definition

Acceptance criteria are a set of testable conditions, written before implementation begins, that define when a requirement is satisfied, so that 'done' is a verified fact rather than a personal opinion.


## How it connects

This concept builds directly on Engineering Intent Documents and Product Requirements Documents — you write an intent to capture the direction, then a Product Requirements Document (a document stating what the product must do) to state the goals, and then you write acceptance criteria to make each requirement in that PRD testable. Each criterion should map back to at least one goal, and later, when you study Requirements Traceability (the practice of linking every criterion to a specific business need), you will connect each criterion forward to a test in code. This concept also feeds into Spec Drift (when the implementation no longer matches the original plan) and Review Gates (checkpoints where work is evaluated). Criteria are what a review gate checks against, and they are what makes drift detectable. Of your neighbouring topics, the strongest link is to Test Planning, which turns acceptance criteria into actual automated tests.


## The mathematics

Acceptance criteria do not require any mathematics beyond arithmetic, but they do rest on a logical idea you have used all your life: a condition is either true or false. I will teach you that idea in plain words, because it is the foundation of every acceptance criterion you will ever write. A condition is a statement about the world that can be checked. 'The door is closed' is a condition — I can look and say yes or no. 'The number of users logged in is greater than 10' is also a condition — I can count and compare. The mathematics here is the comparison: you take a number you observed, you compare it to another number with a symbol like 'greater than' or 'equals', and you get a clear yes or no. For example, 'the response time is less than 2 seconds' is a condition. When you test it, you measure the response time — say it is 1.8 seconds — and you check: is 1.8 less than 2? Yes. That is a true condition, and your acceptance criterion is met. The same idea works with a list of conditions: a requirement is 'satisfied' when every one of its criteria is true. That is the logic of 'AND': all the conditions must be true for the whole to be true. This concept also draws on the arithmetic of comparison — you will use greater-than, less-than, equals, and sometimes ranges like 'between 1 and 5.' None of this is beyond what you do when you check a bill or weigh fruit.

A requirement R is satisfied if and only if all of its acceptance criteria C₁, C₂, ..., Cₙ evaluate to true.
    R satisfied  ⇔  (C₁ AND C₂ AND ... AND Cₙ are all true)
Each criterion Cᵢ is a testable condition, typically comparing a measured value m to a threshold t using a comparison like '=', '<', '>', '≤', '≥'.


## A worked example

```
Imagine a requirement: 'The signup form should load quickly.' Without criteria, 'quickly' is an opinion. I will turn it into a testable condition. The intended criterion: 'The signup form loads in less than 2 seconds.' I will test it 3 times and record the times.

Step 1: Test 1: page loads in 1.8 seconds. Is 1.8 less than 2? Yes.   # The criterion is met for this test.
Step 2: Test 2: page loads in 2.1 seconds. Is 2.1 less than 2? No.   # The criterion is not met for this test.
Step 3: Test 3: page loads in 1.5 seconds. Is 1.5 less than 2? Yes.   # The criterion is met for this test.

Result: Two of the three tests passed. A good acceptance criterion would also define how many tests need to pass. If the criterion says 'less than 2 seconds for 99% of requests,' then you would need to measure many requests, not just three. If the criterion says 'the median of 100 requests is less than 2 seconds,' you would sort the times and take the middle one. For this simple example, I will say the criterion is 'every tested request must be under 2 seconds,' so Test 2 means the criterion fails — and that is useful, because it tells you the form is not good enough yet, and you know exactly what to fix.
```


## From scratch, in Python

```python
# Acceptance criteria as a runnable check
# We will write a small Python function that holds acceptance criteria
# and tests a measured value against them.

# A list is an ordered collection of values. Here we store the results
# of three measurements of the signup form load time, in seconds.
measurements_log = [1.8, 2.1, 1.5]

# A dictionary looks things up by name rather than by position.
# Here we define one acceptance criterion: the maximum load time
# must be less than 2.0 seconds.
criteria = {
    "max_load_time_seconds": 2.0
}

# A function is a reusable block of code. It takes an input,
# does something, and returns an output.
def check_criterion(measurements, max_allowed):
    # max() is a built-in Python function that finds the largest value in a list.
    worst = max(measurements)
    # A comparison: is the worst value less than the allowed maximum?
    passed = worst < max_allowed
    # We return both the worst value and the pass/fail result.
    return worst, passed

# Now we call the function with our measurements and the threshold.
worst_time, is_ok = check_criterion(measurements_log, criteria["max_load_time_seconds"])

if is_ok:
    print(f"PASS: worst load time was {worst_time}s (limit {criteria['max_load_time_seconds']}s)")
else:
    print(f"FAIL: worst load time was {worst_time}s (limit {criteria['max_load_time_seconds']}s)")

# More generally, acceptance criteria can be a list of check functions.
# A function that takes a single argument and returns True/False.
def is_fast_under_2(seconds):
    return seconds < 2.0

def has_https(url):
    return url.startswith("https://")

# A list of criteria functions.
criterion_checks = [
    (is_fast_under_2, "load time under 2s"),
    (has_https, "uses HTTPS")
]

# We can test a sample URL.
sample_url = "https://mysite.com/login"

# Loop through each check and report.
for check, name in criterion_checks:
    passes = check(sample_url)
    print(f"{name}: {'PASS' if passes else 'FAIL'}")

```


## What the code does

We start with a list of three measured response times, stored as `measurements_log = [1.8, 2.1, 1.5]`. A list is an ordered collection — it keeps the values in the sequence you put them in. Next, we create a dictionary called `criteria` that stores a name and a threshold, so we can look up the allowed maximum by its name. Then we define a function `check_criterion(measurements, max_allowed)`. A function is a reusable block of code that takes inputs, does something, and returns an output. In that function, `max(measurements)` finds the largest value in the list, then we compare it with `<` to see if it is below the allowed maximum. The function returns both the worst time and the boolean result. Boolean means a value that is either `True` or `False`. We call the function and then an `if` statement prints PASS or FAIL based on that boolean. The second part of the code shows a more extensible approach: we define two small functions, `is_fast_under_2` and `has_https`, each taking one input and returning a boolean. We put them in a list of tuples, where each tuple holds a function and a descriptive label. A tuple is like a list but cannot be changed after creation. Then we loop through that list with `for check, name in criterion_checks:`, calling each function on the same `sample_url` and printing the result. This mirrors how acceptance criteria maps to tests — each check is a boolean, and all must be true for the requirement to pass.


## Common misconceptions

**Acceptance criteria are the same as unit tests** — If I write tests in code, I don't need separate acceptance criteria.

Acceptance criteria are written before any code, often by a human reading a requirement, and they are written in plain language. They capture the intent of the requirement, not just the mechanics of a test. A unit test checks a specific function, but a criterion like 'the user receives a confirmation email' is a behaviour that may need several tests to verify. In fact: Acceptance criteria are the from what, tests are the how. One criterion can map to many tests, and a test can help verify one criterion.

**Acceptance criteria are a nice-to-have if you have time** — I can write the code first and figure out what 'done' means later.

Writing criteria after implementation encourages you to pick criteria that fit what you built, not what the user asked for. It also hides ambiguity while it is cheap to fix. In fact: Acceptance criteria are written before implementation, so they expose misunderstandings when the cost of changing direction is lowest.

**Acceptance criteria are subjective** — A criterion like 'the form should be easy to use' is fine.

'Easy' is not testable. Two reviewers can disagree about whether it is met. A criterion must be measurable by numbers, observed behaviour, or a clear yes/no check. In fact: A good criterion is unambiguous: 'the form has at most 5 fields and the submit button is above the fold' — that can be checked.

**Acceptance criteria are just a checklist for the end** — I write them, I do the work, and then I tick the boxes once.

If you only check at the end, you might not discover a failure until you have invested days. Criteria should be checked as you go, ideally with automated tests, so you catch failures early. In fact: Acceptance criteria are driving checks. In a spec-driven workflow, each feature is developed against its criteria, and the criteria are continuously verified.

**One criterion is enough** — A single criterion like 'the API works' is enough.

'Works' is vague. A working API might return wrong data, be too slow, or crash under load. A single broad statement misses all this. In fact: Break 'works' into several narrow, testable criteria: 'returns correct data', 'responds in under 2s', 'handles 100 concurrent requests without error'.


## In short

Acceptance criteria turn a vague requirement into a list of testable conditions that define when the work is done. Write them before building, make each one measurable, map each one to a test, and check them continuously — not just at the end. Done well, they make 'done' a verified fact, not an opinion.


## Where this is used

Acceptance criteria are used at every level of building AI systems, from the smallest function to the largest language model. In a structured spec-driven workflow, a developer writes acceptance criteria before implementation, then uses them to review the AI agent's output. In **large language models**, acceptance criteria often apply to the data preparation pipeline. For instance, when building a QA dataset for fine-tuning, a criterion might be: 'Each training sample must have a non-empty answer', or 'No more than 0.1% of samples should be duplicates.' The developer writes a script that checks these conditions, not by eye, but with explicit tests. In **small models on devices**, acceptance criteria guard resource use: 'The model must run in under 100 milliseconds on the target phone' or 'The model file must be under 5 MB.' These criteria are checked with a benchmark harness, and the model is rejected or retrained if it fails. In **retrieval-augmented generation**, an acceptance criterion might be: 'The retrieved passages for each query must have at least 0.8 cosine similarity with the query', or 'The RAG system must cite the retrieved document in 95% of answers.' The developer writes the criteria first, then a test harness that measures retrieval quality. In **agentic systems**, acceptance criteria define when a complex task is complete. For example, an agent that is supposed to book a flight must satisfy: 'The confirmation number is present', 'The booking fits in the budget', 'The user's travel dates are correct.' These criteria become automated evaluators, and the agent is considered done only when all are true. In everyday applied ML, criteria govern model performance: 'Accuracy on the test set must be above 90%', 'Precision for the fraud class must be above 80%', 'Recall of the rare disease must be above 95%.' The developer writes these criteria before training, then evaluates the trained model against them. In all these cases, the developer writes plain Python, using assertions or a testing framework like `pytest`, that encodes the criteria as boolean checks. When done badly, acceptance criteria are missing or vague, and the result is models that 'look like they work' but fail in the field — a model with 95% accuracy on clean data but 60% on real-world noise because nobody wrote a criterion about noise robustness. When done well, acceptance criteria are precise, measurable, and automated, catching failures early and making the definition of done unambiguous.


---

Read this on the web: https://basavapurushottam.com/learn-ai/acceptance-criteria
