Why a 200 OK response can still be broken

Contract drift does not announce itself with an error code. Here is how APIs break while every status check keeps passing, and what to assert instead.

Updated

The outages that hurt are rarely the ones where the API returns 500. Those page someone immediately. The expensive failures are the ones where every endpoint returns 200 OK and the data underneath quietly changed shape.

How this happens

Nobody sets out to break a contract. It happens as a side effect:

  • A serializer gains a field, and an ORM change makes an existing one nullable.
  • An ID grows past what a 32-bit integer holds and is returned as a string "to be safe".
  • An empty relation starts returning null instead of [] after a library upgrade.
  • A timestamp switches from Unix seconds to ISO 8601 during a refactor.

Every one of those ships a 200. Every status-code assertion passes. The consumer crashes at runtime, usually somewhere far from the actual cause.

Why tests miss it

Most API tests assert two things: the status code, and one or two fields the author happened to care about.

assert response.status_code == 200
assert response.json()["id"] == 42

That test passes when email disappears entirely. It passes when teams becomes null. It is checking that the endpoint responded, not that the response is usable.

Assert the shape, not a sample

The fix is to state the whole expected structure once and check responses against it: which fields must be present, what type each is, and — the two that catch the most real bugs — whether a field is required and whether it is nullable.

"Sometimes missing" and "sometimes null" are different failures and both need to be explicit. A schema forces you to decide, which is most of the value even before it catches anything.

Where to point it

  • Third-party APIs. You do not control their release schedule and will not get a changelog entry for a nullability change.
  • Your own API, before release. Validate the live endpoint against its own OpenAPI spec. The two disagreeing is common enough to be worth checking every time.
  • Webhook payloads. See why your webhook works in production but not locally.

The practical walkthrough is in validating API responses against a schema.

Frequently asked questions

Is schema validation the same as contract testing?

Related but not identical. Schema validation checks that one response matches an expected shape. Contract testing is the broader practice of both sides agreeing on that shape and verifying it in CI, so the provider learns it broke a consumer before deploying.