Interfaces: where systems break
Components fail loudly and get fixed. Interfaces fail quietly, in the gap between two teams who each think the other one owns it.
- You can write an interface contract that survives the first real failure.
- You can find the unowned coupling in a system before it finds you.
An interface is everything that crosses a boundary: a function call, an HTTP endpoint, a message on a queue, a file drop, a verbal handover at a shift change. All of them have a contract, even when nobody wrote it — in which case the contract is "whatever the two sides happen to implement today".
A useful contract describes five things, and the fifth is the one people skip:
4.1The same endpoint, two contracts
POST /score
{ "applicant": "A-1234" }
200 OK
{ "score": 720 }
What's missing: what a score means, its range, what happens on a missing applicant, how long the caller should wait, whether retries are safe, and who may change this. Three teams will implement three different guesses.
POST /v2/score
Idempotency-Key: <uuid>
{ "applicant_id": "A-1234", "as_of": "2026-03-11" }
200 OK
{ "score": 720, "range": [300, 850], "model": "risk-v7",
"as_of": "2026-03-11", "expires_in": 3600 }
202 Accepted # still computing; poll or callback
{ "job_id": "j-88f1", "retry_after": 2 }
404 Not Found
{ "error": "unknown_applicant", "detail": "A-1234 not in bureau" }
422 Unprocessable
{ "error": "insufficient_history", "min_months": 6 }
503 Service Unavailable
Retry-After: 30 # retry with the same Idempotency-Key
Now the caller knows: units, freshness, that retries are safe, what each failure means, and how to back off. The consumer can be built without a meeting.
Some of the most consequential interfaces have no API at all: the shift handover between an operator and their replacement, the support agent escalating to engineering, the account manager promising a date. They fail the same way — one side assumes the other received context that was never actually transferred. Writing these down, with the same five headings, is unfashionable and effective: what is handed over, when, in what form, what happens if it is missed, and who owns the wall between the two teams.
4.2Versioning without breaking people
| Change | Safe? | What to do |
|---|---|---|
| Add an optional field | Yes | Ship it; tell consumers to ignore unknown fields |
| Add a required field | No | New version, or make it optional with a default first |
| Rename a field | No | Dual-write both names for a deprecation window |
| Change units or meaning | No — worst kind | New field name; silent semantic drift is how dashboards lie for months |
| Tighten a timeout or limit | Risky | Measure consumers first; announce, then change |
Two services reading the same tables is not an interface — it is an undeclared coupling with no owner on either side. It works until someone adds a column, changes a type, or runs a migration at the wrong hour. If it must exist, treat the schema as a published contract with migrations and a deprecation policy.
4.3Four interface failures that actually happen
| Failure | How it presents | What fixes it |
|---|---|---|
| Silent unit change — milliseconds become seconds | Timeouts that are 1000× too long, or a dashboard showing 40 years of latency | Units in the field name (timeout_ms), and an example in the contract |
| Retry without idempotency | Duplicate orders, double emails, a customer charged twice after one timeout | An idempotency key per intended action, honoured by the receiver |
| Shared schema, two owners | One service's migration breaks another at an unrelated hour | Publish the schema as a contract, or stop sharing it |
| Timeout shorter than the dependency's p99 | Intermittent failures under load that vanish in testing | Measure the dependency's tail latency, then set the deadline above it |
The common thread is that all four are invisible in a happy-path test and obvious in a contract review. That is the argument for writing the failure semantics down: the review costs an hour, and each of these costs a weekend.
You're reviewing an API contract. Which omission is most likely to cause a production incident?
60-second recap
- An interface is a contract: content, timing, failure semantics, ownership, versioning.
- The N² matrix finds unowned coupling in ten minutes; do it before the incident.
- Errors are the interface. Retry safety and idempotency belong in the spec, not in folklore.
- Additive changes are safe; semantic changes are not. Version anything that renames or redefines.