Use Cases
Each of these is a situation the tools were built for, with the smallest config that handles it. The case studies walk through three of them in full, with real commands and real output.
A field was renamed
The service now answers "amount" where your app reads "total". One AfterPatch rule puts the old name back:
rules:
- id: total-renamed
when: {path: /orders/*, status: 200, content_type: application/json}
response:
json:
- rename: {from: amount, to: total}
The key keeps its place in the object, and every other byte of the answer stays as it was.
The service now wants a header
A new version answers 400 unless the request carries a version header, and the old client doesn’t send one:
rules:
- id: send-api-version
when: {path: /api/**}
request:
set_headers: {X-Api-Version: "2026-10"}
An endpoint moved
/v1/rate is gone and the same thing now lives at /v2/rate. The query string goes along exactly as the client wrote it:
rules:
- id: rate-moved
when: {method: GET, path: /v1/rate}
request:
rewrite_path: {from: "^/v1/rate$", to: "/v2/rate"}
The exchange rate case study combines this with the two cases above.
Something leaks that shouldn’t
An upstream started sending a debug header and an internal field to every client:
rules:
- id: strip-internals
when: {content_type: application/json}
response:
remove_headers: [X-Debug-Trace]
json:
- delete: {path: internal}
A whole API version was retired
When the paths, the body shapes and the status codes all changed, AfterAPI is easier to read than a stack of patch rules. You write each old endpoint in terms of the new one:
endpoints:
- id: get-customer
old: GET /api/v1/customer?id={id}
new: GET /api/v2/customers/{id}
response:
body:
customer_id: $.customer.id
name: $.customer.full_name
error:
message: $.error.message
The status codes changed
The new API answers 201 to a create and 204 to a delete, where the old client expects 200 both times. AfterAPI maps the codes and can add the body the old client looks for:
endpoints:
- id: delete-customer
old: DELETE /api/v1/customer?id={id}
new: DELETE /api/v2/customers/{id}
response:
status: {204: 200}
body: {deleted: true}
The customer API case study moves a whole client this way.
Try a fix on live traffic first
Every run command takes --dry-run. Traffic passes through untouched, and the audit log says what each rule would have changed:
aftermarket patch run --config rules.yaml --upstream http://127.0.0.1:9000 --dry-run
Check a new release before it ships
Record what the current version answers, then send the same requests to both versions:
aftermarket record --upstream http://old:9000 --out traffic.yaml
aftermarket replay --fixture traffic.yaml --a http://old:9000 --b http://new:9000 --ignore created_at
Every difference is listed by field. Key order and number formatting don’t count, and only GET and HEAD requests are sent unless you add --include-writes. The release check case study finds a renamed field this way and proves the repair.
Turn real traffic into regression tests
A recording is an ordinary fixture file, with credentials saved as REDACTED. Run it against your rules in CI, and the build fails if a rule starts changing answers it shouldn’t:
aftermarket patch test --config rules.yaml --fixture traffic.yaml
Compare a service with what it used to answer
Without --b, replay compares one service with the answers in the recording. That fits a migration, a dependency upgrade or a config change, where the before picture is only available as a recording:
aftermarket replay --fixture traffic.yaml --a http://127.0.0.1:9000 --ignore created_at
The full formats are in the AfterPatch, AfterAPI and AfterTest and AfterDiff references.