Skip to content

2.3 APIs and interface design

Overview and motivation

An API (application programming interface) is the contract through which one piece of software offers capability to another. It is where teams, systems, and organizations meet, and it is the most durable and expensive thing to get wrong. You can refactor an internal function signature freely. A published API is different: it is a promise to consumers you may never meet, and breaking it breaks them. As organizations split monoliths into services and open up capabilities to partners and the public, the API becomes the main product surface and the main integration risk.

For large teams, APIs are what let people work independently. A well-designed interface lets you change your internals without coordinating with every consumer, which is the whole point of a service boundary. A poorly designed one leaks internal detail, forces lockstep deployment, and turns a set of services into a distributed monolith: services split apart yet so coupled they must be built and deployed together. Your API design directly determines how independently your teams can move.

In enterprise and government settings, APIs also carry compliance, security, and longevity obligations. A public-sector API may be required to follow open standards, stay stable for years, and serve external developers you cannot coordinate with. Enterprise APIs underpin partner integrations with contractual service levels. All of this raises the bar on versioning discipline, backward compatibility, governance, and developer experience.

Key principles

  • Design the contract first. The interface is a deliberate product decision, not a byproduct of implementation.
  • Optimize for the consumer's experience, not your own convenience.
  • Treat backward compatibility as a promise. Breaking changes need a new version and a migration path.
  • Make the easy thing correct: sensible defaults, predictable errors, consistent conventions.
  • Design for failure. Idempotency (a repeated request has the same effect as a single one), retries, pagination, and rate limiting are first-class concerns, not afterthoughts.
  • Choose the protocol style to fit the interaction, not the fashion.
  • Govern APIs as products, with owners, lifecycles, and documentation.

Recommendations

Work API-first and contract-driven

Define and review the API contract, including its resources, operations, schemas, and error semantics, before you write the implementation. Use a machine-readable specification, so the contract can generate documentation, client and server stubs, mock servers, and validation. Now consumers can start integrating against the mock while you build, and the contract becomes the single source of truth that both sides test against.

Choose the interaction style deliberately

Choose among REST (representational state transfer), GraphQL, gRPC, and event-driven messaging based on the interaction, not personal preference. Use REST for resource-oriented, broadly interoperable, cacheable interfaces. Use GraphQL when diverse clients need flexible, aggregated reads over a rich graph. Use gRPC for high-performance, strongly-typed calls between internal services. Use event-driven messaging for asynchronous, decoupled workflows and for propagating state changes. Many large systems use several styles at once, each where it fits.

Version and deprecate with discipline

Adopt an explicit versioning strategy and a published deprecation policy: how you classify changes, how long you support old versions, and how you notify consumers. Draw a clear line between backward-compatible changes (adding optional fields, new endpoints) and breaking changes (removing or renaming fields, changing types or semantics). Never repurpose the meaning of an existing field. Give consumers overlap windows to migrate, and communicate timelines well in advance.

Make error semantics consistent and machine-readable

Return structured, predictable errors: stable machine-readable codes, human-readable messages, and enough context to act on, without leaking sensitive internals. Use the same status semantics across every endpoint, so clients can handle errors uniformly. Document every error a consumer might run into.

Build in idempotency, pagination, and rate limiting

Make write operations safe to retry by supporting idempotency keys, so a client that retries after a timeout does not double-charge or double-create. Paginate every list endpoint from day one, and prefer cursor-based pagination for large or changing datasets. Apply and document rate limits, and return the current limit state to clients so they can back off gracefully.

Govern APIs and invest in developer experience

Treat each API as a product, with an owner, a lifecycle, and a catalog entry. Set up a design review or an API-standards board so interfaces stay consistent across teams. Invest in developer experience: accurate reference docs, quickstarts, examples, a sandbox, and a changelog. In a large ecosystem, a portal or catalog that makes APIs discoverable is essential.

Trade-offs: pros and cons

Style Best for Pros Cons
REST / HTTP Public, resource-oriented APIs Ubiquitous, cacheable, simple, interoperable Over/under-fetching; many round trips; loose contracts unless specified
GraphQL Flexible reads for varied clients Client-specified queries; one endpoint; strong schema Caching and rate-limiting complexity; query-cost risks; server complexity
gRPC Internal high-performance calls Fast, compact, strongly typed, streaming Poor browser support; less human-readable; heavier tooling
Event-driven Async, decoupled workflows Loose coupling; scalable; resilient Harder to reason about; eventual consistency; operational complexity

Versioning strategies trade stability against maintenance. Supporting many old versions protects consumers, but it multiplies the code you have to maintain and test. Backward compatibility trades your own freedom for consumer stability, usually the right trade for a widely used API. The big picture: the cost of a bad API decision is paid by every consumer over the entire life of the interface. So it is worth spending more design effort at the boundary than almost anywhere else.

