Previous Spotlight on Essenters: Wietse Bosman
Next Spotlight on Essenters: Tom Voigt - Jansen

One Spec, Two Audiences, Faster Delivery

Pascal Dufour
7 minutes

One Spec, Two Audiences, Faster Delivery

Keeping a business flow legible when every team owns one part of it, and why teaching the teams BDD wasn’t enough on its own.

Ask a team what their service does and listen to the answer.

You will usually get something like: “it consumes the address-changed event, validates the EAN, and persists a metering point with a start date.” That answer is correct. It is also useless to almost everyone who needs it: the product owner, the team upstream, the team downstream, and the same team’s own new joiner in four months.

Somewhere behind that sentence there is a customer who moved house and would like their first invoice to be right. Nobody in the room said so.

This is the problem I care about more than test speed. We are moving from slow end-to-end tests to fast component tests at Essent, and we are getting the speed. But the interesting problem is the one the decomposition creates: when you break a business flow into components, each team owns one link, and the flow itself becomes nobody’s.

This post is about how we fix that, and about the detour we took first, because the detour is where most teams get stuck.

What decomposition quietly costs you

Take one flow: a customer moves house. On paper it’s one story with one outcome: new address, meter, tariff, first invoice, no double billing.

In our architecture that flow crosses five components, owned by different teams. Contracts. Address and meter registration. Tariff. Billing. Communications. Because we use DDD we have clear boundaries for each bounded context. Each team has its components, its own backlog (not a good idea), a shared pipeline (a good idea).


But look at what each team is now holding. Team B owns one part: given an event, register a metering point.
That is their entire view. Their backlog, their tests, their monitoring, their language. All about that part.

And here is the mechanism I want to name precisely: inside a component, the language degrades toward the technical. Not through carelessness. That’s simply the level at which the team’s daily work happens. The story arrives as “handle the address-changed event”. The implementation is events, EAN codes, idempotency keys. The tests get named after the code. Within a sprint or two, the business meaning of that link exists nowhere except in the heads of whoever was in the original refinement.

The consequences don’t look like testing problems:

  • The seams leak. Nobody double-bills a customer inside a component. It happens between two components, where each team’s local reading of a rule is reasonable and the two readings don’t match.
  • Nobody can answer the business’s question. “What happens if someone moves on the first of the month?” currently requires four people from three teams.
  • Change gets risky invisibly. A developer changes a rule inside their component, correctly, and has no way to see it is load-bearing two hops downstream.
  • Knowledge leaves with people. The technical description survives in the code. The business intent survives nowhere.

Documentation doesn’t fix this. We have flow diagrams and wiki pages. They are great when written but get out of date. If the description of the flow has to stay true, it has to be something that breaks the build when it  stops being true.

What we tried first, and why it wasn’t enough

The obvious move is to fix the understanding where it’s built: in the teams.

So we helped a couple of teams. We started from the technical side: set up component tests inside the team, get the stubbing and the pipeline right, and then bring the business in. Alongside that we brought ExampleMapping as a way to reach shared understanding in refinement. In every team, one or two people lit up. They ran sessions. We got real example maps, better conversations with the business, and component tests that hadn’t existed before.

Six months in, we could point at artefacts and at people who were better at their job. The integration problems eating our time to market were still there.

Two reasons, and neither is about the people.

An enthusiast can change how their team works. They can’t change what their team is handed. Our champions run good sessions on the story in front of them. But that story arrives pre-cut, already scoped, already committed. If the cut puts a business rule on the seam between two teams, better refinement inside one team never finds it.

There was no shared answer to “how do we test here”. Every team invents its own. Fine inside a component.
Not fine at the boundary, where two inventions meet. That is exactly where the integration problems live.

Bottom-up changes practice. Our problem is batch size and direction, and these get decided somewhere else.
So we keep the bottom-up work and add the other half:

1. An explicit test strategy for the department. Where does a check belong? What does a team prove before something moves on? What will not be caught at the end, because there is no longer a big phase at the end? Written down, specific enough to settle arguments. It makes the boundary between two teams somebody’s concern.

2. One flow, cut thin. We pick the move and leave processes and go at them from the business side: not “how do we test a move?” but “what is the smallest move we could deliver?” First slice: register a move, and a simple successful move. Not two contracts, not an unknown address, not the exotic dates. Those are the second slice, or the fifth. A thin slice has fewer seams, and integration problems live in seams.

And the direction of the work reverses. The first time round we started from the tests: set up component tests in the team, then try to make them readable to the business afterwards. Now we start from the business. The scenarios are written in Gherkin, from the business perspective, before any code exists, and they become the acceptance criteria for the thin slice. The component tests fall out of those scenarios, instead of the scenarios
being reverse-engineered from the tests.

Top-down gives the sessions something worth mapping. Bottom-up gives us people who can run them. Neither alone moves the needle.

One spec, three levels

The thing that ties both halves together is where the BDD scenarios live. We write them at three levels, in the same language, and each level answers a different question.

Flow level: does the whole journey still make sense? A handful of end-to-end scenarios that describe the move as the customer experiences it. Few, business-owned, run against the integrated environment. Their job is to keep the flow visible and to catch the seams.

Feature: A customer moves house
Scenario: A simple move results in one first invoice
Given a customer with one contract at their current address
When they move to a known address on 1 October
Then their old address is closed on 30 September
And their new address is supplied from 1 October
And their first invoice covers only the days from 1 October

Slice level: is this piece of functionality good enough to go live? The scenarios for the thin slice we’re delivering now, written with the business before the code exists. They span the two or three components the slice touches, and they are the slice’s acceptance criteria: the product should meet the agreed definition of done. When these pass, the slice can go live for exactly the customers it describes, behind a toggle, while the complex variants are still being built.


