bdd-practices.md

BDD practices

Contents

Discovery

Discovery is the most important practice. Without it, formulating scenarios is a waste of time.

The goal is shared understanding of the behaviour to build. Use structured conversations (discovery workshops, example mapping) to explore concrete examples from the user's perspective.

Example mapping

A lightweight discovery technique using four types of information:

  • Story — the capability under discussion
  • Rules — constraints or acceptance criteria that summarise groups of examples
  • Examples — concrete illustrations of how a rule plays out
  • Questions — unknowns that can't be answered yet

Work through rules one at a time, attaching examples that illustrate each. Capture questions to research later rather than guessing. If there are too many rules or too many questions, the story is too large or too vague — break it down.

What makes a good example

  • Concrete, not abstract — use specific names, values, paths
  • Technology-agnostic — describe what happens, not how the system does it
  • Focused — each example illustrates one rule or edge case
  • Minimal — include only details that affect the outcome

When working solo or with an LLM

Discovery still applies. Before writing any scenario:

  1. Read the relevant spec or requirement
  2. List the rules (constraints, acceptance criteria)
  3. For each rule, identify 2–3 concrete examples including at least one edge case
  4. Note any questions or assumptions

Formulation

Formulation turns discovered examples into Gherkin feature files that are both human-readable specifications and executable tests.

Priorities

  1. Readability — a non-developer should confirm "yes, that's what we want"
  2. Declarative style — describe what, not how
  3. Domain language — use the same terms the team uses when talking about the problem, all the way down into step definitions and code
  4. Reusability — steps should be composable across scenarios and features

The declarative test

Ask: "will this wording need to change if the implementation does?" If yes, rewrite it. Scenarios should survive refactors.

# Declarative — survives implementation changes
When Bob logs in with valid credentials
Then he sees his dashboard

# Imperative — breaks when UI changes
When I enter "bob" in the username field
And I enter "secret" in the password field
And I click the login button
Then I see the text "Welcome, Bob"

Using Rule

Rule groups scenarios under a single business rule. It provides structure without adding verbosity:

Feature: Worktree deletion

  Rule: Clean worktrees can be deleted without force

    Scenario: Delete a clean worktree
      ...

    Scenario: Delete rejects dirty worktree without force
      ...

  Rule: Force overrides the dirty check

    Scenario: Force-delete a dirty worktree
      ...

  Rule: The primary worktree is never deletable

    Scenario: Reject deletion of primary worktree
      ...

Using Background

Move shared preconditions to Background when they appear in every scenario under a Feature or Rule. Keep it short (≤ 4 lines). If it scrolls off screen, the reader loses context.

Using Scenario Outline

Collapse scenarios that differ only in values:

Scenario Outline: URL parsing
  Given the remote URL "<url>"
  When the URL is parsed
  Then the host is "<host>" and the project is "<project>"

  Examples:
    | url                                    | host           | project |
    | git@github.com:user/repo.git           | github.com     | repo    |
    | https://git.sr.ht/~user/repo           | git.sr.ht      | repo    |
    | ssh://git@github.com/user/repo.git     | github.com     | repo    |

Automation

Automation connects formulated scenarios to the system as tests. The scenario drives the implementation, not the other way around.

Automating scenarios after implementation is not BDD

It's a perfectly reasonable way to do test automation, but the value of BDD is that failing tests guide the implementation. Writing tests after the fact loses that feedback loop.

Step definitions are glue

Steps connect Gherkin to your system. They should be thin — call into the real code, set up fixtures, make assertions. Don't put business logic in step definitions.

Common myths

  • Using gocuke means you're doing BDD — BDD is the three practices, not the tool. gocuke is automation infrastructure.
  • You can pick practices in any order — Discovery → Formulation → Automation. Having conversations is more important than capturing them, which is more important than automating them.
  • Discovery doesn't need a conversation — even when working solo, the structured thinking of identifying rules, examples, and questions counts. Don't skip it.
  • Scenarios are tests — they are, but they're also living documentation and specifications. Write them for a reader, not just a test runner.