APIs & Microservices

API-First Design

Contracts, Conventions, and the Design Review

On this page

API-first design treats the interface between teams, systems and partners as a product whose contract is designed, reviewed and agreed before any implementation is written. The discipline pays in parallel work against mocks, consistency across hundreds of endpoints, reuse, governance that runs as automated checks, and a better experience for the developers who consume the result.

This paper sets out the principles of API-first design, compares REST over HTTP with JSON, gRPC, GraphQL, asynchronous event APIs and webhooks, and shows how a contract is specified with the OpenAPI Specification, JSON Schema, protocol buffers and AsyncAPI. A detailed style guide covers resource naming, HTTP semantics, pagination, idempotency, concurrency, errors, field conventions and long-running operations. Versioning and compatibility rules, the design review, documentation and testing practice, and the roles, metrics and maturity path that sustain API-first design at enterprise scale complete the picture.

Written for Enterprise and solution architects, API designers and tech leads, API platform and integration leads, and engineering managers responsible for how teams expose capabilities to one another and to partners.

Executive summary

Every capability an organization offers to another team, a partner or the public is reached through an application programming interface (API). That interface determines how quickly consumers can build, how often change breaks them and how much effort goes into rework. API-first design treats the API as a product and its contract, the formal description of what the interface accepts and returns, as the artifact that is designed, reviewed and agreed before implementation begins.

The alternative, writing code first and extracting a specification afterward, produces interfaces shaped by internal data models and framework defaults rather than by consumer needs, so that no two look alike. API-first inverts the order: consumers and producers agree the contract, both sides work in parallel against mocks generated from it, automated checks enforce shared conventions, and documentation, client libraries and tests derive from one source of truth.

  • Seven principles guide the practice: consumer orientation, the contract as source of truth, consistency over local optimization, evolvability, security by default, documentation as part of the product, and automation from the contract.
  • Style is chosen per interface: REST over HTTP with JSON for broad, long-lived interfaces; gRPC for efficient service-to-service calls; GraphQL where clients shape responses; event APIs and webhooks where the interaction is not request-response.
  • A short, enforced style guide covering naming, HTTP semantics, pagination, idempotency, concurrency, errors and field formats removes most design debate and most consumer surprises.
  • Compatibility comes from additive change, tolerant readers, a published deprecation policy and support windows; a new major version is the last resort.
  • The design review is a collaborative practice run by an API guild or center of enablement, supported by automated linting and scaled to the exposure of the API.

The later sections cover documentation, developer experience and testing, the roles and metrics of an API-first organization, recurring pitfalls, and a staged maturity path from undocumented endpoints to product-managed APIs.

Why API-first

An API is the promise one team makes to another about how a capability can be used; API-first means writing that promise down and agreeing it before building what fulfills it.

An application programming interface (API) is the boundary through which one piece of software uses another: the operations it exposes, the data they accept and return, and the rules of behavior around them. In an enterprise it is also the boundary between the team that owns a capability and everyone who depends on it, and it usually outlives the implementation behind it.

API-first design means that the contract, the formal, machine-readable description of the interface, is designed, reviewed and agreed before implementation begins and remains authoritative afterward. The contract is written in a specification language: the OpenAPI Specification for HTTP APIs, protocol buffer definitions for gRPC, or the AsyncAPI specification for event-driven interfaces.

The contrasting approach is code-first: a team implements its service, exposes the endpoints its framework makes convenient and generates a specification from the code afterward, if at all. A generated specification describes what was built rather than what consumers need, changes whenever the implementation changes, and arrives too late for anyone to object. A hundred independently built services yield a hundred dialects.

API-first pays in five ways:

  • Parallel work. Producers and consumers build at the same time against mocks generated from the agreed contract.
  • Consistency. The contract is checked against a shared style guide before code exists, so interfaces look and behave alike.
  • Reuse. A consumer who has learned one API can predict the next, which makes an existing capability easier to adopt than to rebuild.
  • Automation hooks for governance. Security, naming and compatibility checks run in the pipeline instead of in meetings.
  • Developer experience. Documentation, examples and client libraries derive from the contract, and the improvement shows in how quickly a new consumer makes a first successful call.

Code-first remains acceptable for an interface used by one team on both sides, internal to a single system and expected to live only as long as the release that introduced it. If anyone outside the producing team will ever depend on it, or if that is unknown, designing the contract first costs little compared with changing an interface that others have built on.

Principles of API-first design

Seven principles underpin API-first design. They are stated as commitments a producing team makes, because principles that cannot be translated into observable behavior decay into slogans. Later sections show how each is applied, and the design review checks adherence.

Consumer orientation

The API is designed from the consumer's tasks inward, not from the producer's data model outward.

The contract as source of truth

Implementation, documentation, mocks, tests and client libraries derive from the specification. A difference between contract and behavior is a defect in the implementation.

