Behaviour-Driven Development
BDD as a conversation practice rather than a tool choice — what the three amigos session produces, when Cucumber earns its place, and how it fails.
3 min read · updated 19 September 2026
BDD is a practice for deciding what to build, in a conversation, before anybody writes code. The tooling is downstream of that and largely optional.
This is worth stating plainly because BDD is very often adopted as "we use Cucumber now", which is roughly like adopting agile by buying Jira.
#The part that creates the value
A three amigos session: someone who knows the business need, someone who will build it, someone who will test it. Fifteen to thirty minutes. The output is a small set of concrete examples of the behaviour, including the edge cases, in language all three can argue about.
PO: Customers with an active subscription get free shipping. Tester: What about a subscription that lapsed yesterday? PO: …that should not get it. Dev: And one that lapses between adding to cart and checking out? PO: Hm. Charge at checkout time. Let me check with finance.
Those two questions are the entire return on the meeting. They were going to be asked eventually — either here, for free, or in a bug report three weeks later. That is BDD's actual mechanism: it moves the discovery of ambiguity to the cheapest possible moment.
#Recording it
The examples become scenarios in given-when-then form:
Feature: Shipping charges
Scenario: An active subscriber gets free shipping
Given Alice has an active subscription
When she checks out a basket worth £30.00
Then shipping is free
Scenario: A lapsed subscriber pays standard shipping
Given Alice's subscription lapsed yesterday
When she checks out a basket worth £30.00
Then shipping is £3.95
Scenario: Subscription status is taken at checkout, not at basket
Given Alice has a subscription that expires in one minute
And she has a basket worth £30.00
When two minutes pass and she checks out
Then shipping is £3.95The third scenario is the valuable one, and it exists only because someone asked in the room.
#When the executable layer is worth it
Automating those sentences costs real effort: step definitions, a runner, a reporting layer, and the discipline to keep steps reusable. It pays back under one condition —
Someone outside engineering reads the scenarios and would notice if one were wrong.
If that is true, the feature files are living documentation: a description of the system that cannot silently go stale because it is executed on every build. That is genuinely valuable and very hard to get any other way.
If it is not true, you are maintaining an English-language indirection layer over tests only developers read, and you should write plain tests with good names instead. This is the most common BDD failure and it is entirely predictable.
#Outside-in: how BDD and TDD compose
Scenario (failing) ← BDD: what the business wants
└─ acceptance test drives the API
└─ unit test (red → green) ← TDD: how the code works
└─ unit test (red → green)
Scenario passes ← doneThe scenario frames the work and stays red for hours. Inside it, TDD cycles run in minutes. When the scenario goes green the feature is done, by a definition three people agreed on in advance.
#Keeping steps sane
Step definitions are code and deserve the same care. A codebase with four hundred one-off steps is unmaintainable; one with sixty reusable, domain-level steps is pleasant.
// Reqnroll (SpecFlow's successor). Note the step is domain-level:
// it says what is true, not how the UI achieves it.
[Given(@"(.*) has an active subscription")]
public async Task GivenAnActiveSubscription(string name) =>
_customer = await _api.CreateCustomerAsync(name, subscription: Subscription.Active);
[When(@"(?:she|he|they) check(?:s)? out a basket worth £(.*)")]
public async Task WhenCheckingOut(decimal value) =>
_checkout = await _ui.CheckoutBasketWorth(value);
[Then(@"shipping is free")]
public void ThenShippingIsFree() =>
_checkout.ShippingCents.Should().Be(0);Put UI mechanics behind objects. The step says "checks out"; a page object knows which button that is. Steps full of selectors are the fastest way to make feature files unreadable and unmaintainable at once.
Do not automate every scenario. The conversation may produce twenty examples; three might be worth an end-to-end test and the other seventeen belong in unit tests. Scenarios are for shared understanding. Automation level is a separate decision — see the testing pyramid.
#The honest failure modes
Gherkin as a UI script. When I click "#submit" — covered in
given-when-then. It is the single most common
degradation.
Scenario count as a metric. Teams that measure scenarios end up with hundreds of near-duplicates, each a slow end-to-end test, and a pipeline nobody can keep green.
The amigos meeting quietly dropped. The tooling survives, the practice does not, and eighteen months later nobody can explain why the tests are written in English.
If you are evaluating BDD, evaluate it as a meeting. The file format is the easy part.
Common questions
- Is BDD just TDD with different words?
- No. TDD is a developer's design discipline operating in minutes. BDD is a collaboration practice operating in the hours before code is written, whose output is a shared understanding of what "done" means. They compose well — the scenario from BDD frames the work, TDD drives the code inside it.
- Do I need Cucumber to do BDD?
- No, and most teams that adopt Cucumber without the conversation get the cost and none of the benefit. The value is in the three-amigos discussion; feature files are a way to record its output in a form that is executable.
- Why do BDD adoptions fail?
- Almost always because feature files become a technical artefact written by developers for developers. The moment nobody outside engineering reads them, the Gherkin layer is pure indirection and the team is better off with ordinary well-named tests.
Runnable samples for this page
last test results ↗- Gherkin
cucumber/features
Working tests, not fragments — they run in CI on every push to 8exgh/endtoendtester-samples.
Was this page useful?
Related topics
- Given-When-ThenThe Gherkin vocabulary for describing behaviour, how it maps onto arrange-act-assert, and when a shared specification language is worth its cost.
- Test-Driven DevelopmentRed-green-refactor, what TDD actually changes about a codebase, where it fits badly, and the honest evidence for and against it.
- SDET vs Automation Tester vs Manual QAThree genuinely different jobs that are routinely advertised as seniority levels of one — what each actually does, and what goes wrong when the distinction is lost.
- Naming and Structuring TestsTest names that say what broke without opening the file, the naming conventions worth adopting, and how to organise a suite so people can find things.
- End-to-End TestingWhat belongs in an end-to-end suite and what does not, how many journeys are enough, and the practices that keep a browser suite from becoming the thing everyone ignores.
- The Screenplay PatternActors, abilities, tasks and questions — a compositional alternative to page objects for suites with many user types and deep flows.
- Shift Left TestingMoving quality work earlier — into design, the pull request and the developer's inner loop — and the version of the idea that is just moving work without moving support.
- RSpecRuby's specification-style framework — describe and context blocks, let and subject, matchers, and the readability trade it makes.