Examples

Startup. A seed-stage startup shipping its first public API writes the contract as a machine-readable spec before coding, so its two design-partner customers can integrate against a mock while the backend is still being built. Even with only a handful of consumers, it adds idempotency keys to the charge endpoint and cursor pagination to every list, because retrofitting them once partners depend on the API would mean a breaking change it cannot afford. The upfront contract costs a day and saves weeks of support back-and-forth.

Enterprise. A large payments company exposes a public REST API to thousands of merchants. Every write endpoint accepts an idempotency key, so a network retry never creates a duplicate charge. Every list endpoint uses cursor pagination. Errors carry stable codes documented in a public reference. A formal deprecation policy guarantees a long support window for any version, with advance notice and migration guides. This discipline is a competitive advantage: integrators trust that the API will not break under them.

Government. A national digital service publishes an open API for citizen data, following mandated open standards and an API-first design process. The contract is specified and reviewed before the build, published in a central government API catalog, and served with a sandbox, so third-party developers, who cannot be individually coordinated, can integrate on their own. Long-term backward compatibility is a policy requirement, because integrations must survive across administrations and vendor changes. So breaking changes are rare and heavily governed.

Business case: motivations, ROI, and TCO

Good API design lowers integration cost, which is often the biggest cost in connecting systems and onboarding partners. With a clear, stable, well-documented API, consumers integrate in days without a single support ticket. A poor one generates endless support load, failed integrations, and reputational damage. When the API is itself the product, developer experience directly drives adoption and revenue.

The largest hidden cost is breaking changes. Every breaking change forces a coordinated migration across all consumers, internal teams and external partners alike, and the total cost scales with the number of consumers and how hard it is for them to move in lockstep. Investing up front in contract-first design, backward compatibility, and versioning discipline avoids these expensive, organization-wide migration events. When you talk to leadership, frame API quality as the leverage point for team autonomy, partner ecosystem growth, and avoiding costly forced migrations. Track integration time, support-ticket volume, and breaking-change frequency as your evidence.

Anti-patterns and pitfalls

  • Implementation-first APIs: the interface leaks internal database structure and changes whenever the implementation does.
  • Silent breaking changes: repurposing a field or tightening validation without a version bump breaks consumers unpredictably.
  • Chatty interfaces: designs that require many round trips for one logical operation, hurting performance and usability.
  • Inconsistent conventions: every endpoint invents its own naming, error format, and pagination, so clients cannot generalize.
  • No pagination or rate limiting: endpoints that work in testing and collapse under real data volume or load.
  • Non-idempotent writes: retries cause duplicates; a single network blip corrupts data.
  • Version proliferation: too many live versions with no deprecation, multiplying maintenance until it is unmanageable.
  • Docs as an afterthought: undocumented or out-of-date references that push all integration cost onto consumers.

Maturity model

  • Level 1, Initial: APIs emerge from implementation; no shared conventions; breaking changes are common and unannounced.
  • Level 2, Repeatable: Teams follow basic REST conventions and version informally, but consistency and docs vary.
  • Level 3, Defined: Contract-first design, machine-readable specs, a deprecation policy, and consistent error and pagination conventions across teams.
  • Level 4, Optimizing: APIs are governed products in a catalog with strong developer experience, measured adoption, automated compatibility checks, and rare, well-managed breaking changes.

Ideas for discussion

  • How do you decide when an internal API is stable enough to publish externally?
  • What is the right support window for deprecated versions in your context, and who pays for it?
  • Where should GraphQL or gRPC replace REST internally, and where would they add more complexity than value?
  • How do you enforce API consistency across many autonomous teams without becoming a bottleneck?
  • How should AI-consumable APIs and agent tool interfaces change your design conventions?
  • What automated checks can catch backward-incompatible changes before they ship?

Key takeaways

  • Design the contract first; the API is a product and a long-lived promise.
  • Backward compatibility protects consumers; breaking changes need new versions and migration paths.
  • Choose REST, GraphQL, gRPC, or events by interaction fit, not fashion.
  • Build in idempotency, pagination, rate limiting, and consistent errors from day one.
  • Govern APIs as products with owners, catalogs, and strong developer experience.

References and further reading

  • Roy Fielding, Architectural Styles and the Design of Network-based Software Architectures (dissertation)
  • Arnaud Lauret, The Design of Web APIs
  • Mike Amundsen, RESTful Web APIs and Design and Build Great Web APIs
  • Sam Newman, Building Microservices
  • OpenAPI Specification; JSON Schema (as reference standards)
  • Martin Kleppmann, Designing Data-Intensive Applications