Consistency over local optimization

A convention slightly worse for one API but identical across all of them is preferred, because consumers then learn the organization's APIs once.

Evolvability and compatibility

Interfaces change without breaking existing consumers: additive by default, tolerant of unknown fields and explicit about what is guaranteed.

Security by default

Authentication, authorization scopes, input constraints and rate limits are in the contract from the first draft. An undeclared security scheme fails review.

Documentation and examples as product

Reference documentation, realistic examples and getting-started guides ship with the API and derive from the contract, so they cannot drift.

Automation from the contract

Linting, mock servers, conformance tests, gateway configuration and software development kits (SDKs) are generated from the contract rather than written by hand.

The principles sometimes conflict. Consumer orientation argues for an endpoint shaped to one client's screen, consistency argues against special cases, and evolvability argues for a more general design than the first consumer needs. The design review makes these trade-offs visible and records them. Consistency and evolvability are the most undervalued at design time, because their benefits accrue to consumers who have not arrived yet.

Security by default is the principle most often deferred. The Open Worldwide Application Security Project (OWASP) API Security Top 10 places broken object level authorization, broken authentication and broken object property level authorization at the head of its list of API weaknesses, and each is a design decision before it is an implementation bug. Declaring the security scheme, the scopes each operation requires and the properties each role may read or write makes those decisions reviewable. Enforcement happens at the gateway and in the service, but the decisions belong in the contract, where reviewers can see them.

Consumers learn an organization's APIs once if they are consistent, and one at a time if they are not.

Choosing the API style

An API style is the combination of interaction model, protocol conventions and contract language an interface uses. Five styles, counting webhooks separately, cover nearly all enterprise needs. The choice is made per interface according to its consumers and its interaction pattern, and the style guide names the supported styles and when each is chosen.

REST over HTTP with JSON

Representational state transfer (REST) is the architectural style Roy Fielding described in his doctoral dissertation, derived from the constraints that let the web scale: client-server separation, statelessness, cacheability, a uniform interface, a layered system and, optionally, code on demand. The uniform interface matters most for API design: resources identified by uniform resource identifiers (URIs), manipulated through representations, with self-descriptive messages and hypermedia links that tell a client what it can do next. Enterprise REST APIs exchange JavaScript Object Notation (JSON) representations of those resources over HTTP.

The Richardson maturity model, developed by Leonard Richardson and popularized by Martin Fowler, grades an HTTP API from level zero (procedure calls tunneled through one URI) through distinct resources (level one) and correct use of HTTP methods and status codes (level two) to hypermedia controls (level three). It is a vocabulary rather than a target. Most enterprise APIs aim for level two, and hypermedia pays mainly in long-lived public interfaces whose clients must discover what they can do.

gRPC

gRPC is a remote procedure call framework, originally developed at Google and hosted by the Cloud Native Computing Foundation (CNCF), in which services and messages are defined in protocol buffer interface definition files and carried over HTTP/2 in a compact binary encoding, with generated client and server code. It supports unary calls as well as client, server and bidirectional streaming. Efficiency, strong typing and streaming suit service-to-service communication inside a platform. Browsers cannot speak the gRPC protocol directly and use the gRPC-Web variant, usually through a translating proxy; traffic is unreadable without tooling; and the contract language is unfamiliar to many partners. Together these keep gRPC rare in public interfaces.

GraphQL

GraphQL is a query language and runtime, specified under the GraphQL Foundation, in which the server publishes a typed schema and each client asks for exactly the fields it wants in one request. It solves the over-fetching and under-fetching of fixed resources, and its costs are real. Requests usually travel as POST to a single endpoint, which defeats standard HTTP caching unless queries are persisted and sent with GET. Authorization must be enforced per field. Naive resolvers produce the N+1 query problem, one database query per related object, unless batching is designed in. Unbounded queries need depth and complexity limits. A backend for frontend (BFF), a thin API shaped to one user interface, often serves a single client more simply.

Asynchronous and event APIs

Order placed, payment settled and inventory adjusted are facts that many consumers want to learn about when they happen, not answers to a request. Asynchronous APIs publish such events to topics or queues under a contract, commonly described with the AsyncAPI specification. They decouple producers from consumers in time and fan one event out to many readers, at the price of eventual consistency and delivery semantics (ordering, duplicates and redelivery) that consumers must handle. An event API deserves the same contract-first treatment as a synchronous one: a schema per event type, a named owner, compatibility rules and an entry in the catalog.

Webhooks

Webhooks are HTTP callbacks: a consumer registers a URL, and the producer sends a POST request to it when an event occurs. They are the usual way to push events to external parties who cannot connect to an internal broker. Good webhook design treats the callback as an API that the producer calls: a documented payload schema, a signature header so the receiver can verify the sender, an event identifier so that duplicate deliveries can be ignored, retries with increasing delay, and a way to replay missed events. OpenAPI 3.1 added a top-level webhooks field so that callbacks are described in the same contract as the operations the consumer calls.

