Skip to content

Integration Design

The edge where your system meets one you don't control. Third-party failure is not a question of if, but when. The integration design names what the system does on that day.

Owners: Tech Lead, Developer Phase it lives in: What We Build → How We Build The corpus principle this enacts: Translation lives at the boundary.

Where it lives in the chain

How to do this

For every third-party integration, the ADR names:

  1. Failure modes — what happens when the provider is down, slow, returns garbage, rate-limits, changes their schema?
  2. Retry policy — how many, with what backoff, with what jitter, idempotent-only?
  3. Fallback — degrade gracefully (cached, stale, partial) or fail loudly (block the user, raise to support)?
  4. Sandbox — where do we test against the provider's test environment? How often?
  5. Anti-corruption layer — where do we translate from the provider's model into ours?
  6. Contract pinning — which version of the provider API do we depend on? When do we re-pin?

What good practice looks like

A team integrating with a payment provider doesn't ask "what does the API return?" — they ask "what does the API return when it's struggling?" They run experiments against the provider's sandbox: trigger timeouts, trigger rate limits, trigger validation errors. The integration is designed for those days, not the sunny one.

The translation lives in one adapter (stripe.ts, provider/grading-board.ts) — not scattered through the codebase. Inside the adapter, the provider's vocabulary; outside, our domain vocabulary. When the provider deprecates a field, one file changes. When the provider doesn't deprecate gracefully, one file's failure surfaces — not a hundred grep results.

200apps · How We Work · NWIRE