A retry storm at 02:14
Consider a familiar pattern. A nightly cron worker calls a shared model endpoint for classification. The endpoint throttles under load, the worker retries, and the loop outlives its window. Nobody owns the endpoint. Nobody wrote a stopping condition.
Adoption checklists ask one question: can this run here? The stronger question is what disqualifies this workload. That flips the default. Teams then need a catalog of refusals instead of a list of features.
This post builds that catalog. It ships a small linter that runs in CI. Nothing below depends on a single vendor.
The five fields of an anti-use entry
Each refusal must be arguable and testable. Five fields do that.
-
id— a stable key, quoted in reviews and PRs. -
when— machine-checkable tags and workload fields. -
why— the failure mode, not the policy wording. -
alternative— where the workload belongs instead. -
exit_signal— the observation that proves the refusal was right.
An entry without alternative is a complaint. An entry without exit_signal is a guess.
Four entries that earn a block
AU-01: anything on a signed request path
Webhooks, payment callbacks, and ledger writes share a property. Their receiver owns the timeline, not the caller. A throttled free lane inserts delay into a signed request. Idempotency keys and signature windows do not negotiate.
- Alternative: a pinned runtime with a named on-call owner.
- Exit signal: one signature failure or duplicate delivery in 24 hours.
AU-02: bulk backfills and migrations
A backfill turns quota into a wall-clock dependency. Partial progress is hard to resume. Partial progress is harder to explain to whoever reruns it.
- Alternative: a queue with a fixed concurrency budget, or a batch job with a paid contract.
- Exit signal: the job window exceeds the retry budget on two consecutive runs.
AU-03: customer-facing latency budgets
A free lane can change limits or capacity without notifying the caller. That is a legitimate property of shared infrastructure. It is also incompatible with a p95 promise made to a customer.
- Alternative: a reserved runtime, or a queue that absorbs variance.
-
Exit signal: upstream
429responses inside any customer-visible window.
AU-04: regulated data classes
Data class is not a performance question. A summarizer for public docs and a summarizer for health records share code and share nothing else. The workload field decides, not the model quality.
- Alternative: an environment approved for that data class, with an audit trail.
- Exit signal: any request that crosses a data-class boundary.
The artifact: a catalog plus a linter
First, the catalog. Keep it in the repo next to the workloads it judges.
# antiuse.yaml
version: 1
entries:
- id: AU-01
name: signed-request-path
severity: block
when:
any_tags: [webhook, payment, signature, ledger]
fields:
stage: production
why: >
Retries and throttling shift the delivery timeline of a signed request.
The receiver owns the signature window, not the lane.
alternative: pinned runtime with a named on-call owner
exit_signal: one signature failure or duplicate delivery in 24h
- id: AU-02
name: bulk-backfill
severity: block
when:
any_tags: [backfill, bulk, migration]
why: >
A backfill converts quota into a wall-clock dependency with weak resume points.
alternative: fixed-concurrency queue or a contracted batch job
exit_signal: job window exceeds retry budget twice in a row
- id: AU-03
name: customer-facing-latency
severity: warn
when:
fields:
p95_budget_ms: 800
why: >
Shared capacity can change without notice; a p95 promise cannot.
alternative: reserved runtime or an absorbing queue
exit_signal: upstream 429 inside any customer-visible window
- id: AU-04
name: regulated-data
severity: block
when:
fields:
data_class: regulated
why: >
Data class is a boundary question, not a model-quality question.
alternative: environment approved for that data class
exit_signal: any request crossing a data-class boundary
Next, a workload manifest. Three lines of metadata do most of the work.
# workloads/checkout.yaml
name: checkout-callback
owner: payments-team
stage: production
data_class: regulated
p95_budget_ms: 800
tags: [webhook, signature, payment]
Now the linter. It reads two files and exits non-zero when a block-level rule matches.
#!/usr/bin/env python3
"""fieldguide.py - refuse a workload before it reaches a free-tier lane."""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
import yaml
SEVERITY = {"warn": 1, "block": 2}
def load_yaml(path: Path) -> dict:
with path.open(encoding="utf-8") as handle:
return yaml.safe_load(handle) or {}
def matches(rule: dict, workload: dict) -> list[str]:
reasons, tags = [], set(workload.get("tags") or [])
for tag in rule.get("any_tags") or []:
if tag in tags:
reasons.append(f"tag:{tag}")
for field, expected in (rule.get("fields") or {}).items():
if workload.get(field) == expected:
reasons.append(f"{field}={expected}")
return reasons
def evaluate(catalog: dict, workload: dict) -> dict:
hits = []
for entry in catalog.get("entries", []):
reasons = matches(entry.get("when") or {}, workload)
if not reasons:
continue
hits.append(
{
"id": entry["id"],
"name": entry["name"],
"severity": entry.get("severity", "warn"),
"matched": reasons,
"alternative": entry.get("alternative", ""),
"exit_signal": entry.get("exit_signal", ""),
}
)
verdict = "pass"
if any(hit["severity"] == "block" for hit in hits):
verdict = "block"
elif hits:
verdict = "warn"
return {"workload": workload.get("name", "<unnamed>"), "verdict": verdict, "hits": hits}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--catalog", type=Path, default=Path("antiuse.yaml"))
parser.add_argument("--workload", type=Path, required=True)
parser.add_argument("--fail-on", choices=["warn", "block"], default="block")
parser.add_argument("--json", action="store_true")
args = parser.parse_args(argv)
result = evaluate(load_yaml(args.catalog), load_yaml(args.workload))
if args.json:
print(yaml.safe_dump(result, sort_keys=False))
else:
print(f"{result['workload']}: {result['verdict'].upper()}")
for hit in result["hits"]:
print(f" {hit['id']} {hit['name']} ({hit['severity']}) via {', '.join(hit['matched'])}")
print(f" instead: {hit['alternative']}")
print(f" exit signal: {hit['exit_signal']}")
worst = max((SEVERITY[h["severity"]] for h in result["hits"]), default=0)
return 1 if worst >= SEVERITY[args.fail_on] else 0
if __name__ == "__main__":
sys.exit(main())
Run it against the checkout manifest. The output is short and blunt.
$ python fieldguide.py --workload workloads/checkout.yaml
checkout-callback: BLOCK
AU-01 signed-request-path (block) via tag:webhook, tag:signature, tag:payment
instead: pinned runtime with a named on-call owner
exit signal: one signature failure or duplicate delivery in 24h
AU-04 regulated-data (block) via data_class=regulated
instead: environment approved for that data class
exit signal: any request crossing a data-class boundary
$ echo $?
1
Two tests pin the behavior. They run without touching the catalog file.
from fieldguide import evaluate
CATALOG = {
"entries": [
{
"id": "AU-04",
"name": "regulated-data",
"severity": "block",
"when": {"fields": {"data_class": "regulated"}},
"alternative": "approved environment",
"exit_signal": "data-class crossing",
}
]
}
def test_regulated_workload_blocks():
result = evaluate(CATALOG, {"name": "checkout-callback", "data_class": "regulated"})
assert result["verdict"] == "block"
def test_internal_tool_passes():
result = evaluate(CATALOG, {"name": "docs-summarizer", "data_class": "public"})
assert result["verdict"] == "pass"
Wire it into CI. A blocked workload fails the pull request, not the incident channel.
# .github/workflows/fieldguide.yml
name: fieldguide
on: [pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pip install pyyaml
- run: python fieldguide.py --workload workloads/checkout.yaml --fail-on block
Where a free tier does earn its place
Refusal is not the whole story. Low-stakes lanes are the right home for shared infrastructure. Internal tools, demo servers, prototype spikes, and test fixtures qualify. The cost of a bad response stays inside the team that asked for it.
MonkeyCode is one option in that space. Its project offers free model access and a free server option, per the operator's description. Quotas and terms change; read the project page instead of a blog post, this one included.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
When a lane is adopted, write the exit signal first. The catalog above still applies. Internal tools can graduate into customer paths without anyone editing a document.
What the linter cannot do
Be honest about the ceiling.
- Tag matching is coarse. A mislabeled manifest defeats every rule.
- Data class is declared by a human, not discovered by the tool.
- The script judges intent, not runtime behavior. It is not a security control.
- A passing verdict is the start of a review, not the end of one.
Every block needs a human sign-off with a name attached. A file that nobody owns will drift within a quarter.
Who should skip this approach
Some teams should not adopt the catalog yet.
- Teams handling regulated data without an approved environment for it.
- Teams without a named owner per workload.
- Teams already running more CI jobs than they can maintain.
- Teams that want a scoring model instead of a decision.
Those teams need an inventory first. A refusal list without owners is decoration.
The point of writing refusals down
The catalog costs an afternoon and saves arguments later. It records why a workload stays off a shared lane. It gives reviewers a link instead of an opinion. If one lane survives the catalog, run it there for a week and record what broke.
Top comments (0)