Interaction pattern: request-response to push and streaming

gRPC with streaming

Known services; efficiency and streams matter

Event APIs and webhooks

Many consumers; facts pushed as they happen

GraphQL or a BFF

One client shapes the response it needs

REST over HTTP with JSON

Broad, long-lived, cacheable interfaces

Consumer breadth: one known client to many independent consumers

Figure 1A starting point for choosing an API style by consumer breadth and interaction pattern

The figure above is a starting point rather than a rule: a broad, request-response interface defaults to REST, a push to many independent consumers defaults to events or webhooks, and the two styles on the left serve a known, narrow set of callers. The table below compares the five styles in more detail.

Table 1API styles compared
StyleContract and transportStrengthsWeaknessesTypical fit
REST with JSONOpenAPI; HTTP; text representationsUniform, cacheable, rich toolingOver- and under-fetching; chatty clientsPartner, public and cross-team APIs
gRPCProtocol buffers; HTTP/2; binaryEfficient; typed; streamingBrowsers need a proxy; unfamiliar to partnersService-to-service inside a platform
GraphQLGraphQL schema; usually POST to one endpointClient-shaped responsesCaching, per-field authorization and N+1 effortUser interfaces with varied data needs
Asynchronous eventsAsyncAPI; brokers and topicsTemporal decoupling; fan-outEventual consistency; delivery semanticsFacts many systems react to
WebhooksOpenAPI webhooks; HTTP POST callbacksReaches external partiesReceiver availability; signing and retriesPushing events to partners

Specifying the contract

Three specification languages cover the styles above. Each pairs a description of operations with a schema language for the data exchanged and metadata about security, servers and documentation.

The OpenAPI Specification

The OpenAPI Specification (OAS), maintained by the OpenAPI Initiative under the Linux Foundation, describes HTTP APIs in a JSON or YAML document. Version 3.1 aligned the Schema Object fully with JSON Schema and added the webhooks field. Version 3.2, which is backward compatible with 3.1, adds support for the HTTP QUERY method, hierarchical tags, streaming formats such as server-sent events and JSON Lines, and the OAuth 2.0 device authorization flow. A document opens with an info object and a servers list. The paths object holds one path item per URI template with its operations, and each operation declares its parameters, request body, responses by status code, security requirements and tags, together with an operation identifier that the specification requires to be unique when present and that many style guides make mandatory.

The components object is where reuse lives: schemas, parameters, responses, headers, examples, request bodies, links, callbacks and security schemes. Security schemes declare how the API authenticates callers: by API key, by HTTP authentication such as bearer tokens, by mutual TLS, through OAuth 2.0 flows with named scopes, or through OpenID Connect discovery. Operations reference components, so an error response or a pagination parameter is defined once. Examples attached to schemas and responses make generated documentation and mock servers realistic; a contract without them is incomplete.

The fragment below shows the shape of a reviewed operation: a named sub-resource rather than a verb in the path, a declared security requirement with its scope, an idempotency key, and responses defined once in components and referenced.

openapi: 3.1.0
info:
  title: Shipments API
  version: 1.4.0
paths:
  /shipments/{shipmentId}/cancellations:
    post:
      operationId: cancelShipment
      summary: Request cancellation of a shipment
      security:
        - partnerAuth: [shipments.write]
      parameters:
        - $ref: '#/components/parameters/ShipmentId'
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '202':
          $ref: '#/components/responses/Accepted'
        '409':
          $ref: '#/components/responses/Conflict'
components:
  securitySchemes:
    partnerAuth:
      type: oauth2
      flows:
        clientCredentials:
          tokenUrl: https://auth.example.com/oauth2/token
          scopes:
            shipments.write: Create and cancel shipments

Other contract languages

JSON Schema describes the structure and constraints of JSON data: types, required properties, formats, enumerations, ranges, patterns and composition through allOf, anyOf and oneOf. Since OpenAPI 3.1 the same schema can validate requests at the gateway, generate client types and drive documentation, provided it is strict enough to mean something.

Protocol buffer definition files are the contract for gRPC. Field numbers, not names, identify data on the wire, so a field number is never reused or renumbered, and the numbers and names of removed fields are marked reserved so that no later edit can reuse them by accident:

syntax = "proto3";

message Shipment {
  reserved 4;                  // a removed field: the number is never reused
  reserved "carrier_code";     // nor is its name
  string shipment_id = 1;
  string status = 2;
  repeated Parcel parcels = 3;
}

AsyncAPI applies the OpenAPI pattern to event-driven interfaces, with channels in place of paths and operations that send or receive messages. One style guide should span all three languages, because naming, error and compatibility conventions should not depend on the protocol.

