Write and maintain Behavior-Driven Development tests with Gherkin and Cucumber. Use when defining acceptance scenarios, writing feature files, implementing step definitions, running Three Amigos sessions, or diagnosing BDD test quality issues. Keywords: bdd, gherkin, cucumber, given when then, feature files, step definitions, acceptance criteria, three amigos, example mapping.
Does it follow best practices?
Evaluation — 96%
↑ 1.04xAgent success when using this tile
Validation for skill structure
A shared vocabulary used consistently by both technical and business team members throughout code, conversations, and documentation. The foundation of effective BDD collaboration.
Ubiquitous Language is:
Product Manager: "When a customer's subscription lapses, we need to win them back."
Developer: "So you want to reactivate a churned user account?"
QA: "Are we testing the expired premium member re-enrollment flow?"
Same concept, three different terms. Confusion follows.
Everyone: "When a subscriber churns, we offer a winback promotion."
Terms defined:
Ask questions during Three Amigos sessions:
What do you call this?
PM: "When someone stops paying..."
Dev: "What's the term for that? Cancelled? Inactive?"
PM: "We call them 'churned subscribers'"
✅ Term added to glossary: Churned SubscriberWhat's the difference between X and Y?
QA: "What's the difference between 'guest' and 'user'?"
PM: "Guest hasn't registered. User has an account."
✅ Two distinct terms: Guest, UserWhen does this state change?
Dev: "When exactly does an order become 'completed'?"
PM: "When payment clears and items ship."
✅ Term defined: Completed = Paid + ShippedFeature: Subscription Management
# Define domain terms upfront
Background:
"""
Domain Terms:
- Subscriber: Customer with active subscription
- Churned Subscriber: Cancelled within last 30 days
- Lapsed Subscriber: Cancelled over 30 days ago
- Winback Offer: Discount to re-activate churned subscriber
"""
Scenario: Winback offer for churned subscriber
Given a churned subscriber from 15 days ago
When they visit the pricing page
Then they should see the winback offer
Scenario: No winback for lapsed subscriber
Given a lapsed subscriber from 60 days ago
When they visit the pricing page
Then they should see standard pricing
And they should not see the winback offer❌ Inconsistent Terms:
Feature: User Account Management
Scenario: User registers
When a customer signs up
Then a member account is created
And the client receives confirmation emailFour terms (user, customer, member, client) for one concept!
✅ Consistent Terms:
Feature: Member Account Management
Scenario: Member registers
When a customer creates an account
Then a member account is created
And the member receives confirmation emailDistinction:
# Domain Glossary
## Order Lifecycle
- **Cart** - Temporary collection of items (not yet ordered)
- **Order** - Finalized purchase (payment may be pending)
- **Pending Order** - Order created, awaiting payment
- **Confirmed Order** - Payment received, awaiting fulfillment
- **Shipped Order** - Items dispatched to customer
- **Completed Order** - Items delivered successfully
- **Cancelled Order** - Order terminated before shipping
- **Returned Order** - Items sent back after delivery
## Membership
- **Guest** - Browsing without account
- **Customer** - Anyone (guest or member)
- **Member** - Registered account (free tier)
- **Subscriber** - Paid subscription
- **Premium Member** - Highest paid tier
## Inventory
- **In Stock** - Available for immediate purchase
- **Low Stock** - Less than 10 units remaining
- **Out of Stock** - Zero units, cannot purchase
- **Backordered** - Out of stock, but can pre-order
- **Discontinued** - No longer sold// GOOD - Uses domain language
class Subscriber {
churnDate?: Date;
isChurned(): boolean {
return this.churnDate !== undefined;
}
isEligibleForWinback(): boolean {
if (!this.isChurned()) return false;
const daysSinceChurn = daysBetween(this.churnDate, now());
return daysSinceChurn <= 30;
}
}
// AVOID - Technical jargon
class UserAccount {
deactivationTimestamp?: number;
isDeactivated(): boolean {
return this.deactivationTimestamp !== undefined;
}
}// GOOD - Domain verbs
order.confirm();
order.ship();
order.complete();
subscription.churn();
subscriber.winback();
// AVOID - Generic CRUD
order.update({ status: 'confirmed' });
subscription.delete();
user.create();// GOOD - Domain states
enum OrderStatus {
Pending = 'PENDING',
Confirmed = 'CONFIRMED',
Shipped = 'SHIPPED',
Completed = 'COMPLETED',
Cancelled = 'CANCELLED',
}
// AVOID - Technical states
enum OrderStatus {
State1 = 1,
State2 = 2,
State3 = 3,
}# GOOD - Business domain language
Feature: Order Management
Scenario: Checkout with insufficient inventory
Given product "Laptop" is out of stock
When customer tries to purchase "Laptop"
Then they should see "Out of stock"
And order should not be created
# AVOID - Technical implementation
Feature: Database Constraint Handling
Scenario: Handle zero inventory constraint
Given inventory.stock_count = 0 for product_id = 123
When POST /api/orders with product_id = 123
Then response should be 409 Conflict
And orders table should have no new recordsFeature: Subscription Billing
Scenario: Monthly subscription renewal
Given a subscriber with monthly billing cycle
When their subscription renews on billing date
Then they should be charged subscription fee
And subscription expiry should extend 30 days
# Use "subscriber" consistently, not:
# - "user"
# - "customer"
# - "member"
# - "account"❌ Don't expose implementation:
When I POST to /api/auth with credentials
Then JWT token is stored in localStorage
And user_id foreign key is inserted✅ Use business language:
When I log in with valid credentials
Then I should be authenticated
And I should access my dashboard❌ Vague:
Given the item is inactive
When I process the thing
Then the status should update✅ Specific:
Given the product is discontinued
When I attempt to purchase the product
Then I should see "Product no longer available"❌ Inconsistent:
Scenario: Register new account
When I sign up
Then I create a profile
And I complete onboarding
# "register", "sign up", "create profile", "onboarding" - which is it?✅ Consistent:
Scenario: Register new member
When I complete registration
Then my member account should be created
And I should receive welcome email❌ Overloaded term "User":
Given a user is logged in # Authenticated member?
And a user visits the page # Anonymous visitor?
And a user account exists # Member entity?✅ Distinct terms:
Given a member is logged in
And a guest visits the page
And a member account exists with email "test@example.com"Language evolves as domain understanding deepens:
Initial:
Scenario: Deactivate user
When admin deactivates user
Then user cannot loginAfter discussion:
Team realizes two types of deactivation:
- Temporary suspension (can be reversed)
- Permanent deletion (cannot be reversed)Evolved:
Scenario: Suspend member account
When admin suspends member
Then member cannot login
But member data is retained
Scenario: Permanently delete member account
When admin deletes member
Then member cannot login
And member data is anonymizedUpdate glossary:
- ~~Deactivate~~ (deprecated - ambiguous)
- **Suspend** - Temporarily disable login (reversible)
- **Delete** - Permanently remove member (irreversible)Everyone speaks the same language. No translation layer between business and technical.
Code mirrors business domain, making it easier to understand and maintain.
Product, dev, and QA collaborate using shared terminology.
Scenarios document business rules using terms everyone understands.
New team members learn domain vocabulary from scenarios and code.
Step 1: Brainstorm Terms
What do we call someone who:
- Visits the site? → Visitor? Guest?
- Creates account? → User? Member? Customer?
- Pays subscription? → Subscriber? Premium Member?
- Cancels subscription? → Churned? Inactive? Lapsed?Step 2: Define Distinctions
Guest: Not logged in, browsing
Member: Free account, logged in
Subscriber: Paid monthly subscription
Churned Subscriber: Cancelled within 30 days
Lapsed Member: Cancelled over 30 days agoStep 3: Document in Glossary
# Terms
- **Guest** - Visitor without account
- **Member** - Registered free account
- **Subscriber** - Paid subscription
- **Churned** - Cancelled subscription (last 30 days)
- **Lapsed** - Cancelled subscription (30+ days ago)Step 4: Use in Scenarios
Scenario: Guest browses products
Given I am a guest
When I view products
Then I see public pricing
Scenario: Subscriber sees discounted pricing
Given I am a subscriber
When I view products
Then I see subscriber pricingStep 5: Implement in Code
class Guest extends Visitor {
viewProducts(): Product[] {
return this.catalog.getPublicProducts();
}
}
class Subscriber extends Member {
viewProducts(): Product[] {
return this.catalog.getSubscriberProducts();
}
}