Module 045 min

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.

After this module
  • 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:

1 · Content
What is exchanged, in what shape. Field names, types, units, nullability, required vs optional.
2 · Timing
How often, how soon, in what order. Synchronous or asynchronous, at-least-once or exactly-once, with what deadline.
3 · Failure semantics
What happens when it goes wrong: timeout, retry, partial success, poison message, backpressure. An interface without error behavior is half a contract.
4 · Ownership
Who is accountable for the contract, who may change it, and how consumers hear about the change.
5 · Versioning
How it evolves without breaking callers. Additive changes are safe; renames and semantic changes are not.
N² INTERFACE MATRIX · 4 ELEMENTS Gateway Auth Scoring Log Gateway Auth Scoring Log — — — — sync async row sends to column · blank = no contract COUPLING sync waits async queues blank none 3 sync · 1 async
Draw the matrix once and the argument ends. Every dot is a contract that needs an owner — and the blank cells are where someone will eventually say "I assumed you handled that".

4.1The same endpoint, two contracts

Try it · contract reviewhappy path vs reality
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.

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

ChangeSafe?What to do
Add an optional fieldYesShip it; tell consumers to ignore unknown fields
Add a required fieldNoNew version, or make it optional with a default first
Rename a fieldNoDual-write both names for a deprecation window
Change units or meaningNo — worst kindNew field name; silent semantic drift is how dashboards lie for months
Tighten a timeout or limitRiskyMeasure consumers first; announce, then change
The trap · shared database

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

Each of these is a contract bug, not a code bug. None of them throw an exception.
FailureHow it presentsWhat fixes it
Silent unit change — milliseconds become secondsTimeouts that are 1000× too long, or a dashboard showing 40 years of latencyUnits in the field name (timeout_ms), and an example in the contract
Retry without idempotencyDuplicate orders, double emails, a customer charged twice after one timeoutAn idempotency key per intended action, honoured by the receiver
Shared schema, two ownersOne service's migration breaks another at an unrelated hourPublish the schema as a contract, or stop sharing it
Timeout shorter than the dependency's p99Intermittent failures under load that vanish in testingMeasure 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.

Check yourselfQuestion 1 of 1

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.