skip to content

Which level should the outer acceptance loop drive from when a full run is too slow to repeat?

level: principalimportance: nice to knowfreq 26%

answer

  1. The loop needs a runtime budget
  2. Outermost level that still returns fast
  3. Speed is bought with wiring coverage
  4. Driving and confidence can live at different levels

basics

~20 s

Drive from the outermost level that still returns inside a development cycle — usually the service boundary just inside the delivered interface. Keep a small set of the same journeys running through the delivered interface on a slower cadence for wiring confidence.

solid answer

~50 s

The outer loop is only a driver if re-running it is not a decision, so it needs a runtime budget of seconds to a couple of minutes. Three levels are available: through the delivered interface (highest confidence, slowest, most fragile), at the service boundary just inside it with real components wired behind (usually the best default — real persistence and composition, none of the client-driving cost), or at the domain boundary with the edges substituted (fastest, but it stops checking composition). Every step inward buys speed by transferring risk, so name what you stopped checking. The strongest answer splits the loop's two jobs: drive each slice from the service boundary where it runs in seconds, and keep a handful of the same journeys at the delivered interface as confidence rather than as a driver. That ratio is a decision to defend, not a default.

go deeper

for a junior

Know that an acceptance scenario can be driven at more than one level, and that the further out it runs the more realistic and the slower it is. You are not expected to make the choice yourself yet.

for a middle

Be able to compare driving through the delivered interface against calling the application's own entry point with real components behind it, and say which defects each one can and cannot catch.

for a senior

Show judgement about the runtime budget: what happens to the workflow when the loop stops being re-run each cycle, and how you would recover speed without pretending the lost coverage never mattered.

for a principal

Own it as a standing commitment about where confidence comes from: the level slices are driven at, the small set of journeys kept at the delivered interface, who maintains that set, and what a green run is allowed to mean.

### The outer loop has a runtime budget The outer loop is only a driver if you re-run it while you work. If a run takes seventeen minutes, you will not re-run it after each inner cycle; you will run it in the morning and at the end of the day, and it becomes a late report rather than a feedback loop. The practical budget is "fast enough that re-running it is not a decision" — on the order of seconds, at worst a couple of minutes. Choosing the level the outer loop drives from is mostly choosing how to stay inside that budget without giving away the confidence the loop exists to provide. ### The three candidate levels **Through the delivered interface.** The scenario drives the system the way a person or a calling system would. Highest confidence: it exercises real wiring, real configuration, real serialization. Also the slowest and the most fragile, because it is sensitive to everything, including things the slice has nothing to do with. **At the service boundary just inside the delivered interface.** The scenario calls the application's own entry point — the request handler or command interface — with the real components wired behind it. Usually the best default: real persistence, real internal composition, real error paths, but no rendering, no driving of a client, and far fewer moving parts to go wrong for unrelated reasons. On the transcoding queue, driving at this level moved the outer loop from around seventeen minutes to roughly forty seconds per run. **At the domain boundary with the edges substituted.** The scenario exercises the domain with storage and external calls replaced. Fast and stable, but it stops checking composition — and composition is where a large share of integration defects live. ### The tradeoff you are actually making Each step inward buys speed with coverage of the wiring. That is a real, permanent transfer of risk, not a free optimisation, and the principal-level answer names where the risk went and what now catches it. If the outer loop no longer drives the delivered interface, then serialization, routing, configuration and ordering between components are unchecked by it — and something else has to check them, or you have quietly decided not to. ### Splitting the loop's two jobs The outer loop does two things: it **drives** the slice, and it **provides confidence** that the composed system works. These do not have to happen at the same level, and separating them is usually the strongest available answer: * Drive every slice from the service boundary, where the loop is fast enough to re-run constantly. * Keep a small, deliberately chosen set of the same journeys running through the delivered interface on a slower cadence, as confidence rather than as a driver. That gives a development loop measured in seconds and a wiring check that still exists, at the cost of a second place where the journey is described and a second thing to maintain. Say the transcoding queue keeps four journeys at the delivered interface against forty-one scenarios at the service boundary — that ratio is a decision to defend, not a default. ### A failure worth naming A team that substituted the queue at the domain boundary had every scenario green for a full three-week release train, and shipped a defect where two workers completed renditions out of the order the storage layer assumed. Nothing in the fast loop could have caught it, because the ordering only exists once the real queue and the real storage are composed. The lesson is not "always drive from the outside"; it is that when you move the loop inward you must name the class of defect you just stopped checking and decide, explicitly, what covers it instead. ### How to decide Ask, in order: what is the fastest level that still fails when the slice is genuinely broken? What classes of defect does driving at that level stop seeing? Is there a cheap, separate check for those, and is anyone accountable for it? What does a new team member believe when this loop is green — and is that belief true? A principal owns the answer because it is a standing architectural commitment about where confidence comes from, not a per-slice preference, and because the cost of getting it wrong shows up as either a development loop nobody runs or a green suite nobody should trust.

  • What do you lose by driving the outer loop below the delivered interface?
    Everything the interface layer contributes: serialization, routing, configuration, authentication wiring, and ordering between real components. Those are exactly the defects that only appear once the pieces are composed for real. Moving inward is legitimate, but it is a transfer of risk that must be replaced by a smaller, explicitly owned check rather than quietly dropped.
  • How many journeys would you keep at the delivered interface, and how do you choose them?
    As few as will still fail if the wiring is broken — often a handful. Choose the journeys that cross the most component boundaries and the ones whose failure would be most costly, not the ones that are easiest to automate. Review the set when the architecture changes; an unreviewed set grows into a slow regression pack nobody trusts.
  • How do you know your runtime budget has been exceeded before anyone complains?
    Watch behaviour, not the clock: if people run the outer loop once a morning instead of after each inner cycle, it has already stopped being a driver. Recording per-run duration over a release train shows the drift, but the reliable indicator is that re-running it has become a decision someone weighs.

It is the difference between a rehearsal you can run every hour and a dress rehearsal you can run twice: you need both, but only one of them can be your working loop.

saying these in an interview costs you the question

  • Insists the outer loop must always drive the delivered interface
  • Moves the loop inward without naming what stops being checked
  • Treats a slow outer loop as merely an infrastructure problem
  • Assumes green at the domain boundary proves the system composes
  • Keeps every journey at the delivered interface for confidence
  • Has no runtime budget for the loop at all

context