Catch a regression before it ships¶
The problem¶
A new build passes its tests, and it is slower and chattier than the one before. Nobody notices until users do. You want a check that runs on a capture of a standard flow and fails when the flow got worse.
The solution¶
-
Keep a baseline. Save a capture of the flow from a build you accept. This guide uses
baseline.har. -
Capture the new build doing the same flow, and compare.
max severity: high new hosts: auth.shop.example, www.shop.example new endpoints: 4 removed endpoints: 1The result is
highbecause of the new 5xx errors further down. The new hosts and endpoints are listed, and they do not raise the severity. -
Read what the severity is made of. The lists say what, and in which endpoint:
new errors: [high] POST api.shop.example/v1/cart/sync -> 503 (5x) [medium] GET search.shop.example/v1/suggest -> 429 (4x) [medium] GET api.shop.example/v1/cart -> 401 (2x) [medium] GET api.shop.example/v1/auth/session -> 401 (1x) [medium] GET api.shop.example/v1/notifications -> 401 (1x) [medium] POST payments.shop.example/v1/charges -> 402 (1x) latency regressions: [medium] POST api.shop.example/v1/orders p50 319ms -> 2409ms [medium] GET cdn.shop.example/img/hero.webp p50 541ms -> 1921ms [medium] POST api.shop.example/v1/uploads/avatar p50 294ms -> 1104ms [medium] OPTIONS payments.shop.example/v1/charges p50 277ms -> 887ms [medium] GET api.shop.example/v1/products p50 128ms -> 558msA new
503on the cart sync, new429s from search, and an order that went from 319 ms to 2,409 ms. Each is an endpoint to open withslowestorerrors. -
Add what
comparecannot see. It misses the number of calls. Rundiagnoseon the new capture, so a burst or a heartbeat is not lost:The ranked list holds the N+1, the identical heartbeat calls and the ignored rate limit, none of which moved
compare. -
Choose the line. Decide which finding fails the build:
--fail-on highfails on a new 5xx and passes a slow endpoint.--fail-on mediumfails on both. The exit code is the gate. See Gate a build for the pipeline. -
Replace the baseline on purpose. When a slower endpoint is the price of a feature, capture again and commit the new baseline in the same change, so the review sees it.
How it works¶
compare compares medians, so one slow request does not trip it, and a change has to be consistent. It counts an endpoint's
errors as "new" only when the baseline had none for that endpoint, so a long-standing error does not fail every build.
Those two choices make the gate quiet, and the price is that it is blind to counts and tails.
Variations¶
- Two checks. Run
comparefor what got worse anddiagnosefor what is wrong now. Both exit1on findings. - Only part of the flow.
--filter "host:api.shop.example"compares one host. - A noisy endpoint. A filter keeps what matches and cannot exclude, so compare only the part you trust, for example
--filter "host:api.shop.example"on both captures, or raise the bar with--fail-on high.
Related¶
- Compare against a baseline has every rule and threshold.
- Gate a build shows the pipeline step.
- Diagnose is the other half of the check.