Behavior-Driven Development
Use BDD to turn user intent into concrete, observable behavior before choosing
implementation details. Treat it as a collaboration and specification practice,
not as a requirement to write .feature files for every change.
Use gherkin when these examples must become formal
.feature artifacts. Use Gherkin alone for syntax-only edits when the behavior
is already clear.
For photo/video DAM workflows, compose with
digital-asset-management to express
original preservation, metadata conflicts, hierarchy moves, smart-collection
membership, rendition authorization, and asset-version restore as observable
behavior rather than implementation details.
When to Use
Use BDD when the work involves:
- User-visible behavior, product rules, workflows, permissions, or outcomes.
- Ambiguous acceptance criteria that need examples before implementation.
- Cross-functional expectations from product, domain experts, QA, support, or
existing behavior contracts.
- Acceptance, integration, end-to-end, or contract tests that should describe
behavior in business-readable terms.
- Bug fixes where the failure should be captured as an externally observable
regression.
Do not force BDD for:
- Trivial documentation edits, formatting-only changes, mechanical renames, or
dependency bumps with no behavior change.
- Purely technical refactors whose behavior is already well covered.
- Low-level implementation details that are better specified with unit tests.
- Formal
.feature grammar, dialect, or step wording when no behavior
clarification is needed; use gherkin.
Workflow
Identify the behavior.
- Restate the user's request as observable outcomes: who does what, under
which conditions, and what changes from the user's or system's perspective.
- Prefer domain language from the request, product docs, tests, and code.
- Separate behavior from mechanism: describe effects, not classes, tables,
routes, selectors, or algorithms unless they are part of the public contract.
Create examples before implementation when useful.
- Write a short Given/When/Then sketch even if no
.feature file is needed.
- Cover the main success path, important alternatives, and meaningful failure
cases.
- Keep scenarios specific and testable; avoid broad statements such as
"works correctly" or "handles errors".
- If the examples need a
.feature file, load
gherkin for local syntax and runner conventions.
Choose the executable layer.
- Use acceptance tests for product-level flows.
- Use integration or contract tests for API, storage, messaging, or boundary
behavior.
- Use end-to-end tests only when browser, device, or full-system behavior is
essential evidence.
- Use unit tests for small domain rules that do not need business-readable
acceptance coverage.
Implement to satisfy the examples.
- Let scenarios guide scope; avoid adding behavior that is not described or
needed.
- Keep test names, assertions, docs, and user-facing text aligned with the
behavior vocabulary.
- Update scenarios when implementation reveals a better business rule, but do
not weaken them to fit an accidental design.
Verify and report.
- Run the tests that execute the described behavior.
- State which scenarios or examples are covered and which are deferred.
- Call out any ambiguity that remains in product or domain expectations.
Architecture Boundaries
- BDD should stay mechanism-neutral, but architecture affects where examples are
executed. In Clean, Hexagonal, or Onion designs, drive scenarios through a
public API, inbound adapter, use case, or application service rather than
private classes, database rows, or framework internals.
- Load
hexagonal-architecture when
acceptance behavior must be mapped to ports, adapters, or external actors. Load
clean-architecture for use-case,
interactor, presenter, or interface-adapter boundaries. Load
onion-architecture for domain/application
rings around a protected domain model.
- Do not put ports, repositories, controller names, ORM details, or layer names
in Given/When/Then steps unless those mechanisms are part of the public
contract.
Given/When/Then Thinking
Use this structure to sharpen behavior even outside Gherkin files:
Given <important context or state>
When <the actor performs the meaningful action>
Then <the observable outcome should occur>
And <additional outcome, only when it belongs to the same behavior>
Good:
Given a member has an expired invitation
When they try to accept it
Then the system rejects the invitation
And explains that a new invitation is required
Poor:
Given the invitation row has expires_at in the past
When the controller calls InvitationService.accept()
Then it returns Error::Expired
The poor version may be useful as a unit test note, but it is not a
business-readable behavior specification.
Scenario Quality Checklist
- The scenario title names the behavior, not the implementation.
- The actor, context, action, and expected outcome are clear.
- Steps are declarative and business-readable.
- Details are specific enough to test but not brittle.
- Every scenario can map to an automated test or a deliberate manual check.
- Scenarios avoid duplicate coverage unless each duplicate protects a distinct
rule, role, or boundary.
Common Pitfalls
- Writing UI scripts instead of behavior: avoid clicks, selectors, HTTP status
codes, and database fields unless those are the contract being specified.
- Hiding assertions in vague wording: every
Then should be observable.
- Adding ceremony after the fact: if examples did not influence scope or tests,
BDD was probably unnecessary.
- Over-covering the obvious: one clear scenario is better than many variants
that do not change the business outcome.
1---2name: behavior-driven-development3description: Apply Behavior-Driven Development to clarify user-visible behavior, acceptance criteria, business workflows, and executable examples before or during implementation. Do not use to author formal .feature syntax; use gherkin.4---56# Behavior-Driven Development78Use BDD to turn user intent into concrete, observable behavior before choosing9implementation details. Treat it as a collaboration and specification practice,10not as a requirement to write `.feature` files for every change.1112Use [`gherkin`](../gherkin/SKILL.md) when these examples must become formal13`.feature` artifacts. Use Gherkin alone for syntax-only edits when the behavior14is already clear.1516For photo/video DAM workflows, compose with17[`digital-asset-management`](../digital-asset-management/SKILL.md) to express18original preservation, metadata conflicts, hierarchy moves, smart-collection19membership, rendition authorization, and asset-version restore as observable20behavior rather than implementation details.2122## When to Use2324Use BDD when the work involves:2526- User-visible behavior, product rules, workflows, permissions, or outcomes.27- Ambiguous acceptance criteria that need examples before implementation.28- Cross-functional expectations from product, domain experts, QA, support, or29 existing behavior contracts.30- Acceptance, integration, end-to-end, or contract tests that should describe31 behavior in business-readable terms.32- Bug fixes where the failure should be captured as an externally observable33 regression.3435Do not force BDD for:3637- Trivial documentation edits, formatting-only changes, mechanical renames, or38 dependency bumps with no behavior change.39- Purely technical refactors whose behavior is already well covered.40- Low-level implementation details that are better specified with unit tests.41- Formal `.feature` grammar, dialect, or step wording when no behavior42 clarification is needed; use [`gherkin`](../gherkin/SKILL.md).4344## Workflow45461. Identify the behavior.47 - Restate the user's request as observable outcomes: who does what, under48 which conditions, and what changes from the user's or system's perspective.49 - Prefer domain language from the request, product docs, tests, and code.50 - Separate behavior from mechanism: describe effects, not classes, tables,51 routes, selectors, or algorithms unless they are part of the public contract.52532. Create examples before implementation when useful.54 - Write a short Given/When/Then sketch even if no `.feature` file is needed.55 - Cover the main success path, important alternatives, and meaningful failure56 cases.57 - Keep scenarios specific and testable; avoid broad statements such as58 "works correctly" or "handles errors".59 - If the examples need a `.feature` file, load60 [`gherkin`](../gherkin/SKILL.md) for local syntax and runner conventions.61623. Choose the executable layer.63 - Use acceptance tests for product-level flows.64 - Use integration or contract tests for API, storage, messaging, or boundary65 behavior.66 - Use end-to-end tests only when browser, device, or full-system behavior is67 essential evidence.68 - Use unit tests for small domain rules that do not need business-readable69 acceptance coverage.70714. Implement to satisfy the examples.72 - Let scenarios guide scope; avoid adding behavior that is not described or73 needed.74 - Keep test names, assertions, docs, and user-facing text aligned with the75 behavior vocabulary.76 - Update scenarios when implementation reveals a better business rule, but do77 not weaken them to fit an accidental design.78795. Verify and report.80 - Run the tests that execute the described behavior.81 - State which scenarios or examples are covered and which are deferred.82 - Call out any ambiguity that remains in product or domain expectations.8384## Architecture Boundaries8586- BDD should stay mechanism-neutral, but architecture affects where examples are87 executed. In Clean, Hexagonal, or Onion designs, drive scenarios through a88 public API, inbound adapter, use case, or application service rather than89 private classes, database rows, or framework internals.90- Load [`hexagonal-architecture`](../hexagonal-architecture/SKILL.md) when91 acceptance behavior must be mapped to ports, adapters, or external actors. Load92 [`clean-architecture`](../clean-architecture/SKILL.md) for use-case,93 interactor, presenter, or interface-adapter boundaries. Load94 [`onion-architecture`](../onion-architecture/SKILL.md) for domain/application95 rings around a protected domain model.96- Do not put ports, repositories, controller names, ORM details, or layer names97 in Given/When/Then steps unless those mechanisms are part of the public98 contract.99100## Given/When/Then Thinking101102Use this structure to sharpen behavior even outside Gherkin files:103104```text105Given <important context or state>106When <the actor performs the meaningful action>107Then <the observable outcome should occur>108And <additional outcome, only when it belongs to the same behavior>109```110111Good:112113```text114Given a member has an expired invitation115When they try to accept it116Then the system rejects the invitation117And explains that a new invitation is required118```119120Poor:121122```text123Given the invitation row has expires_at in the past124When the controller calls InvitationService.accept()125Then it returns Error::Expired126```127128The poor version may be useful as a unit test note, but it is not a129business-readable behavior specification.130131## Scenario Quality Checklist132133- The scenario title names the behavior, not the implementation.134- The actor, context, action, and expected outcome are clear.135- Steps are declarative and business-readable.136- Details are specific enough to test but not brittle.137- Every scenario can map to an automated test or a deliberate manual check.138- Scenarios avoid duplicate coverage unless each duplicate protects a distinct139 rule, role, or boundary.140141## Common Pitfalls142143- Writing UI scripts instead of behavior: avoid clicks, selectors, HTTP status144 codes, and database fields unless those are the contract being specified.145- Hiding assertions in vague wording: every `Then` should be observable.146- Adding ceremony after the fact: if examples did not influence scope or tests,147 BDD was probably unnecessary.148- Over-covering the obvious: one clear scenario is better than many variants149 that do not change the business outcome.