The contract-first workflow

  1. Frame intent

    Consumer jobs, boundary, style and exposure tier

  2. Design contract

    Draft the specification with examples

  3. Review and lint

    Style guide checks and guild review

  4. Mock and build

    Consumers code against mocks in parallel

  5. Verify conformance

    Implementation tested against the contract

  6. Publish

    Docs, SDKs and catalog entry from the contract

Figure 2The contract-first workflow from intent to verified publication

The workflow starts before the specification. An intent review settles what the API is for, who will consume it, which style fits and how exposed it will be. The designer then drafts the contract with examples, runs the organization's linter locally and opens a pull request in the repository where the contract lives under version control, which is where review happens. Once the contract is approved, a mock server generated from it lets consumers begin while the producer implements against generated server stubs or validation middleware.

The step most often skipped is conformance verification: proving that the running implementation matches the approved contract. Conformance tests replay the contract's operations and examples against the implementation, validate every response against its schema and fail the build on any deviation. Only a passing implementation is published, and its documentation, client libraries and catalog entry are regenerated from the contract. Three further disciplines keep the contract authoritative: it changes only through reviewed pull requests, generated artifacts are never edited by hand, and the pipeline treats contract and implementation as a pair, failing a change to either one that the other does not match.

Design conventions and the style guide

A style guide is the organization's answer to the questions every API designer would otherwise answer differently; the shorter and better enforced it is, the more it is followed.

An API style guide records the conventions that make an organization's APIs feel like one product. Published guides from large technology companies are useful raw material, but a guide is effective only when it is short enough to read in an hour, enforced by a linter and owned by a group that can change it. Each convention below carries its rationale, because conventions without reasons are argued about forever.

Resources and HTTP semantics

Resources are nouns, named as a plural collection and a singular member, such as /customers and /customers/{customerId}. Nesting expresses ownership and stops at one or two levels; deeper hierarchies signal that a sub-resource deserves its own collection. Actions that are not simple state changes, such as approving an order, are modeled as a transition on the resource (a PATCH that sets its status) or as a named sub-resource (/orders/{orderId}/approvals) created with POST, never as a verb in the path.

HTTP methods carry the semantics defined in RFC 9110, HTTP Semantics. GET and HEAD are safe and must not change state. PUT and DELETE are idempotent, so repeating them has the same effect as sending them once, and every safe method is idempotent too. POST is neither, and serves creation and operations with no better home. PATCH (RFC 5789) applies a partial update, commonly as a JSON Merge Patch document (RFC 7396) or, where precise operations matter, a JSON Patch document (RFC 6902). QUERY (RFC 10008) is a safe, idempotent method that carries the query in the request body, for searches too large or complex to fit comfortably in a URI.

Status codes keep their standard meanings: 201 (Created) with a Location header for creation, 202 (Accepted) for work accepted but not yet complete, 204 (No Content) for success without a body, 409 (Conflict) for a state conflict, 412 (Precondition Failed) for a failed precondition, and 422 (Unprocessable Content) for a well-formed request that the server cannot process, which many guides use for business-rule violations.

Collections

Every collection endpoint is paged from its first version, because adding pagination later is a breaking change. Offset pagination (page and size parameters) is simple but degrades on large tables and returns duplicates or gaps when rows change between requests. Cursor pagination returns an opaque token marking the position after the last item; it is stable under concurrent change, at the cost of random access. Many guides default to cursors, return the next cursor in the body and optionally in a Link header (RFC 8288), and cap the page size. Filtering, sorting and partial responses use query parameters named after fields, a sort parameter and a fields parameter respectively.

Idempotency and concurrency

POST operations that create or charge are not idempotent, yet clients retry them after timeouts. An idempotency key, a unique value the client generates and sends in a request header, lets the server recognize a retry and return the original result instead of acting twice. The server stores the key with its response for a bounded period and rejects reuse of the key with a different payload. The pattern is widely used in payment APIs, and an Internet-Draft from the IETF HTTP APIs working group proposes a standard Idempotency-Key header field for it.

Concurrent updates use optimistic concurrency. A GET returns an ETag header identifying the current version of the representation, the client sends it back in an If-Match header on PUT or PATCH, and the server rejects the update with 412 (Precondition Failed) if the resource changed in between. RFC 9110 defines these conditional request semantics, and 428 (Precondition Required), from RFC 6585, lets a server insist that clients use them. The alternative, last write wins, silently discards work. The exchange below shows both mechanisms: a payment request that can be retried safely, and an update made against a stale version that the server refuses.

POST /payments HTTP/1.1
Idempotency-Key: 5f7c2d1e-8a4b-4c3d-9e2f-6a1b0c9d8e7f
Content-Type: application/json

{ "amount": "125.00", "currency": "EUR", "payee": "acct-77f3" }

