bdd-step-library-curator · git:20260725.b4ad92d · 2026-07-25 · sha256 19c57682f22e5ed3
bdd-step-library-curator git:20260725.b4ad92dA
Immutable. This exact content never changes and is served at /api/v1/blob/19c57682f22e5ed3.
---
name: bdd-step-library-curator
description: "Keeps a BDD step-definition library DRY across a Cucumber / Behave / Reqnroll project - inventories every step definition, detects duplicates (different patterns matching the same intent), recommends canonical consolidations, reorganizes steps by domain, and publishes a step-library README the team greps for \"is there already a step for X?\" before authoring new ones. Use on a cadence as the antidote to step-definition proliferation in long-lived BDD projects, or when a new engineer reports they cannot find an existing step."
---
# bdd-step-library-curator
## Overview
After 6 months of a BDD project, step definitions proliferate:
- Two engineers write `Given a user` and `Given a logged-in user`
for the same fixture.
- Three slightly-different "I click X" steps (one for buttons,
one for links, one for arbitrary elements) - all do the same
thing.
- A new engineer can't find existing steps and writes a fourth
variant.
The result: step library bloat → Gherkin features hard to read,
step ambiguities, runtime errors, drift.
This skill builds a curation workflow.
## When to use
- A BDD project's step count exceeds ~50 (proliferation threshold).
- A new engineer reports "I couldn't find a step for X."
- Quarterly: scheduled step review.
- Before adopting BDD across multiple teams (proactively design
the step library shape).
## How to use
1. **Inventory** every step definition across the project - extract
patterns per runner (the per-runner commands are in
[references/step-extraction-and-overlap.md](references/step-extraction-and-overlap.md)).
2. **Detect** duplicate / overlapping patterns by normalizing wording
and grouping - the overlap script lives in the same reference.
3. **Recommend** one canonical step per duplicate group; deprecate the rest.
4. **Reorganize** step files by domain and **publish** the step-library
README - the team's "is there a step for X?" index.
5. **Gate** new steps at pre-merge so every added definition is acknowledged.
6. **Repeat** on a cadence (quarterly plus threshold-triggered reviews).
## Step 1 - Inventory step definitions
Extract every declared step pattern across the project. Each runner
uses its own annotation syntax (`@Given` in Cucumber-JVM, `@given`
in Behave, `[Given]` in Reqnroll / SpecFlow) - the per-runner
extraction commands are in
[references/step-extraction-and-overlap.md](references/step-extraction-and-overlap.md).
The inventory produces a step audit:
```
Total step definitions: 142
Unique patterns: 138 (4 ambiguous duplicates already)
Per Gherkin verb:
Given: 58
When: 34
Then: 47
And: 3
```
## Step 2 - Detect duplicates / overlaps
Two patterns are likely duplicates when:
- Same wording, different phrasing: `Given a user` vs `Given an
existing user` vs `Given the user`.
- Same parameters, different verbs: `When I click {label}` vs
`When I press {label}` vs `When I tap {label}`.
- Same fixture, different shape: `Given a cart with {n} items` vs
`Given a cart containing {n} items`.
Normalize each pattern (lowercase, drop articles, collapse
parameters) and group by the normalized form; any group of size
greater than one is a candidate cluster. The overlap script that
does this is in
[references/step-extraction-and-overlap.md](references/step-extraction-and-overlap.md).
## Step 3 - Recommend consolidation
For each duplicate group:
```markdown
**Duplicate group:** 4 step definitions with normalized "user is
logged in"
| Pattern | File / line |
|------------------------------------|----------------------------|
| `Given a user is logged in` | `auth_steps.py:12` |
| `Given the user is logged in` | `cart_steps.py:8` |
| `Given an authenticated user` | `checkout_steps.py:5` |
| `Given a logged-in user` | `profile_steps.py:14` |
**Recommendation:** Consolidate to **`Given a logged-in user`**
(highest count of usage in current Gherkin; clearest wording).
Replace the other 3 step definitions with a single canonical one
in `shared_steps.py`. Update the Gherkin features that use the
deprecated patterns.
```
## Step 4 - Domain organization
Group steps per domain area:
```
features/steps/
├── shared/ # cross-domain (login, navigation, etc.)
│ ├── auth_steps.py
│ ├── navigation_steps.py
│ └── data_steps.py
├── checkout/ # checkout-specific
│ ├── promo_steps.py
│ ├── payment_steps.py
│ └── cart_steps.py
├── account/ # account-specific
│ └── profile_steps.py
└── README.md # step library index
```
The README is the discoverability artifact:
```markdown
# Step library
## Shared (cross-domain)
### Auth
- `Given a logged-in user` (auth_steps.py:12) - creates and logs in a generic test user.
- `Given a logged-in admin` (auth_steps.py:25) - logged-in user with admin role.
- `Given an unauthenticated visitor` (auth_steps.py:38) - no session.
### Navigation
- `When I navigate to {path}` - go to URL.
- `When I click {label}` - click any element with this label.
## Checkout
### Cart
- `Given the cart contains {qty} of {sku} at ${price}` - seed a cart.
- `Given the cart is empty` - empty cart.
### Promo codes
- `Given promo code {code} is active` - pre-seed a promo in the admin.
- `When I enter {code} in the promo input` - type the code.
- `When I click {label}` - (uses shared step).
(...)
```
The README + grep is the team's "is there a step for X?" tool.
## Step 5 - Pre-merge step gate
Add a CI check that flags new step definitions so no engineer adds a
duplicate unknowingly. The check warns (doesn't block), forcing the
author to acknowledge the new step and grep the README first. The
`check-new-steps.sh` script is in
[references/step-extraction-and-overlap.md](references/step-extraction-and-overlap.md).
## Step 6 - Quarterly cadence
| Cadence | Trigger |
|--------------------|---------------------------------------------------|
| Quarterly | Scheduled step library review. |
| New team member | Onboarding: walk the README. |
| Step count exceeds threshold | Triggered review. |
| New domain area | Add steps; update README. |
## Worked example
Curating a 142-step library in a mixed Behave project, end to end.
**1. Inventory.** Running the Behave extraction over `features/steps/`
yields 142 step definitions and 138 unique patterns - so 4 are
already ambiguous duplicates the runner would flag at match time:
```
Total step definitions: 142
Unique patterns: 138
Given: 58 When: 34 Then: 47 And: 3
```
**2. Detect.** The overlap script normalizes patterns and surfaces
one cluster:
```
Likely duplicates (4): normalized "user is logged in"
Given a user is logged in - auth_steps.py:12
Given the user is logged in - cart_steps.py:8
Given an authenticated user - checkout_steps.py:5
Given a logged-in user - profile_steps.py:14
```
**3. Consolidate.** Pick `Given a logged-in user` as canonical
(clearest wording, most-used in current Gherkin). Move it into
`features/steps/shared/auth_steps.py`, delete the other three
definitions, and rewrite the `.feature` files that used the
deprecated phrasings - edit the Gherkin first so no scenario is
left orphaned.
**4. Reorganize + publish.** Split the flat `features/steps/` into
`shared/`, `checkout/`, and `account/`, then regenerate the README
index. The library now advertises
`Given a logged-in user (auth_steps.py) - creates and logs in a generic test user`
under Shared → Auth.
**5. Gate.** Add the pre-merge new-step check. The next PR that adds
`Given the customer is signed in` trips the warning; the author greps
the README, finds `Given a logged-in user`, and reuses it instead of
adding a fifth variant.
Result: 142 → 139 step definitions, zero ambiguous duplicates, one
discoverable index the whole team searches before writing a step.
## Anti-patterns
| Anti-pattern | Why it fails | Fix |
|-----------------------------------------------------------------------|---------------------------------------------------------------------------|-----|
| Per-engineer step files (`alice_steps.py`) | Ownership without domain alignment; duplication across files. | Domain-organized files (Step 4). |
| Step library README missing | Discoverability nil; engineers re-implement. | README per Step 4. |
| Ambiguous step deletion without grep | Breaks scenarios silently. | Replace canonical → deprecated; update all Gherkin first. |
| Step "shared library" that's a giant `helpers.py` | All engineers conflict on it; merge hell. | Domain split (Step 4). |
| Reviewing step count quarterly only | Proliferation outpaces review; library bloats. | Pre-merge gate (Step 5). |
## Limitations
- **Heuristic duplicate detection.** Some patterns are
legitimately distinct (`Given a user` for auth vs `Given a user
with profile data` for profile tests).
- **Domain organization is per-team.** What's "shared" varies.
- **Consolidation cost.** Each refactor touches Gherkin + step
definitions; not free.
- **Doesn't fix Gherkin quality.** A clean step library doesn't
prevent imperative-style Gherkin scenarios. Pair with a Gherkin
style review.
## References
- Step extraction commands, the overlap-detection script, and the
pre-merge gate:
[references/step-extraction-and-overlap.md](references/step-extraction-and-overlap.md).
- `cucumber-testing`,
`behave-testing`,
`reqnroll-testing` - per-language
runners this curator works alongside.