Skip to content
CukeDedup
v0.13.0 — latest release

Find duplicate step definitions.Remove ambiguity.Keep your Cucumber suite maintainable.

CukeDedup is a fast static analyzer for duplicate, ambiguous, reusable, and unused Cucumber/Gherkin step definitions. It reads your project configuration and test code — and never executes either one.

Detection rules
8
Report formats
5
Frameworks
5
terminal
$ npm install --save-dev cuke-dedup
added 1 package in 2s

Node.js 20 or newer. Also installable with cargo install cuke-dedup.

terminal — cuke-dedup .
$ npx cuke-dedup .
CukeDedup v0.13.0

duplicate-matcher
  [error] Two step definitions use the same effective matcher
    --> features/steps/login.steps.ts:7:1
    related: features/steps/session.steps.ts:4:1
    action: Keep one definition or make the matchers intentionally distinct

parameterization-candidate
  [warning] Handler structures differ primarily in literal values
    evidence: `the cart has 5 items` ↔ `the cart has 10 items`

Analyzed 11 definitions and 12 feature steps: 3 errors, 3 warnings
Duplication: 2 of 11 definitions (18.18%) — FAIL
Detection time: 15.5 ms

Real output from an analysis run over a small demo project.

The problem

Step libraries grow quietly — and start contradicting each other.

In a growing Cucumber suite, new step definitions are added by many people over many months. Nobody notices the exact duplicate, the near-copy that only differs in a literal, or the definition no feature has used for a year — until steps become ambiguous and refactoring feels risky.

  • Duplicates and near-duplicates

    Two definitions match the same text — exactly, or after normalization. Whichever runs first wins.

  • Ambiguous feature steps

    A concrete step in a .feature file matches multiple definitions, and the suite behaves unpredictably.

  • Copy-paste handlers

    Handlers that differ only in literal values (`5 items` vs `10 items`) instead of using a step parameter.

  • Dead definitions

    Steps no feature references anymore, still imported, still maintained, still misleading readers.

  • Rising maintenance cost

    Every duplicated or unused step makes the suite harder to evolve and slower to reason about.

two files, one matcher — CukeDedup flags it
// features/steps/login.steps.tsWhen('the user signs in with a valid password', () => { /* ... */ }); // features/steps/session.steps.ts  — added months laterWhen('the user signs in with a valid password', () => { /* ... */ });

The solution: analysis, not guesswork

CukeDedup compares every discovered definition — matcher text, normalized forms, and handler structure — and reports eight classes of duplication, ambiguity, reuse, and disuse with suggested actions. It is an analysis tool: it tells you exactly what collides and why, and incomplete analysis is reported instead of treated as clean.

See all eight rules →

Features

Everything a step-definition audit needs — and nothing that executes your code.

Eight detection rules

Exact matcher duplicates, normalized equivalents, ambiguous feature steps, overlapping matchers, duplicate handlers, near-duplicates, parameterization candidates, and unused definitions — each with a suggested action.

Rule reference →

Five report formats

Terminal for humans, JSON and JSON Lines for tooling, a searchable self-contained HTML report, and SARIF for GitHub code scanning.

Report formats →

Quality gates

A duplication threshold for CI, semantic baselines so existing findings do not block new work, changed-file analysis, suppressions with reasons, and .cuke-dedupignore patterns.

CI and baselines →

Zero-config start, real config when needed

Conventional discovery gets you running with `cuke-dedup .` — no configuration required. A .cuke-dedup.json, .cuke-dedupignore, and supported framework configs refine it, with CLI flags always winning.

Configuration →

GitHub Action with provenance

A ready-made Action that downloads a native binary and verifies its checksum and build provenance, with SARIF, report artifacts, and baseline support.

GitHub Action →

Built for agents and humans

An installable Agent Skill for safely interpreting and remediating JSONL findings, plus experimental opt-in Cucumber-Ruby support with a documented subset.

Install the skill →

Reports

Findings you can act on, not just count.

Every finding carries severity, precise source spans, structured evidence — matcher differences, similarity scores, handler analysis — and a suggested action. The report below is a real cuke-dedup . --reporters json output (schema v3) from a small demo project; expand any finding to inspect it.

11

Definitions analyzed

12

Feature steps

6

Active findings

18.18%

Duplication rate

