3.8 Engineering
Everything until now bought you speed — engineering is where you spend it, turning live specifications into production-ready code one small merge at a time.
Engineering puts everything since 3.3 Identify product increment to work. Every feature in the product increment arrived in the increment backlog as a set of INVEST-shaped stories, each with Gherkin scenarios for validation, each live-linked to the design artifacts that model it. Check the definition of ready before the build starts:
Before starting
Most methodologies treat engineering as the moment the real work starts — the point where design documents get set aside and the team starts improvising. In this playbook it’s the opposite: engineering is where the work gets easy. Teams that struggle during the build are almost never struggling with the code. They’re struggling with ambiguity — unclear requirements, untestable acceptance criteria, designs that live in someone’s head. We spent five activities eliminating exactly that ambiguity.
There’s a reframing worth holding on to as you work through this activity: engineering is not where quality gets added. It’s where quality gets proven. The specifications your team formulated in 3.6 Specification and elaborated in 3.7 Elaboration already define what “correct” means, at every level the system is tested. The engineering activity makes those definitions executable — and then writes the code that satisfies them.
This chapter completes the third stage of Behavior Driven Development: Automation — connecting Gherkin to executable test code, so the specifications stop being documents about the product and become part of the product.
The pipeline deserves special mention. Chapter 2.8 Delivery processes & tools built the machinery this activity depends on: ephemeral environments, test batteries, automated security scanning, release orchestration. If your pipeline can’t take a merge from trunk to a production-ready state without a human pushing buttons, stop and fix that first. Everything in this chapter assumes the machinery works.
Guide to engineering
The engineering activity runs inside your delivery cadence, so unlike previous activities there’s no workshop to schedule — the “workshop” is the daily work itself. What the playbook prescribes is the way the team works: how specifications become tests, how tests drive code and how code reaches trunk and beyond.
That way of working has a name: T*D — Test-Driven Development, Trunk-Based Development and Team-Focused Development, fused into one operating model. I introduced it in Pull requests are a symptom of low trust: here’s the fix, and if you haven’t read that article, do it before the build starts — it’s the infrastructural argument underneath everything in this chapter. T*D isn’t three separate practices to adopt; it’s one model with three legs, and the engineering activity is where all three legs carry weight at once. The process below is organized around them: tests drive the code, code flows to trunk daily through automated gates and the team — not a queue — owns quality and review.
Goals and impact
This activity has clear goals that align with specific, long-term impact to the project. These are, respectively:
Inputs
- Elaborated feature specifications. Gherkin scenarios at the component and assembly levels, from 3.7 Elaboration.
- Increment backlog. INVEST-shaped user stories with dependencies captured and sequenced.
- Engineering models. Marble and sequence diagrams from 3.5 Modeling, live-linked to their specifications.
- Delivery pipeline. The fully automated CI/CD pipeline from 2.8 Delivery processes & tools.
- UX assets. Wireframes and design system references for stories with customer-facing surface area.
Process
The process is three moves, applied story by story: make the specification executable, drive the implementation with tests and deliver in small, continuously integrated increments. The first two moves are the test-driven leg of T*D; the third is the trunk-based leg. The team-focused leg runs through all of it — we’ll get there in the collaboration rhythm below.
Feature files become code
The best place for a specification to live is a feature file under source control. This is worth saying plainly: feature files are part of the product. They are source code. They version with the code, branch with the code, merge with the code and — critically — fail the build when the code stops honoring them. This is what we mean by “live” test specifications, and it’s the practical fulfillment of Specification by Example: the requirement and its proof are the same artifact.1
The most common convention is one feature per file, or a few closely related features grouped together. Keep the scenarios for a feature in one place; scatter them and you’ll lose the narrative that makes the file readable to your product manager.
Continuing with the banking example from the last two chapters — the anti-fraud feature that protects the loan prequalification flow:
@LOAN-2417 @marble-4.2
Feature: Anti-fraud event processor
As a bank manager
I want fraudulent activity identified during prequalification
So that I can ensure the applicant is not committing fraud
Scenario: Borrower is not a fraud and passes fraud checks
Given a "BorrowerPrequalificationRequested" event with correlation ID "PQ-0093"
And the borrower profile matches no entries on the fraud watchlist
When the anti-fraud processor evaluates the application
Then a "FraudCheckPassed" event is emitted
And the event payload includes a risk classification of "standard"
Notice the tags. @LOAN-2417 is the Jira ticket; @marble-4.2 is the engineering drawing this scenario proves out. Those two tags are the live links from 3.7 Elaboration, now enforceable in the repository. The user story references the feature file. The feature file references the marble. The marble references the Jira ticket. Anyone — engineer, product manager, auditor — can walk the chain in either direction, from customer desire to key result to specification to model to code. That’s traceability from feature to test, and it costs almost nothing to maintain because the links live where the work lives.
Make the links mechanically enforceable. A CI check that reconciles feature-file tags against your marble inventory catches drift the day it happens, not six months later when nobody trusts either artifact. We covered the mechanics in 3.7 Elaboration; this is where they start paying rent.
The two loops of test-driven development
With an executable specification in hand, the implementation follows a rhythm that Test Driven Development practitioners will recognize — except it runs as two nested loops rather than one.
The outer loop is the behavior loop. Wire the Gherkin scenario to step definitions and run it. It fails — of course it fails, the feature doesn’t exist yet. That red scenario is your north star for the story. It describes, in the business’ own ubiquitous language, exactly what “done” means.
The inner loop is the unit loop. Now write the code that makes the scenario pass — test-first, in the classic red-green-refactor cycle. Write a small failing unit test, make it pass with the simplest code that works, refactor, repeat. Each turn of the inner loop moves the outer loop closer to green. When the scenario passes, the story’s behavior is proven — at the component level, at the assembly level, at whatever levels you elaborated in 3.7.
This two-loop structure is what makes the goals of this activity achievable rather than aspirational:
- Refactor with confidence. As long as the behavior tests pass, the team is free to restructure, simplify and improve the code underneath. The scenarios define the contract; everything below the contract is negotiable. This is the payoff for all that specification work — a safety harness that lets the team improve the code continuously instead of tiptoeing around it.
- Shift defect discovery left. A defect caught by a failing unit test costs minutes. The same defect caught in production costs orders of magnitude more — Boehm and Basili’s research put the multiplier at up to 100x for post-release fixes. The two loops are a machine for catching defects at the cheapest possible moment: while the intent is still fresh in the author’s head.2
- Design under pressure. Writing the test first forces you to consume your own interface before you build it. The outside-in pressure produces cleaner, more focused code — I’ve made the fuller argument in The balanced power of TDD, and the scenario-level version in Why testing use cases alone fails.
One warning, because I’ve watched teams talk themselves into it: “we’ll write the tests after” is not a schedule adjustment. It’s a different practice with different results. Test-after surrenders the design pressure, invites untestable code and — in every case I’ve observed — quietly becomes test-never the moment the increment gets tight. The tests exist before the feature exists. That’s the practice.
Modern delivery ergonomics
The third move governs how finished work flows out of the team, and it comes down to a small set of non-negotiable disciplines.
Merge every day. No merge should be large. Code returns to trunk daily, with incomplete work carried behind feature flags. Long-lived branches are feature trains in miniature — they accumulate risk, invite conflicts and defer the integration pain to the worst possible moment. Small daily merges keep every diff reviewable in minutes and every failure easy to attribute. The research backs the discipline: DORA’s analysis found elite performers are 2.3 times more likely to practice trunk-based development, and it remains one of the strongest predictors of delivery performance.3
Documentation is part of the merge. No merge happens without complete documentation — and in this playbook, documentation means the Domain Driven Design artifacts and engineering diagrams that describe the change, not a wiki page someone promises to write later. If the change altered a marble, the updated marble merges with it. If it introduced a new term, the ubiquitous language glossary updates with it. Documentation that travels with the code stays true; documentation that travels separately drifts.
The only path to higher environments is through automated testing gates. If tests fail, the build fails and the code doesn’t move — no exceptions, no overrides, no “just this once.” The test batteries you built in 2.8 Delivery processes & tools are the gatekeepers, and the feature files you just wired up are their instructions. This is also why the transition to production takes minutes rather than weeks: the gate is a green pipeline, not a committee.
Every iteration delivers to a production-ready state. Not “code complete.” Not “ready for hardening.” Production-ready — integrated, tested, documented and deployable. Deployment itself happens in the Operations phase, but the increment leaves engineering with nothing left to finish — daily deployments are possible. This is how we avoid monolithic feature trains, and it’s the same “just enough, all the way” discipline behind the steel thread approach.
The collaboration rhythm
This is the team-focused leg of T*D — and it’s the one that makes the other two sustainable. Engineering is not a phase where product hands off to technology and waits. The three-amigos conversations from 3.6 Specification and 3.7 Elaboration continue right through the build — they just change tempo.
Scenarios will surprise you. A developer three hours into a story discovers a case nobody formulated: what happens when the fraud watchlist service times out mid-check? That’s not a blocker; that’s the process working. The specification is a living document — the discovery goes back through a quick three-amigos conversation (a short example mapping session is the fastest way to run it), the scenario gets formulated and elaborated, the feature file gains a case and the live links keep the whole chain honest. What the team must not do is improvise the answer in code and leave the specification behind. The moment the code knows things the specification doesn’t, your value chain has gone slack.
Peer review, done right
Let’s talk about review, because this is where most teams burn the time they saved everywhere else.
The T*D article draws the sharp dividing line: the routine, blocking, async PR queue is the last manual gate left standing in a pipeline whose entire purpose is to eliminate manual gates. That argument stands. But it’s an argument against the routine gate, not against review — and the line is worth drawing carefully, because the answer isn’t “no PRs.” It’s “PRs where they earn their cost.”
The default is trunk-based development. Routine changes — the well-specified story in a familiar context, covered by passing behavior tests, written by an engineer who knows the territory — go through the automated validation gates and straight into trunk. The pipeline is the reviewer: the feature files prove the behavior, the test batteries prove the assembly and the security scans prove the hygiene. Adding a human approval to that path buys you almost nothing and costs you hours of queue time per change. Rouan Wilsenach’s Ship / Show / Ask framing is a practical way to sort the work: most changes Ship straight to trunk, some Show — merged immediately, with a non-blocking PR opened as a venue for discussion — and a deliberate few Ask.4
The Ask category is the interesting one. Some changes genuinely warrant a human gate:
- Security-sensitive surfaces. Authentication, authorization, payment flows, cryptography, anything handling personal data.
- Novel architecture. The first implementation of a new pattern, a new bounded context, a change to a contract other teams depend on.
- High blast radius. Changes to shared infrastructure, data migrations, anything that’s expensive to roll back.
- Unfamiliar territory. An engineer working in a context they don’t yet know well — the review is as much mentorship as inspection.
For these, open a pull request — and keep it worthy of the attention it requests: small, focused and reviewed within hours, not days. A PR under a few hundred lines with a clear description, a linked specification and a fast turnaround is a genuinely valuable artifact. This is what it means to celebrate the peer review process: when a PR appears, it means something. It says “this change deserves your full attention,” and the reviewer can give exactly that, because they’re not wading through a queue of routine approvals to find it. Scarcity is what makes the signal valuable.
For everything else, review still happens — it just happens continuously. Pairing and ensemble work move review from post-hoc inspection to the moment of creation, which is both cheaper and dramatically better at transferring knowledge. The team that pairs on the hard parts and ships the routine parts through automated gates gets more review than the team drowning in PRs — they just don’t queue for it.
Measuring the work
The last goal of this activity is the one teams most often skip: reduce non-value-add time and measure the development team’s efforts so you know whether you’re succeeding.
Non-value-add time is everything draining your team’s effort: waiting for review, waiting for environments, waiting for another team’s API, reworking a story because the specification was ambiguous, hand-testing something the pipeline should have verified. Individually these leaks look small. Collectively they’re usually the majority of your lead time — I’ve made the fuller case in Your team isn’t slow. It’s leaking time.
You can’t fix a leak you can’t see, so instrument the flow:
- Wait states. Time stories spend blocked or waiting — on review, on environments, on dependencies. Your ticketing system already has this data; most teams just never look.
- Rework rate. Stories that bounce back after “done.” A rising rework rate almost always traces to thin specifications — which means the fix is upstream in 3.6 Specification and 3.7 Elaboration, not in the build.
- The DORA four. Deployment frequency, lead time for changes, change failure rate and time to restore. These are outcome measures — they tell you whether the disciplines in this chapter are actually producing flow.3
One caution, and it matters: measure the team, not the people. The moment flow metrics become individual performance metrics, your team will optimize the metric instead of the flow — and they’ll be right to. The point of measurement is to find the process leaks the team can fix together, and the numbers should be as visible to the team as they are to anyone else.
The same instrumentation serves a second customer: continuous discovery. Production telemetry — what users actually do with what you shipped — is discovery evidence flowing in without an interview. The team hears the product itself, and the hypothesis log gets its cheapest data for free.
Lessons learned
A few rules of thumb — each one earned the hard way, by teams I’ve watched skip it:
- Every merge traces to a Specification by Example source. If a change can’t point to the specification it fulfills — and the BDD artifacts that prove acceptability — it isn’t ready to merge. This single discipline keeps the value chain intact from customer desire to delivered code.
- Documentation merges with the code, every time. The first “we’ll update the diagram later” is the beginning of drift. Six months later nobody trusts the diagrams, and the team is doing archeology instead of engineering.
- Merge daily, and watch for creep. Teams under pressure revert to comfortable habits — branches grow lives of their own, PRs quietly become the default path again, Friday grows a mega-merge. The retro is the place to catch it; the wait-state metrics are the evidence.
- The gates are absolute. The first manual override of a failing gate — however justified it feels in the moment — converts your automated pipeline back into a negotiation. Fix the test or fix the code, but never route around the gate.
- A red scenario is information, not failure. Scenarios that fail during development are the process working exactly as designed: the specification caught a gap before your customer did. Celebrate that, too.
Outputs
- Production-ready features. Fully implemented, integrated and documented — quality proven at the lowest level of the code, ready to deploy through the Operations phase.
- Executable specifications. Feature files under source control, wired to the test framework, passing in CI and enforcing the behavior contract on every future change.
- Intact live links. Specifications, marbles and stories all still reference each other and the CI reconciliation check keeps them honest.
- Flow metrics. A baseline of wait states, rework and DORA measures the team can improve against, increment over increment.
In the next chapter, 3.9 Validation, we’ll close the delivery loop — confirming the increment against its acceptance criteria and key results, and proving that what we built is what the customer wanted.
Templates
- Feature specifications template — the same template used in 3.6 Specification and 3.7 Elaboration.
Next activity
Footnotes
-
Gojko Adzic, Specification by Example: How Successful Teams Deliver the Right Software (Manning, 2011). The canonical treatment of executable specifications as “living documentation.” ↩
-
Barry Boehm & Victor Basili, Software Defect Reduction Top 10 List, IEEE Computer, January 2001. Finding one: fixing a defect after delivery is often 100 times more expensive than fixing it during design. ↩
-
Google Cloud, Accelerate State of DevOps Report 2023. Elite performers meeting reliability targets are 2.3 times more likely to use trunk-based development; the DORA four metrics originate in Forsgren, Humble & Kim’s Accelerate (IT Revolution, 2018). ↩ ↩2
-
Rouan Wilsenach, Ship / Show / Ask, martinfowler.com, September 2021. ↩