# Writing effective acceptance criteria

Good acceptance criteria are specific, verifiable, and complete. This guide shows how to write criteria that lead to accurate verification.

### Be specific

Vague criteria can’t be reliably verified.

**Too vague:**

```markdown
-[ ] Endpoint is secure
```

**Specific:**

```markdown
-[ ] Requires authentication via Bearer token
-[ ] Returns 401 for missing or invalid token
-[ ] Returns 403 if user lacks permission
```

**Too vague:**

```markdown
-[ ] Handles errors properly
```

**Specific:**

```markdown
-[ ] Returns 404 if resource not found
-[ ] Returns 503 if downstream service unavailable
-[ ] Error responses include correlation ID
```

### Include both positive and negative requirements

Say what should happen *and* what shouldn’t.

```markdown
-[ ] Response includes: status, renewal_date, plan_name
-[ ] Response excludes: internal_id, billing_provider_id
```

```markdown
-[ ] Creates new user record
-[ ] Does not modify existing user records
```

Negative requirements often catch bugs that positive requirements miss.

### Cover error cases

Don’t just test the happy path.

```markdown
-[ ] Returns 400 for malformed request body
-[ ] Returns 404 if subscription not found
-[ ] Returns 409 if resource already exists
-[ ] Returns 503 if payment service unavailable
```

Think about what could go wrong and how your code should respond.

### Add performance requirements only when meaningful

```markdown
-[ ] P99 latency under 200ms
-[ ] No N+1 queries
```

Only include these if they’re actual requirements. Don’t add “P99 under 100ms” to everything just because it sounds good.

### Express constraints as criteria

Things that must *not* happen:

```markdown
-[ ] No new external dependencies
-[ ] Does not modify database schemas
-[ ] Does not call billing-service directly
-[ ] No breaking changes to existing API
```

### Use concrete values

**Vague:**

```markdown
-[ ] Returns appropriate status code
```

**Concrete:**

```markdown
-[ ] Returns HTTP 201 on successful creation
```

**Vague:**

```markdown
-[ ] Response includes required fields
```

**Concrete:**

```markdown
-[ ] Response includes: id, name, created_at
```

### Group related criteria

For readability, group criteria by concern:

```markdown
## Acceptance Criteria

### Endpoint behavior
-[ ] Endpoint: `POST /api/v1/users`
-[ ] Accepts JSON request body
-[ ] Returns HTTP 201 on success

### Validation
-[ ] Returns 400 if email is missing
-[ ] Returns 400 if email format is invalid
-[ ] Returns 409 if email already exists

### Response
-[ ] Response includes: id, email, created_at
-[ ] Response excludes: password_hash

### Security
-[ ] Requires admin authentication
-[ ] Password is hashed before storage
```

### Common patterns

#### REST endpoint

```markdown
-[ ] Endpoint: `GET /api/v1/resource/{id}`
-[ ] Requires authentication
-[ ] Returns 200 with resource data
-[ ] Returns 404 if not found
-[ ] Returns 403 if user lacks access
-[ ] Response format matches ResourceSchema
```

#### Data mutation

```markdown
-[ ] Creates/updates record in database
-[ ] Validates input before persisting
-[ ] Returns created/updated resource
-[ ] Emits event to message queue
-[ ] Operation is idempotent
```

#### Integration

```markdown
-[ ] Calls external-service API
-[ ] Handles timeout with retry
-[ ] Handles 5xx with graceful degradation
-[ ] Logs external call with correlation ID
```

### Criteria to avoid

**Implementation details:**

```markdown
# Bad - specifies how, not what
-[ ] Uses Redis for caching
-[ ] Implements retry with exponential backoff
```

Better to focus on behavior:

```markdown
# Good - specifies observable behavior
-[ ] Caches response for 60 seconds
-[ ] Retries failed requests up to 3 times
```

**Subjective judgments:**

```markdown
# Bad - can't be objectively verified
-[ ] Code is clean and readable
-[ ] Performance is acceptable
```

**Compound criteria:**

```markdown
# Bad - multiple things in one criterion
-[ ] Validates input, creates record, and sends email
```

Split into separate criteria:

```markdown
# Good - one thing per criterion
-[ ] Validates input against schema
-[ ] Creates record in database
-[ ] Sends confirmation email
```

### See also

* Tutorial: Your first spec
* Reference: Spec format
* Explanation: How verification works


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.aviator.co/verify/how-to-guides/writing-effective-acceptance-criteria.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