PATCH /customers/c-81f2 HTTP/1.1
If-Match: "v7"
Content-Type: application/merge-patch+json

{ "preferredName": "Pat" }

HTTP/1.1 412 Precondition Failed

Errors

Errors are part of the contract and deserve one shape across every API. RFC 9457, Problem Details for HTTP APIs, which obsoletes the earlier RFC 7807, defines a JSON document with the media type application/problem+json and the members type (a URI reference identifying the kind of problem, which defaults to about:blank), title, status, detail and instance, plus extension members such as a list of invalid fields or a correlation identifier. An organizational catalog of problem types, documented once and referenced from every API, gives consumers failures they can handle in code rather than by matching message strings; RFC 9457 also establishes an IANA registry for problem types that are common across APIs. Messages never expose stack traces or internal identifiers.

HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://problems.example.com/validation-failed",
  "title": "The request failed validation",
  "status": 422,
  "detail": "Two fields are invalid.",
  "instance": "/orders/requests/9b2e41",
  "correlationId": "req-8c41f2d7",
  "errors": [
    { "pointer": "/deliveryWindow", "detail": "Must start after the order time" },
    { "pointer": "/items/0/quantity", "detail": "Must be at least 1" }
  ]
}

Fields and operational conventions

Field conventions remove a whole class of consumer bugs. One casing convention for property names is chosen and never mixed. Dates and times are strings in RFC 3339 format with an explicit offset, and a calendar date is distinguished from a timestamp. Money is never a floating-point number: amounts are decimal strings with an ISO 4217 currency code. Identifiers are opaque strings rather than sequential integers, which also makes enumeration harder (though it never replaces an authorization check), and enumerations list their permitted values with the rule that consumers tolerate values they do not recognize.

Operational conventions complete the guide. Every request carries a correlation identifier, generated by the client or the gateway and echoed in the response, so that a failure can be traced across services; where distributed tracing is in use, the W3C Trace Context headers carry the same identity from service to service. Rate limits are signaled rather than merely enforced: a 429 (Too Many Requests) response carries a Retry-After header and, commonly, headers disclosing the limit and the remaining quota, for which an IETF Internet-Draft defines standard RateLimit and RateLimit-Policy fields. Long-running operations return 202 (Accepted) with a Location header pointing to a status resource that the client polls, or that notifies it through a webhook. Bulk operations accept a bounded collection in one request and report a result per item.

Table 2Core conventions of an API style guide and the rationale for each
ConcernConventionRationale
Resource namingPlural nouns; shallow nesting; no verbs in pathsPredictable URIs; actions modeled as state changes or sub-resources
Methods and status codesRFC 9110 semantics; standard codes with documented meaningClients, caches and gateways behave correctly by default
CollectionsCursor paging by default; field-named filters; fields parameterStable under change; less over-fetching
IdempotencyIdempotency key header on unsafe POST operationsSafe retries after timeouts; no duplicate charges
ConcurrencyETag on reads; If-Match on updates; 412 on conflictPrevents silent lost updates
ErrorsRFC 9457 problem details with a catalog of problem typesOne error shape, handled in code rather than by string matching
Dates and moneyRFC 3339 timestamps; decimal strings with an ISO 4217 codeNo time zone or floating-point ambiguity
Correlation and rate limitsCorrelation identifier on every call; 429 with Retry-AfterTraceable failures; clients back off politely
Long-running and bulk202 with a status resource; bounded batches with per-item resultsNo blocked connections; no caller starves others

Versioning and compatibility

The cheapest version is the one never issued. An API that evolves compatibly for years serves consumers better than one that ships a clean new major version every year or two and leaves everyone with a migration project. Compatibility is a design discipline first and a versioning scheme second.

Breaking and compatible changes

A breaking change is one that can cause a correctly written existing consumer to fail. The table below gives the rules most style guides adopt. The hard cases are changes that are technically compatible but practically risky, such as a different default sort order or page size; these are treated as breaking unless the contract documented the behavior as unspecified.

Table 3Compatibility rules for common contract changes
ChangeCompatibleHandling
Add an optional request field or parameterYesDocument the default behavior when it is absent
Add a response fieldYesConsumers must ignore unknown fields
Add an operation or resourceYesAnnounce in the changelog; no version change
Add an enumeration valueConditionalOnly where consumers were told to tolerate unknown values
Remove or rename a field or operationNoNew major version, or a long deprecation with Sunset signaling
Change a type, format or constraintNoAdd a new field instead and deprecate the old one
Make an optional field requiredNoBreaking for every existing consumer
Change authentication requirementsNoRun old and new schemes in parallel during migration
Change default ordering or pagingTreat as noBehavior consumers rely on, even if undocumented
Change an error type or status codeNoConsumers branch on these in code

Tolerant readers

