skip to content

Across a Gatling suite that gates CI, would you write the response-time budget as a `global` assertion, a `forAll` assertion, or one `details(...)` assertion per critical request, and how would you decide?

level: principalimportance: should knowfreq 38%

answer

  1. They layer; assertions takes any number
  2. global pools and hides a low-volume request
  3. forAll binds probes and warm-up calls too
  4. details couples the gate to request names
  5. A rename fails the run, never skips

basics

~20 s

Usually all three, layered: details rules on the journeys that matter, a loose forAll safety net, and a global volume floor. global alone aggregates away the request you care about; forAll alone binds every request to one budget; details alone breaks on renames.

solid answer

~50 s

They are not alternatives so much as layers, and `assertions(...)` ANDs as many rules as you pass it. `global()` pools every request, so a low-volume request can degrade badly without moving it - cheap, never stale, poor at catching what you want. `forAll()` gates every request type automatically, which is real coverage, but it binds health probes and warm-up calls to the same budget and the grammar offers no exclusions, so the budget ends up set by your slowest legitimate request. `details(path)` is precise and names the offender in its failure message, at the cost of coupling to request names: a rename makes the path unresolvable, which **fails** the run rather than silently dropping the rule. A few `details` rules, one loose `forAll` net and a `global` volume floor is the shape that usually survives.

go deeper

for a junior

Be ready to say that setUp accepts many assertions at once and that they are ANDed, so the three scopes can be combined rather than chosen between.

for a middle

Be ready to explain what each scope aggregates over and why an unresolvable details path fails the run rather than being skipped.

for a senior

Be ready to propose a layered gate and to defend the maintenance cost of details paths against the coverage that forAll gives for free.

for a principal

Be ready to state a position on request names as a stable interface, and on what a team does when a gate goes red for a rename rather than a regression.

## The three shapes, and what each one buys Gatling gives exactly three assertion scopes, and choosing between them for a CI gate is a real design decision rather than a preference. They differ in coverage, in maintenance cost and in what a failure tells you: | shape | coverage | what a failure tells you | maintenance | |---|---|---|---| | `global()` | one rule over all requests pooled | that the run as a whole moved | none | | `forAll()` | one rule, evaluated per request type | which request types breached | none, but it binds every request | | `details(path)` per request | one rule per request you name | exactly which named request moved | breaks when a request is renamed | ## What each one actually costs **`global()`** is one rule and one verdict, and it aggregates across request types. That aggregation is precisely its weakness as a gate: a pooled statistic is dominated by the requests that produced the most traffic, so a rule on it can stay comfortably green while a low-volume request degrades badly. It is cheap, it never goes stale, and it is a poor instrument for catching the regression you most want to catch. **`forAll()`** is the coverage answer with no maintenance: it expands into one evaluation per request type and holds only if every one passes, so a new request added to a scenario is gated the day it appears. The cost is that it binds **every** request to the same budget — the login call, a health probe, a static asset fetch, a warm-up request. Any single one of them can turn the pipeline red for reasons that have nothing to do with the service's behaviour, and there is no exclusion mechanism in the grammar. A `forAll` budget is therefore necessarily set by your slowest legitimate request, which usually means setting it loose enough that it stops discriminating. **`details(path)` per critical request** is the precise option, and the only one whose failure message names the thing that moved without further investigation. Its cost is coupling: the path is an exact match against the request's full name, group prefix included, and there is no exclusion or wildcard. Rename the request in the scenario and the assertion no longer resolves — which, in Gatling, is a **failed** assertion rather than a skipped one. That behaviour is the right default, but it means a `details` gate is a maintenance obligation, not a write-once artefact. ## How the decision usually resolves There is no single right answer, but there is a shape that most teams converge on, and it exploits the fact that `assertions(...)` takes any number of rules and ANDs them: 1. **A small set of `details(...)` rules on the journeys that carry the business**, each with its own budget. These are the rules people read when the pipeline goes red, and they are worth the maintenance. 2. **A loose `forAll()` rule as a safety net** — a failure-rate or a gross latency ceiling that no healthy request should ever approach. Its job is not to discriminate but to catch a request that nobody wrote a specific rule for and that has broken badly. 3. **A `global()` volume floor** on `allRequests().count()`, so a run that produced almost no traffic cannot report a pass simply by having nothing to fail. That layering gives precise diagnosis where you invested, coverage where you did not, and a guard against the empty run. It is more rules than a single gate, but each one is cheap and every one prints its own verdict line. ## The judgement calls that remain Three things are genuinely contested and worth having a position on: - **How much coupling to accept.** Every `details` path is a dependency on a request name. Either treat request names as a stable interface that reviews defend, or accept that gates will go red on renames and budget for the churn. Both are defensible; drifting into the second without saying so is not. - **Whether a broken gate should be loud.** An unresolvable `details` path fails, so a rename produces a red run rather than silent loss of coverage. Teams under delivery pressure are tempted to remove the rule; removing it is exactly the outcome the failure exists to prevent. - **Where the rules live.** Assembling assertions from a shared base class is the shape that most often loses them, because a second `assertions(...)` call replaces rather than appends in the Java API. Prefer a single call fed from a list. What the budget numbers themselves should be is a separate question with its own discipline, and it is not answered by the choice of scope.

  • Why can a global response-time assertion stay green while one endpoint degrades badly?
    Because a global statistic is pooled across every request, so it is dominated by whichever requests produced the most traffic. A low-volume request can degrade severely and barely move the pooled figure. The grammar offers no weighting - if you want that request judged, you have to name it with `details(...)` or bring it under `forAll()`.
  • What is the argument against removing a details assertion that keeps failing after a rename?
    That the failure is the rule working. An unresolvable path fails rather than being skipped precisely so that losing coverage is visible. Deleting the rule converts a loud, one-line fix into permanently missing coverage that nobody will notice again. Update the path instead, and treat request names as an interface the suite depends on.
  • Why is a forAll budget usually looser than you would like?
    Because it binds every named request, including health probes, warm-up calls and asset fetches, and the grammar has no way to exclude one. The budget therefore has to accommodate your slowest legitimate request, which is often far slower than anything you actually care about. That is why forAll works as a safety net rather than as the primary gate.

saying these in an interview costs you the question

  • Treating the three scopes as mutually exclusive choices
  • Assuming a global rule will catch a single slow endpoint
  • Believing forAll can exclude a health probe or warm-up call
  • Deleting a details rule that fails after a request rename