errorambiguous-stepFeature step `the user signs in with a valid password` matches 2 definitions

Locations

features/login.feature:5:5
features/steps/login.steps.ts:7:1
features/steps/session.steps.ts:4:1

Evidence

  • `the user signs in with a valid password`, `the user signs in with a valid password`
  • More than one definition accepts at least one concrete feature step

Suggested action

Make the matchers mutually exclusive

Ambiguous step · schema v3 finding

errorambiguous-stepFeature step `the user signs in with a valid password` matches 2 definitions

Locations

features/login.feature:10:5
features/steps/login.steps.ts:7:1
features/steps/session.steps.ts:4:1

Evidence

  • `the user signs in with a valid password`, `the user signs in with a valid password`
  • More than one definition accepts at least one concrete feature step

Suggested action

Make the matchers mutually exclusive

Ambiguous step · schema v3 finding

errorduplicate-matcherTwo step definitions use the same effective matcher

Locations

features/steps/login.steps.ts:7:1
features/steps/session.steps.ts:4:1

Evidence

  • Matchers are textually identical
  • Handler behavior signatures differ

Similarity

Matcher similarity
100%
Handler similarity
0%

Suggested action

Keep one definition or make the matchers intentionally distinct

Duplicate matcher · schema v3 finding

warningparameterization-candidateHandler structures differ primarily in literal values

Locations

features/steps/checkout.steps.ts:3:1
features/steps/checkout.steps.ts:7:1

Evidence

  • `the cart has 5 items` ↔ `the cart has 10 items`
  • Handlers have the same control flow and calls after literal normalization

Similarity

Matcher similarity
97%
Handler similarity
95%

Suggested action

Consider replacing the literal differences with a step parameter

Parameterization candidate · schema v3 finding

warningunused-definitionStep definition `the dashboard gets displayed` is not used by the feature corpus

Evidence

  • No discovered feature step matched this definition

Suggested action

Remove the definition or add the missing feature usage

Unused definition · schema v3 finding

warningunused-definitionStep definition `an administrator has signed in` is not used by the feature corpus

Evidence

  • No discovered feature step matched this definition

Suggested action

Remove the definition or add the missing feature usage

Unused definition · schema v3 finding

Prefer a searchable interface? The analyzer also ships a self-contained HTML report, and SARIF for code scanning.All report formats →

Works with your stack

Five workflows, one analyzer.

Package entrypoints are matched exactly, so registrations are only recognized when they come from a supported package. Classic .feature files and Gherkin Markdown .feature.md files are both discovered. Experimental, opt-in Cucumber-Ruby support covers an explicitly selected subset.

WorkflowRecognized registrations
Cucumber.js@cucumber/cucumber and legacy cucumber; ESM, CJS, aliases, namespaces, and static local re-exports.
Playwright-BDDcreateBdd() registrations and playwright-bdd/decorators class-method decorators.
Cypress Cucumber@badeball/cypress-cucumber-preprocessor and legacy cypress-cucumber-preprocessor/steps.
Jest-CucumberdefineFeature → scenario → step callbacks, with handler-reuse rules.
Vitest Cucumber PluginESM Given, When, and Then exports; global step registry.

Plain Playwright projects are supported when their Gherkin bindings use Cucumber.js or Playwright-BDD.Discovery details →

Integration

From local check to CI quality gate.

.github/workflows/cuke-dedup.yml
permissions:  contents: read steps:  - uses: actions/checkout@v4    with:      fetch-depth: 0      persist-credentials: false  - uses: figueiredoluiz/cuke-dedup@v0.13.0    with:      path: .      threshold: 5      reporters: terminal,json,html,sarif

The Action downloads a native binary and verifies its checksum and build provenance. Changed-file checks, baselines, report artifacts, and hardened pinning are covered in the docs.

Predictable exit codes

0
The configured quality gate passed.
1
The duplication threshold, an error rule, or the new-finding allowance failed.
2
Discovery, parsing, configuration, or another operational error prevented a valid run.

Warnings alone never produce exit code 1.

Fits the workflow you already have

  • Local: runcuke-dedup . from any project root — zero configuration required.
  • Code review: HTML and JSON reports, suppressions with reasons, and semantic baselines keep signal high.
  • CI: the GitHub Action, changed-file analysis with --changed-since, and SARIF upload for code scanning.