Gherkin, the Given/When/Then language behind Cucumber, Reqnroll, and Behave, is either the thing that saved your test suite or the thing that doubled its line count for no reason. Both experiences are real. The difference is almost never the syntax. It is who reads the scenarios and what they do next.
Here is where Given/When/Then genuinely pays off, where it turns into ceremony, and what to reach for when it does.
What Gherkin actually is
Strip away the tooling and Gherkin is a small vocabulary for describing behavior in plain language:
Feature: Checkout
Scenario: Complete checkout with a saved card
Given the shopper has an item in the cart
When the shopper completes checkout with a saved card
Then the order confirmation should be displayed
Given is the starting context, When is the action under test, and Then is the observable outcome that proves it worked. That structure is the whole idea. Everything else — step definitions, tags, scenario outlines — is machinery layered on top.
Where it earns its keep
A shared contract across roles. The original pitch for BDD was collaboration: a product owner, a tester, and a developer looking at the same scenario and agreeing on what "done" means before code exists. When that conversation actually happens, Gherkin is excellent. The plain-language layer is a genuine artifact that a non-programmer can read, edit, and sign off on. If your acceptance criteria currently live in a ticket that nobody rereads after the sprint, moving them into executable scenarios is a real upgrade.
Review before implementation. A scenario is reviewable in a way a block of automation code is not. You can catch a wrong expectation in Then the order confirmation should be displayed in seconds, whereas the same mistake buried in a locator chain and an assertion is easy to skim past. Behavior review and code review become two separate, cheaper steps.
Grounding for AI generation. This is the newest and most underrated reason. When an AI generates a test, the quality of its output is bounded by the clarity of its intent. A vague instruction like "login works" gives the model nothing to verify, so it writes actions with no assertion. A concrete Then the dashboard should open and the user's name should be visible in the header tells the generator exactly what evidence proves success. The Then step is the part that turns "click some buttons" into "prove the behavior happened." Gherkin's structure forces you to name that outcome.
Where it is just ceremony
Solo or engineer-only teams. If the only people who will ever read the scenarios are the same people who write the automation, the plain-language layer is a translation nobody needs. You write English, then you write the code that fulfills the English, and you maintain both. That is two artifacts to keep in sync for an audience of one.
Step-definition maintenance in classic BDD. Traditional Cucumber-style frameworks require every step to map to a hand-written function:
When('the shopper completes checkout with a saved card', async function () {
await this.page.getByRole('button', { name: 'Checkout' }).click();
await this.page.getByText('Saved card ending 4242').click();
await this.page.getByRole('button', { name: 'Place order' }).click();
});
Now every scenario line is two artifacts to keep in sync. A one-word change in the English can break a step definition three features away, and a large suite accumulates a step library that is its own codebase. The indirection buys you reuse, but it also buys you a layer of debugging that has nothing to do with the product under test.
Scenarios written to look like BDD. The worst outcome is Gherkin as decoration: When I click the button with id "submit-btn" is implementation detail wearing a When costume. It is less readable than the code it wraps and it fools no product owner. If your steps mention CSS selectors, HTTP status codes, or database rows, the ceremony has fully detached from the value.
The honest alternatives
You do not have to choose between "full Cucumber" and "no structure." A few points on the spectrum:
- Plain Playwright with descriptive names.
test.describeandtestblocks give you readable grouping without a translation layer. The test name carries the intent; the body carries the action.
test.describe('Checkout', () => {
test('completes checkout with a saved card', async ({ page }) => {
await addItemToCart(page);
await checkoutWithSavedCard(page);
await expect(page.getByText('Order confirmed')).toBeVisible();
});
});
- Given/When/Then as comments or helper names. Keep the discipline of naming context, action, and outcome without the parser. You get the mental model for free and skip the step-definition tax.
- Plain-language test instructions. If the goal is a readable description that a machine turns into automation, you can write the intent directly and let generation produce the code, keeping one source of truth instead of two.
The rule of thumb
Keep Gherkin when a non-programmer will read or approve the scenario, or when its structure is feeding something — a reviewer or a generator — that benefits from an explicit outcome. Drop it when the plain-language layer is a costume over code that only engineers touch.
This is the model TestVibe leans on. You describe behavior in Gherkin-style feature files, and the readable intent, especially a specific Then that names the visible result, is what the AI turns into Playwright code and then verifies by actually running it. The point was never the keywords. It was writing down the outcome clearly enough that both a human and a machine can check it.
Curious how readable intent becomes a running test? See the Gherkin and feature files guide, or get early access.