skip to content

How does parameterizing a step definition stop the glue layer growing one definition per sentence?

level: middleimportance: must knowfreq 58%

answer

  1. Grow with behaviors, not with sentences
  2. The generated skeleton is the trap
  3. Replace the literal that varies
  4. Register the conversion once, use it everywhere
  5. Count definitions against scenarios

basics

~20 s

Write one definition per behavior and capture the parts that vary as parameters. Sentences differing only by a value then reuse the same function, so the library grows with the number of behaviors, not sentences.

solid answer

~50 s

The sprawl has a mechanical cause: a new sentence arrives, the runner reports it undefined and prints a skeleton, and pasting that skeleton is easier than looking for the definition that already covers the behavior. The discipline is to search first and, when the only difference is a value, replace the literal with a typed placeholder rather than register a second pattern. Custom parameter types go further: register one transform that turns matched text into a domain object, and every step using that placeholder receives a typed argument for free. Optional and alternate text markers absorb wording variations such as singular versus plural. Keep the definitions thin and push shared work into a helper layer beneath them, so duplication collects in helpers, which are cheap to refactor, instead of in the step library, which is a shared namespace. Watch the definitions-per-scenario ratio as the signal.

code

pseudocode · 12 lines
pseudocode
# before: one definition per sentence
step("a teacher schedules Year 9 Maths in Tuesday period 3", ...)
step("a teacher schedules Year 11 Physics in Thursday period 1", ...)
step("a teacher schedules Year 8 Art in Monday period 5", ...)

# after: one definition per behavior
parameterType("course", "[A-Za-z0-9 ]+", function(text) { return courses.byName(text) })

step("a teacher schedules {course} in {word} period {int}",
     function(course, day, period) {
         planner.schedule(course, day, period)
     })

go deeper

for a junior

Be ready to say that one definition should serve many sentences that differ only by a value, and to show a literal replaced by a placeholder. Knowing that the generated skeleton is a starting point, not the finished design, is the main thing.

for a middle

Explain the mechanics: typed placeholders, a custom parameter type registered once to convert text into a domain object, and optional or alternate text absorbing wording variation. Be able to name the smells of over-parameterizing.

for a senior

Demonstrate judgement about where duplication should live — thin definitions over a helper layer — and use a concrete signal such as the definitions-per-scenario ratio when arguing for consolidation work.

for a principal

Own the shared-library question: when a phrase namespace crosses teams it is an interface, so talk about ownership, the blast radius of generalising a pattern, and whether team-local duplication is the cheaper failure.

The glue layer's characteristic failure is growth in the wrong dimension: a library that grows with the number of *sentences written* instead of the number of *behaviors the product has*. **Parameterization** is the main tool against it. ## Why sprawl happens It is not laziness so much as a path of least resistance built into the tooling: 1. Someone writes a new scenario, runs it, and the runner reports the new line as undefined and helpfully prints a skeleton definition. 2. Pasting the skeleton takes seconds; opening the existing library, finding the near-identical definition, and generalising it takes minutes and touches code other scenarios depend on. 3. Repeat that a few hundred times and the arithmetic gets ugly. A timetable-planner suite that had grown this way carried 418 step definitions for 96 scenarios — more than four definitions per scenario, which means almost every line ever written got its own function. After consolidation it held 63, and the suite exercised exactly the same behavior. **The ratio of definitions to scenarios is the cheapest health signal you have**; a mature library trends toward far fewer definitions than scenarios, because scenarios reuse. ## One definition per behavior The rule to state in an interview is that a definition corresponds to a *behavior of the product*, not to a *string of text*. "A course is scheduled into a period" is one behavior. `"Year 9 Maths" into Tuesday period 3` and `"Year 11 Physics" into Thursday period 1` are two instances of it. So the pattern captures course, day and period, and one function serves both — and serves every future row of examples anyone adds without a line of new glue. ## Placeholders and typed parameters - **Basic placeholders** capture a number, a quoted string, or a single word and hand them over already converted, so the function signature reads like a domain operation rather than a parse. - **Custom parameter types** are the step up: you register once that the text `Year 9 Maths` resolves to a `Course` object, and from then on every definition using that placeholder receives a `Course`. The conversion, the lookup, and the "no such course" error message live in one place instead of being re-implemented in twenty definitions. This is also what keeps definitions thin — a definition that begins with five lines of turning text into objects has swallowed a job that belongs in a parameter type. ## Wording variation without extra definitions Alternation and optional-text markers in a step expression let one pattern absorb "a teacher"/"the teacher", singular/plural, and a trailing clause that some sentences carry and others do not. Used in moderation this removes a whole class of duplicate definitions that exist purely because two authors punctuated differently. Used immoderately it produces patterns nobody can read, which is the next hazard. ## Over-parameterizing is a real failure too A pattern of the shape "the user does <anything>" matches everything, which makes it a magnet for ambiguous matches and makes the library impossible to reason about — you can no longer tell from a sentence which function will run. Two smells mark the line: - First, a **catch-all wildcard** where a typed placeholder would do. - Second, **parameter count**: a definition taking five or six arguments has stopped being a sentence and become a function call written in prose, and it usually wants splitting or a table argument instead. A useful reflex is to parameterize the *values* a behavior varies over and to leave the *behavior itself* in the literal text of the pattern. ## Where duplication should go instead Definitions should be one to three lines: convert arguments into a call on a helper or driver, or assert on what a helper returns. When two definitions genuinely need the same twelve lines of setup, that setup moves into the helper layer. - **Duplication in helpers** is ordinary code duplication and can be refactored freely; - **duplication in the step library** is duplication in a shared, text-addressed namespace where any change risks breaking scenarios nobody in the room has read. ## Ownership when the library is shared Once several teams load the same glue, **a phrase becomes an interface**. Adding a placeholder to an existing pattern is a compatible change if the old sentences still match, and a breaking one if they do not — the failure surfaces as an undefined step in someone else's suite. Teams that share a library therefore need a named owner for the phrase set and a habit of grepping the whole corpus before generalising a pattern, exactly as they would before changing a published signature.

  • When is registering a second definition better than generalising the existing one?
    When the two sentences describe different behaviors that happen to look alike. Generalising then produces a pattern with a branch hidden inside it, which is worse than two honest definitions. The test is whether the difference is a value the behavior varies over, or a different behavior wearing similar words.
  • What would make you suspect a step definition is over-parameterized?
    A wildcard that matches any text, a parameter list of five or six arguments, or a pattern you cannot read aloud and predict from. All three mean the definition has stopped representing one behavior; the first also invites ambiguous matches, since a catch-all will collide with any narrower pattern registered later.
  • How do you generalise a pattern that several teams' scenarios already match?
    Treat it as a published interface. Check that every existing sentence still matches the new pattern — adding a placeholder where a literal stood usually breaks them unless the literal is kept as an alternative — and run the whole corpus, not just your own scenarios. A break shows up as an undefined step in a suite you do not own.

saying these in an interview costs you the question

  • Pastes the generated skeleton without searching the library
  • Rewrites the scenario sentence to fit an existing literal
  • Thinks one definition per sentence is the intended design
  • Uses a catch-all wildcard as the standard parameter
  • Puts text-to-object conversion inside every definition
  • Measures glue health by lines of code rather than reuse

context