Skip to content

Why the wire contract is a file, not a convention

Engineering · Aug 4, 2026 · Dana Reyes

Abstract illustration of layered data schemas

Most data APIs treat their response shape as a convention. It lives in the docs, it lives in a TypeScript type someone hand-maintains, and it lives — really lives — in the code that produces it. Three copies, none authoritative.

That arrangement works until someone renames a field. Then you find out from a consumer, usually at the worst possible moment, and usually by way of a null that used to be a string.

The contract as a file

At BatScrape the wire contract is a package. It has a version, it is vendored into every actor repository, and nothing ships without it.

packages/contract/          source of truth for the wire
vectors/wire-v1.json        golden signature and schema vectors, append-only
scripts/verify-vectors.mjs  the gate

The vectors file is append-only, which is the constraint that makes the rest work. You cannot quietly change what v1 means; you can only add v2. A rename is now a diff someone reviews, not a discovery someone makes.

Where the gate runs

This is the part that took us a second attempt to get right.

The obvious place for a contract check is CI. We tried that, and measured it three times on the previous project: the platform build finished 80 to 83 seconds before CI had an opinion. A red tick sat on a commit whose artefact was already tagged and pullable.

So the gate moved inside the Docker build, between install and build:

COPY packages/contract ./packages/contract
COPY vectors ./vectors
RUN node scripts/verify-vectors.mjs
RUN pnpm run build

If the wire moved, the image does not exist. There is no race to lose.

What it costs

Two seconds per build, and the discipline of writing a vector before writing a field. That is genuinely all. The thing it buys is that “the API changed” stops being a category of incident.

What it does not solve

A contract cannot tell you the source was lying. If a site starts returning prices in a different currency without changing the field, the wire is unmoved and the data is wrong. That is a different problem, and it belongs to the actor, not the contract.

Back to blog

Ready to stop babysitting scrapers?

When no scraper exists for your source, BatScrape ships one.

Get API keys
  • One contract for every source
  • Versioned wire, no surprises