Feature: Registering a move
Scenario: A move date outside the allowed window is refused
Given a customer with one active contract
When they register a move with a date outside the allowed window
Then the move is not registered
And the customer is told which dates they can choose

Component level: does this component do its part? Many scenarios, one per component, run locally in seconds with dependencies stubbed. This is where the volume is, and where the speed comes from.


Feature: Registering a meter when a customer moves in
Scenario: The previous occupant's registration is closed on the move-in date
Given a home whose meter is registered to the previous occupant
When a new customer's contract starts on 1 October
Then the previous occupant's registration is closed on 30 September
And the new customer's registration starts on 1 October
And no day is registered to both customers

Read the three together. The same rule, no day belongs to two customers, appears at flow level as one line about the invoice, and at component level as a precise check inside one link. The slice in between says which version of the flow we commit to make work first. Same nouns, same verbs, top to bottom. That’s the ubiquitous language doing real work: when several teams each own a link, the shared vocabulary is the integration mechanism.

It also settles the old argument about component tests versus end-to-end tests. Component scenarios can’t test the seams; that’s what the few flow scenarios are for. Flow scenarios are too slow and too coarse to run on every commit; that’s what the many component scenarios are for. The slice scenarios tell you when you’re allowed to stop.

Gherkin as a description, not a test framework

We don’t write the component tests in Gherkin because we need Cucumber. We do it because Gherkin imposes a constraint plain test code doesn’t: you cannot write a Given/When/Then without saying who is involved, what happened, and what should be true afterwards, in language a non-programmer can check.


Before:
✓ handleAddressChangedEvent > closes existing metering point when
occupantId differs and startDate is not null

After: the component scenario above. Same behaviour. One of them the product owner can read.

The component test suite stops being a safety net only engineers can read and becomes the current, verified description of what this link contributes to the flow. A product owner can read the feature file. So can the team upstream, which matters more.

There’s a diagnostic in this, and it’s the most useful thing we find. If you cannot write the scenario without technical nouns, you don’t yet understand your part of the flow. When a draft reads “Given an address-changed event with a valid EAN”, that’s not a style problem. The team knows what they receive but not what it means.
Every time we push on that, we find a real gap, usually about something happening in someone else’s component.

That’s where the two audiences come from. One artefact. Engineers get an executable specification bound to real code. The business gets a readable, always-true account of what a component does for a customer.

Example Mapping: where the understanding gets built

Good scenarios aren’t a documentation exercise you do afterwards. If the understanding isn’t there, you get well-formatted scenarios that describe the implementation in longer words. It looks like progress and isn’t.

So the understanding is built before the code, in refinement, with Example Mapping. Four colours of card, about 25 minutes for a well-sized story: the story on yellow, rules on blue, concrete examples on green, questions nobody in the room can answer on red.

For a team that owns one link of a flow, the red cards are the point. Our unanswerable questions are overwhelmingly about what happens outside the component. What does billing do if the move date is backdated? Who sets the tariff if the previous occupant had a fixed contract? Those red cards are the flow showing up in the room: the seams becoming visible while they’re still cheap. On a thin slice, the pile is short enough to actually go and answer.

Two things we learn. Don’t write Gherkin in the session. Use rough, conversational examples (“the one where the meter’s still on the old tenant”) because the moment you negotiate syntax, the conversation stops being about the business. Draft the Gherkin afterwards and have the business and the team review it. That review is the test of whether the understanding transfers.

And if there’s no business person in the room, you’re not example mapping. Three developers guessing at rules together is a design meeting with coloured cards.

From agreed examples to code

Only now does tooling matter. The expensive part of BDD has always been maintaining the spec layer by hand.
It’s the first thing dropped under deadline.

That changes, because a specification is now something you write anyway: it’s how you brief the coding agent. We use OpenSpec, a small folder in the repo ( openspec/specs/ for the agreed state, openspec/changes/ for changes in flight), each change carrying a proposal, requirement deltas and a task list. You agree the spec with the agent before it writes code, and archive it into the living spec when done. Its requirements are plain
markdown with WHEN / THEN scenarios, so the example-mapping output goes in almost verbatim. We use AI to generate the tests, following our guardrails.

The chain runs: example mapping → agreed rules and examples → OpenSpec spec → Gherkin scenarios at the right level → tests. Change the intent at the top and it propagates down.

A side effect we didn’t plan for: the agent’s output becomes reviewable by the business. Generation is cheap.
Agreement is not, and this is where you spend it.

What you can try in your next refinement

1. Ask your team, out loud, what their component does, and ban technical nouns. Let at least one example be theirs. If the answer stalls, you’ve found the gap.
2. Run one Example Mapping session with a business person present. Count how many red cards are about someone else’s component.
3. Rewrite one component test as a scenario and show it to your product owner and team. Either they can read it or they can’t. Both are information.
4. Write the flow-level scenario for one journey, then ask which slice of it you could go live with first. Then write only those scenarios.

Where this is going

Fast tests are the problem we set out to solve. Shared understanding of the whole flow turns out to be the problem worth solving, and it takes a change from two directions to get there. The teams need the skills. The organisation needs to hand them a thinner problem, and a shared answer to how it will be tested.

What we have is a set of feature files two very different audiences can both read, at three levels that each answer a different question, and refinement sessions that surface the seams before the code does.

We will see in the upcoming months whether time to market is getting shorter.

Pascal Dufour

As QA Lead for the I-join art at Essent, Pascal works with DevOps teams to improve how they build, verify, and run their application. The focus for the art is to optimise the time to market to innovate with the use of DDD, BDD, and AI.