Compatibility is a two-sided bargain. Producers make only additive changes, and consumers behave as tolerant readers, the pattern Martin Fowler described: they ignore fields they do not recognize, do not depend on field order, accept new enumeration values and validate only what they use. Generated clients must be configured accordingly, since strict deserialization turns every additive change into an outage. Hyrum's law also applies: with enough consumers, every observable behavior of an interface will be depended on by someone, whatever the contract promises, so defaults, ordering and timing deserve as much care as schemas.

Versioning strategies

When a breaking change cannot be avoided, the new contract needs an identifier, and three strategies compete. URI versioning (/v2/customers) is explicit and easy to route at a gateway, but it implies that the whole API changes at once and tempts teams to version everything. Header versioning, a custom request header naming the version, keeps URIs stable but hides the version from casual inspection, and from caches unless the header is listed in the Vary response header. Media type versioning, a versioned media type in the Accept header, is the most consistent with HTTP content negotiation and versions representations rather than whole APIs, but it is the least familiar to consumers. Many organizations put a major version in the URI, change everything else additively and run at most two major versions at once.

Deprecation and sunset signaling

A deprecation policy published before the first consumer arrives is what makes retirement possible later. It states the minimum support window, how consumers are notified and what happens at the end. In practice the version is marked deprecated in the contract and the catalog, consumers are notified, and usage is monitored per consumer. Responses then carry two standard signals. The Deprecation header (RFC 9745) states that the resource is or will be deprecated, and a Link header with the deprecation relation type can point to migration documentation. The Sunset header (RFC 8594) states when the resource is expected to become unresponsive; the sunset time must not be earlier than the deprecation time. Support windows scale with exposure: an internal version can be retired once its usage reaches zero, while partner and public APIs commit to windows stated in the contract.

Version as a last resort, deprecate as a policy, and change additively as a habit.

The API design review

The design review is where API-first becomes an organizational practice rather than an individual habit. It catches wrong boundaries, consumer-hostile designs and convention breaches while they are cheap to fix, and it spreads design judgment across teams. Done badly, it is a committee approving unread specifications from a backlog; done well, it is a conversation on a pull request, completed within days.

Intent before detail

Review happens twice. The intent review takes place before detailed design, on a one-page statement: the consumer jobs the API supports, the bounded context it belongs to, the resources or events it will expose, the proposed style and exposure tier, and the existing APIs considered and rejected. It is the moment to discover that the capability already exists, or that the consumer needs an event feed rather than a polling endpoint. The contract review follows on the full specification and concentrates on what linting cannot judge: naming that matches the business language, resource granularity, workflow completeness, error cases and security scopes.

Who reviews

Reviewers come from an API guild or center of enablement: a cross-team group of experienced designers who review, coach and maintain the style guide. The group is not a gate committee. Approval authority rests with the producing team for internal APIs and escalates only for partner and public interfaces; the guild's influence comes from the quality of its comments and from the linter it maintains. Membership rotates, and the guild's time is funded explicitly rather than borrowed from delivery.

Automation and proportionate rigor

Automated contract linting runs on every pull request against a rule set that encodes the style guide: naming patterns, required security schemes, mandatory examples, error response shapes, pagination parameters on collections and forbidden constructs. Open-source linters that accept custom rule sets, of which Spectral is one example, remove the trivial findings that otherwise dominate human review. Rigor then scales with exposure tier. An internal API needs linting and one peer reviewer. A partner API adds guild review, security review and a stated deprecation policy. A public API adds product and legal review, a sandbox and a published changelog from the first release.

Table 4Design review checklist scaled by API exposure tier
CheckInternalPartnerPublic
Intent statement reviewedTeam leadGuildGuild and product
Contract linted in the pipelineRequiredRequiredRequired
Human contract reviewOne peerGuild reviewerGuild and security
Security scheme and scopes declaredRequiredRequired and reviewedRequired and threat modeled
Examples and mock serverRecommendedRequiredRequired, with a sandbox
Deprecation policy and support windowDefault policyStated in the contractPublished with a changelog
Design decisions recordedPull requestDesign recordDesign record

Recording decisions

Unrecorded decisions are argued again at the next review. A short API design record, modeled on the architecture decision record, captures the question, the options, the choice and its consequences, and it is stored with the contract. Recurring decisions graduate into the style guide, and the records provide the evidence that API lifecycle governance asks for as a by-product of design rather than as separate paperwork.

Documentation, developer experience and testing

An API is adopted through its documentation, not its implementation. Developer experience (DX) is everything a consumer meets between hearing of an API and running it in production: discovery, reference documentation, guides, credentials, a place to try calls safely, client libraries and notice of change. Each can be derived from, or anchored to, the contract.

Portal and catalog

Discovery, onboarding, credentials, changelog

Reference docs and guides

Generated reference; written task guides

SDKs and client libraries

Generated per language; configured as tolerant readers

Mocks and tests

