| Dave | 6 min read

Acceptance Criteria Developers Actually Thank You For

Learn how to write acceptance criteria that developers love. 5 real examples of bad AC rewritten as good AC using the Given/When/Then pattern.

The spectrum of bad acceptance criteria

I’ve reviewed hundreds of user stories over the years, and acceptance criteria tend to fail in one of two directions.

Too vague: “The feature should work well and be user-friendly.”

Too prescriptive: “Use a blue (#2563EB) button, 14px font, positioned 24px from the right edge, which triggers an API call to /api/v2/submit.”

The first gives developers nothing to work with. The second gives them no room to think. Both lead to the same outcome: multiple rounds of rework because expectations weren’t clear.

Good acceptance criteria sit in the middle. They describe what success looks like without dictating how to get there.

The pattern that works: Given / When / Then

If you take one thing from this post, let it be this format:

  • Given [a specific context or starting state]
  • When [the user takes an action]
  • Then [this observable outcome should happen]

It works because it forces you to think about context, actions, and outcomes separately. You can’t be vague when you have to fill in all three parts.

Let’s look at five real examples.

Example 1: User registration

Bad AC: “Users should be able to register. Validation should be good.”

What does “good” mean? Does it check email format? Password strength? Duplicate accounts? A developer reading this has to make dozens of assumptions.

Rewritten:

  • Given a visitor is on the registration page

  • When they submit a valid email and a password with at least 8 characters (including one number and one uppercase letter)

  • Then an account is created and a verification email is sent within 60 seconds

  • Given a visitor submits an email that already exists in the system

  • When they click Register

  • Then they see an error message: “An account with this email already exists” and no duplicate account is created

Notice how each scenario covers one path. Don’t try to cram every case into one AC statement.

Example 2: Search functionality

Bad AC: “Search should return relevant results quickly.”

“Relevant” and “quickly” are opinion words, not requirements.

Rewritten:

  • Given a user types a query of 3 or more characters into the search bar

  • When they press Enter or wait 300ms after typing stops

  • Then results matching the query in the title or description fields are displayed, sorted by relevance score, within 2 seconds

  • Given a user searches for a term with no matching results

  • When the search completes

  • Then a message is displayed: “No results found for [query]. Try different keywords.” along with 3 suggested alternatives

Example 3: File upload

Bad AC: “Users can upload files. Supported formats should be handled.”

Which formats? What size limits? What happens when an upload fails?

Rewritten:

  • Given a user selects a file in PNG, JPG, or PDF format under 10MB

  • When they click Upload

  • Then the file is uploaded, a progress indicator is shown during upload, and a thumbnail preview appears on completion

  • Given a user selects a file that exceeds 10MB or is in an unsupported format

  • When they click Upload

  • Then the upload is blocked before it starts and a message explains the limitation: “Files must be PNG, JPG, or PDF and under 10MB”

Example 4: Notification preferences

Bad AC: “Users should be able to manage their notification preferences.”

This describes a feature, not criteria for success.

Rewritten:

  • Given a user is on the Notification Settings page

  • When they toggle off email notifications for “Weekly digest”

  • Then no weekly digest emails are sent to that user starting from the next scheduled send

  • Given a user has all email notifications turned off

  • When a critical security event occurs (password change, new device login)

  • Then a security alert email is still sent regardless of preferences

That second scenario is the kind of thing that gets missed in vague AC. Security notifications aren’t optional, and your developers need to know that upfront.

Example 5: Checkout flow

Bad AC: “Checkout should be smooth and handle errors gracefully.”

Every PM has written something like this at least once. It says nothing actionable.

Rewritten:

  • Given a user has items in their cart and a valid payment method on file

  • When they click “Place Order”

  • Then the order is created, payment is charged, a confirmation page is shown with the order number, and a confirmation email is sent within 2 minutes

  • Given a user’s payment is declined during checkout

  • When the payment gateway returns a failure response

  • Then the user sees “Payment declined. Please check your card details or try a different payment method.” and their cart contents are preserved

Tips for writing AC that sticks

One behaviour per statement. If your AC has the word “and” connecting two different outcomes, split it into two statements.

Include the sad paths. What happens when things go wrong is just as important as when they go right. Developers will encounter these paths whether you document them or not. Better to decide the behaviour now than debate it during code review.

Use real numbers. “Fast” isn’t a requirement. “Under 2 seconds” is. “Short” isn’t testable. “Maximum 160 characters” is.

Skip implementation details. Don’t specify the database query, the CSS class, or the API endpoint. That’s the developer’s domain. Your job is to describe what the user experiences.

Read it out loud. If your AC sounds like a legal document, simplify it. If a new team member couldn’t understand it in one read, it’s too complex.

Writing good AC takes practice, not just process

The Given/When/Then format is a great starting point, but the real skill is learning to think like both the user and the developer at the same time. What would a user expect here? What would a developer need to know to build it right the first time?

If you want a shortcut for generating solid acceptance criteria, Projan can help. You describe what you’re building in a conversation, and it generates structured acceptance criteria automatically, complete with Given/When/Then formatting and edge cases you might miss. It’s particularly useful when you’re writing a lot of stories at once and don’t want quality to slip. You can try it free during the beta.

But tool or no tool, the principle stays the same: tell developers what done looks like, and let them figure out how to get there.

Stop writing PRDs from scratch

Try Projan free for 14 days. Beta users get 50% off for life.

Start Free Trial