Quick answer: Reading the changed code can make a change look correct, and only a real request confirms it. Build a concrete call that exercises the changed path, send it against the live endpoint, and assert the status, the body against the contract, and the side effect in the data. Keep the request and the raw response so the check is reproducible. A reviewer who merges on a strong hunch is trusting a prediction about code the review never ran.
Why the hunch feels convincing
When the diff is small and the logic looks clean, a reviewer can simulate the execution in their head and arrive at a confident answer, which is a real skill for trivial changes. It breaks down as soon as the path depends on real data, a guard elsewhere, or a shared helper, because the mental model cannot see the values the endpoint actually receives.
The hunch is also unrecorded. It cannot be rerun, handed to a teammate, or compared against the real output, so a wrong prediction leaves no evidence to catch it. A real request answers the same question with an observable result.
What a real request checks
| Layer | Assertion | Failure signal |
|---|---|---|
| Transport | The expected status code | An error or unexpected code |
| Contract | The body matches the schema and types | A missing or wrong field |
| Business rule | The value follows the rule | A 200 with wrong data |
| Side effect | The expected row or state changes | No write or an extra deletion |
| Idempotency | Repeating the call stays safe | A duplicate effect |
The side effect row is the one a hunch most often misses, because a response can look correct while the storage changed in the wrong way.
Building the verification
- Identify the exact behavior the change alters and the trigger that reaches it.
- Build a request with the concrete input that fires the trigger, including any auth and headers.
- Send it and assert the status, then the body against the contract.
- Check the side effect in the data, not just the returned payload.
- Repeat the call where idempotency matters and confirm no duplicate effect.
- Store the request and the raw response so the check can be rerun.
Take a change that alters how archived notes are handled on deletion. The hunch says the new filter is safe. The request sends a selection change with archived notes present in the visible list. The status is 200 and the body looks normal, but the side effect check shows an archived row was removed, contradicting the hunch. Adjusting the request to exclude that path shows the correct behavior, and the stored request now reproduces either case.
When a 200 is not enough
A success status only says the endpoint accepted the call, and it does not establish that the returned data or the side effect is correct. Assert the body and the storage change separately, because a 200 that breaks the contract or deletes the wrong row is a failed verification dressed up as success.
The evidence is classified by type, and an AI-only claim stays a hypothesis until a real check backs it. The deterministic verdict passes only fully verified evidence, with no LLM involved, and an empty result is not a pass.
Powerduck connects the changed code to the request workflow, so the reviewer sends a confirming call and keeps the evidence alongside the diff.
Verifying the error paths too
The happy path is the easy confirmation, and many defects live on the error branches. Send a request that triggers a validation failure or a missing resource, and assert the error status and the body, since a change that handles the success case can mishandle the failure and leak the wrong detail or leave a partial write. Check that no side effect lands when the call is rejected, because an error response that still removed a row is a defect the happy-path check cannot see. These calls are as reproducible as the success one, and they cover the branches the normal request never reaches. A change is not verified until both the accepted and the rejected paths have evidence.
Honest limits
A real request confirms the specific paths it exercises, and it does not cover every edge case, so keep the untested cases visible rather than assuming one call proves the endpoint. Dynamic loading and external consumers can also remain outside the check.
When the request cannot reach the changed branch, treat the result as inconclusive and adjust the input rather than counting the attempt as a pass.
Frequently asked questions
Is a mental simulation ever enough? For trivial, isolated changes it can be, but any path involving real data or shared code needs a real request.
Why check the side effect? A 200 response does not show the storage change, and the wrong row can be deleted behind a normal body.
What makes the check reproducible? Storing the concrete request and the raw response so the same call can be rerun later.
The bottom line
Do not merge an API change on a hunch, however clean the diff looks. Send a real request that fires the trigger, assert the status, the body, and the side effect, and keep the call reproducible. A 200 alone is not enough, and a check that never reaches the branch stays inconclusive. The observable result is the confirmation, and the prediction is only where you start. Read more in the API scenario testing guide.