Mock servers; contract and conformance tests

Gateway policies

Request validation, security schemes, rate limits

The contract

OpenAPI, protocol buffers or AsyncAPI in source control

Derived from one source of truth
Figure 3Artifacts that one contract supports, from the developer portal down to the specification

Documentation and examples

Reference documentation is generated from the contract on every release, so it is complete and current by construction; its quality depends on the descriptions and examples in the contract, which reviewers should read as documentation. Guides written by people cover the tasks a consumer actually has, such as onboarding a customer, as sequences of calls with realistic payloads. A sandbox with test credentials and deterministic data lets consumers try those sequences without risk, and a changelog, generated from contract differences and annotated by the team, tells existing consumers what changed.

Software development kits

SDKs generated from the contract lower the first-call barrier and encode conventions such as authentication, retries, pagination and idempotency keys, so that consumers do not reimplement them. Generation keeps them in step with the contract; hand-written SDKs drift and become a second contract. Languages follow consumer demand, SDKs are versioned alongside the API, and a generated client that fails on an additive change is a defect in the generator configuration.

Testing against the contract

Three kinds of test keep contract and implementation aligned. Conformance tests, run in the producer's pipeline, exercise the implementation against the contract and fail on any schema or status deviation. Consumer-driven contract tests, a pattern described by Ian Robinson, work in the other direction: each consumer records the interactions it depends on, the producer replays them against every build, and a change that breaks a recorded expectation fails before release. Frameworks such as Pact are one example of the category. Mock servers generated from the contract serve the consumer side during development and give the consumer's own tests a stable target.

Discovery

None of this helps a consumer who cannot find the API. The API catalog, usually surfaced through a developer portal, lists every API with its owner, tier, status, contract, documentation and consumers, and it is populated from the pipeline so that it stays complete. The OWASP API Security Top 10 lists improper inventory management as a risk in its own right, because an API that is not cataloged is not reviewed, monitored or retired. Publication to the catalog closes the contract-first workflow and opens the consumer's.

Organizing for API-first

API-first survives only where someone owns each API as a product and where teams have help adopting the practices. Three roles recur.

API product owner

Accountable for an API's consumers, roadmap, deprecation decisions and metrics.

API platform team

Runs the gateway, the catalog and portal, the linter and the pipeline templates that make contract-first the easy path.

Enablement function

An API guild or named center of enablement that maintains the style guide, reviews designs and coaches teams.

Keeping tools and skill-building separate stops a tooling team from being blamed for design quality it cannot influence, and stops an enablement group from becoming a bottleneck for platform changes.

Metrics

API-first is measured through its consumers. The metrics below are reported per API and in aggregate; trends matter more than single values.

Table 5Metrics for an API-first practice
MetricWhat it indicatesHow to use it
Time to first successful callDeveloper experience from discovery to a working requestMeasure in the sandbox; remove onboarding friction first
Adoption and reuseConsumers per API; share of new integrations that use existing APIsLow reuse signals discoverability or design problems
Contract conformanceShare of APIs whose implementation passes conformance testsTarget full coverage for the partner and public tiers
Change failure rateShare of API releases that cause consumer-visible incidentsPair with the count of breaking changes per quarter
Deprecation progressConsumers remaining on deprecated versions against the support windowDrive outreach; retire on the published date
Consumer satisfactionPeriodic rating from internal and partner developersRead together with support ticket themes

Common pitfalls

A staged maturity path

  1. Stage 1
    Undocumented

    Endpoints without contracts or owners

  2. Stage 2
    Documented

    Specifications exist, often generated after the code

  3. Stage 3
    Standardized

    Style guide, linter and catalog in place

  4. Stage 4
    Contract-first

    Review, mocks and conformance before release

  5. Stage 5
    Product-managed

    Owners, metrics, deprecation policy and consumer voice

Figure 4A maturity path for API-first design

The order of the stages matters. Documenting what exists creates the inventory without which no standard can be enforced. Standardization gives teams something concrete to conform to. Contract-first then becomes achievable, because the review has rules to apply and the pipeline has artifacts to generate. Product management comes last, because an owner cannot manage consumers who cannot find the API or trust its contract. Most organizations start with partner-facing APIs and extend the practice inward; mandating API-first everywhere on one date produces specifications written to pass a gate rather than to serve a consumer.

Conclusion

API-first design is a small inversion with large consequences: the contract comes first, it is agreed with consumers, and everything else is derived from it. The supporting practices are unglamorous: a short style guide, a linter in the pipeline, a review that concentrates on intent and trade-offs, additive change as a habit, a published deprecation policy, and documentation and tests generated from the same source of truth. Organizations that adopt them gain interfaces that consumers learn once, change that does not break them and governance that runs as code; those that skip them accumulate dialects, migrations and meetings.

The place to begin is the next API that will be exposed beyond its own team.

  1. Run an intent review. Write the one-page statement of consumers, boundary, style and exposure tier, and check the catalog for an existing capability before designing anything.
  2. Adopt a short style guide. Start from the conventions in this paper, encode them as lint rules and run the linter on every pull request.
  3. Put the contract under version control. Review it as a pull request, generate a mock server from it and let consumers start before the implementation is complete.
  4. Verify conformance on every build. Fail the pipeline when implementation and contract disagree, and regenerate documentation and SDKs only from a passing contract.
  5. Publish the deprecation policy. State support windows and the Deprecation and Sunset signaling before the first consumer arrives.
  6. Measure from the consumer's side. Track time to first successful call, reuse and conformance, and extend the practice inward once partner-facing APIs show the benefit.

Glossary

API-first design
The practice of designing, reviewing and agreeing an API's contract before implementation and keeping that contract authoritative afterward.
API contract
The formal, machine-readable description of an interface: its operations, data schemas, security requirements and documented behavior.
API style guide
An organization's published conventions for naming, HTTP usage, pagination, errors, field formats and versioning, enforced by linting and review.
AsyncAPI
A specification for describing event-driven interfaces, with channels in place of paths and operations that send or receive messages.
Backend for frontend (BFF)
A thin API owned by a user interface team and shaped to that interface's screens rather than to general reuse.
Breaking change
A change to a contract or its behavior that can cause a correctly written existing consumer to fail.
Consumer-driven contract
A test in which a consumer records the interactions it depends on and the producer verifies every build against them.
Cursor pagination
Paging a collection with an opaque token that marks the position after the last item returned, stable under concurrent change.
Deprecation policy
The published rules for announcing, supporting and retiring an API version, including minimum support windows and notification channels.
Exposure tier
The classification of an API as internal, partner or public, which sets the rigor of review, documentation and support it requires.
Idempotency key
A unique client-generated value sent with an unsafe request so that a retried request produces the original result rather than a second effect.
OpenAPI Specification (OAS)
The standard maintained by the OpenAPI Initiative for describing HTTP APIs in a machine-readable JSON or YAML document.
Optimistic concurrency
Detecting conflicting updates by sending a resource's version identifier (an ETag) with the update and rejecting the update if the resource has changed.
Problem details
The standard error document for HTTP APIs defined in RFC 9457, with type, title, status, detail and instance members.
Richardson maturity model
A four-level vocabulary for how fully an HTTP API uses resources, HTTP methods and hypermedia, from level zero to level three.
Sunset header
An HTTP response header defined in RFC 8594 that states when a resource is expected to become unresponsive; it complements the Deprecation header of RFC 9745.
Tolerant reader
A consumer that ignores unknown fields, does not depend on field order and validates only what it uses, so that additive changes do not break it.

References and further reading

  1. OpenAPI Initiative (Linux Foundation), OpenAPI Specification, versions 3.1 and 3.2.
  2. JSON Schema, JSON Schema Core and JSON Schema Validation specifications.
  3. AsyncAPI Initiative, AsyncAPI Specification.
  4. gRPC Authors, gRPC documentation; and Protocol Buffers Language Guide.
  5. GraphQL Foundation, GraphQL Specification.
  6. IETF RFC 9110, HTTP Semantics; RFC 5789, PATCH Method for HTTP; RFC 7396, JSON Merge Patch; and RFC 10008, The HTTP QUERY Method.
  7. IETF RFC 9457, Problem Details for HTTP APIs, which obsoletes RFC 7807.
  8. IETF RFC 9745, The Deprecation HTTP Response Header Field; and RFC 8594, The Sunset HTTP Header Field.
  9. IETF RFC 6585, Additional HTTP Status Codes; RFC 8288, Web Linking; and RFC 3339, Date and Time on the Internet: Timestamps.
  10. IETF HTTP APIs Working Group, Internet-Drafts The Idempotency-Key HTTP Header Field and RateLimit header fields for HTTP.
  11. Roy T. Fielding, Architectural Styles and the Design of Network-based Software Architectures, doctoral dissertation, University of California, Irvine.
  12. Martin Fowler, Richardson Maturity Model: Steps Toward the Glory of REST and TolerantReader, martinfowler.com.
  13. Ian Robinson, Consumer-Driven Contracts: A Service Evolution Pattern, martinfowler.com.
  14. OWASP Foundation, OWASP API Security Top 10.

This paper offers general guidance and is not legal, regulatory or professional advice. Summaries of laws, regulations and standards are for orientation only; confirm the obligations that apply to your organization with qualified advisors. Product and vendor names are used for illustration and imply no endorsement.

© ByteCI, LLC. All rights reserved.

Put this into practice

Want help applying this?

ByteCI helps federal and private-sector teams turn guidance like this into working architecture, roadmaps and delivery. Tell us about your situation.