.png)
The way we build software is shifting.
APIs are no longer an integration detail at the edge of a product. They are product surfaces in their own right, and increasingly, surfaces that AI agents consume too.
At the same time, tooling for API design, generation, testing, and documentation has matured to the point where treating the API or service contract as the starting point pays off in a way it did not a few years ago.
For a company like Primer, the consequences of that contract are not confined to the engineering team. It’s also the interface merchants depend on to process live payments. And when a contract misrepresents how an API behaves, it creates production risks for merchants, as any integration mistake can result in failed payments.
This article explains how we practise API-first engineering to keep that from happening.
What led us to API-first
So what is API-first exactly?
It is an approach where you start by defining the interface—the API contract—before committing to implementation or writing a line of code.
Getting the schema right early unlocks a range of benefits, from code generation and documentation to testing and AI-agent integration. At Primer, we apply it both to the public APIs we expose to merchants and to internal service-to-service communication.
We moved to API-first because we kept seeing the same problem: code drifting out of sync with specifications, documentation, and tests. Without a central interface description that is current and valid, small gaps become larger problems that show up in different ways.
For example, a specification that no longer reflects the code can allow bugs or incidents through that more contract tests would have caught. It also makes collaboration harder, whether a backend and frontend engineer are building an API together or an infrastructure team needs to understand which endpoints a service exposes.
Two forms of this hurt us the most, and they are what pushed us towards API-first:
- Trust: When documentation drifts from the live API merchants feel the effect directly. A mismatch can break checkout or create a poor developer experience, eroding trust in our ability to deliver.
- Scale: As our number of internal services grew, engineers and infrastructure teams needed a way to see and consume a service's API without reading its codebase. And reading a service's codebase just to learn how to call it does not scale and adds needless cognitive load.
The rest of this article looks first at the general benefits of API-first, then at how we apply it at Primer. The most popular standard for describing APIs in this approach is OpenAPI, which we reference throughout. The same approach works with other standards and tools such as AsyncAPI for event-driven APIs, Protobuf, and many others.
Benefits of an API-first approach
Easier consumption
A clear, well-considered service contract makes an API easier to consume. It reduces the time developers spend working out what an API does, what each field means, and how it should be used.
APIs rarely remain static. They evolve with new functionality, additional fields, and richer data models. Designing the contract with future evolution in mind makes it much easier to extend an API without introducing breaking changes or forcing client migrations.
Automatic code generation
Once you have an API contract, it can be used to generate both client- and server-side code, removing a whole class of hand-written boilerplate. There are tools available for many programming languages that can help with this. In fact, entire companies have been built around this idea, such as Fern, Speakeasy, and Stainless, providing powerful platforms for code generation and building production-ready SDKs.
Automatic documentation generation
One of the most popular uses for OpenAPI is documentation generation. Swagger UI (Swagger Specification was the predecessor of the OpenAPI Specification) remains one of the most popular tools for rendering API documentation, while products such as Mintlify, README.com, and others provide polished developer portals for production use.
Easier Testing
In the day-to-day development process, even before any code is written, engineers can run mock servers using only an OpenAPI specification. This makes it possible to test integrations and validate the API contract before the implementation exists.
For example, a frontend engineer can build against a mock server generated from the specification while the backend is still in progress, then point to the real service once it lands without changing the client.
OpenAPI specifications can also be used to generate test cases, supporting test-driven development, while the underlying schemas are commonly used for contract testing and runtime validation of API requests and responses.
Enabling AI Agents
With the evolution of AI, the API-first approach has become even more valuable. Large language models (LLMs) can use API specifications to understand a service’s capabilities and generate code, tests and integrations.
More recently, OpenAPI has also become a common source definition for generating Model Context Protocol (MCP) servers. An MCP generator maps OpenAPI operations into MCP tools that AI agents can discover and invoke. When an LLM calls one of these tools, the MCP server translates the request into the corresponding HTTP call to the underlying API.
A single OpenAPI specification can therefore serve as the foundation for documentation, SDKs, testing, and AI-powered integrations.
This only scratches the surface of what OpenAPI can be used for. For the full list of tools that can leverage OpenAPI specifications, see OpenAPI Tools.
.png)
API-first practices at Primer
Before we adopted API-first, clients were handwritten, and the OpenAPI spec followed the code rather than leading it. We either maintained it manually whenever the code changed or generated it from the implementation. That generated output was often difficult for humans to read, and downstream generators would often fail on it.
We moved away from that incrementally rather than in one large move.
We started by making OpenAPI specs a standard for our internal services, established API design standards so specs stay consistent with one another, and then enforced those standards with automation in CI. We later added a schema registry and further polished our public OpenAPI specifications.
We’re now integrating Arazzo and Overlay specs into the workflow that we’re covering later in the article. To be fair, we still do some of this by hand in places, and we treat API-first as a journey rather than a finished state. The rest of this section walks through how each piece works today.
Schema before code
Before writing a single line of code for a new API, we create its OpenAPI schema. OpenAPI files are committed alongside the codebase, so they are versioned together with the implementation and easy to reference.
We leverage LLMs to generate an initial draft according to our internal standards, then iterate on it, scrutinize it and review it carefully. This is also where the main risk of LLM-drafted specs shows up: they look plausible while being subtly wrong, a field marked optional that should be required, an enum missing a value.
We treat the generated draft as a starting point to be picked apart, not accepted, which is why the review step is not optional.
For all the public APIs we expose, a dedicated group of API reviewers additionally ensures consistency and standards across our API specifications. A central review group is a deliberate tradeoff: it protects consistency across our public surface, but it can become a bottleneck, so we lean on the automated vacuum checks below to catch the mechanical issues and keep human review focused on design.
What we generate
Once the specification is agreed, we generate as much as we can from it, such as:
- Clients: Generated for the services consuming the API, removing hand-written HTTP code
- Server scaffolding: Generated server code that keeps the implementation aligned with the contract
- Test cases: Generated tests for the future implementation, supporting test-driven development
This lets us focus purely on business logic, and it means the models and server code never drift from the specification, since both are produced from the same source.
In some of our services, for example, we generate the Pydantic request and response models straight from the OpenAPI spec with datamodel-code-generator:
Generating the full server scaffolding has been harder. We tried several tools, and each came with limitations: some made it hard to modify the generated stubs; some could not accommodate our standard HTTP client, and others generated only the models, like datamodel-code-generator above. That is why we use it for exactly that. We are still defining our approach to server scaffolding.
Beyond development teams
Specifications serve more than development teams. Infrastructure and security teams use OpenAPI files to identify the endpoints we expose without needing to look at the code, put safety measures around them, and test them against different attack vectors.
This gives us an accurate, machine-readable inventory of our API surface without maintaining one manually.
JSON Schemas and our registry
Starting with OpenAPI 3.1, the Schema Object is fully based on JSON Schema Draft 2020-12. This is a significant change from OpenAPI 3.0: JSON Schemas can now be embedded, used independently of OpenAPI, and large specifications become far more readable when broken down into smaller schemas.
At Primer, this version allowed us to use standalone JSON Schemas for writing fixtures and in our contract tests, independently of the OpenAPI files they belong to.
We also run a dedicated registry for both JSON Schemas and OpenAPI files. Every schema gets a stable reference URL, which is then used for rendering documentation, resolving references between schemas, and access by other tools.
.png)
Lifecycle specs
The API-first ecosystem has continued to evolve beyond OpenAPI. Complementary specifications such as Arazzo and OpenAPI Overlays address additional parts of the API lifecycle.
Arazzo provides a standard way to describe multi-step workflows that span multiple API operations, while Overlays allow reusable modifications to API specifications without changing the source document.
We have brought both into our workflows recently. Arazzo lets us describe these multi-operation scenarios and test them before the implementation exists. We found Arazzo GPT to be a helpful assistant when creating these scenarios.
Overlays proved especially useful when we needed to keep internal details of our APIs out of the published specs, while still maintaining a single production OpenAPI file as the source of truth. We’re also exploring Overlays as a mechanism for versioning our APIs.
Standards and consistency
As we expand the number of services and APIs, it becomes increasingly important to follow the same standards across the board. This matters most for the public APIs we expose to merchants. Every inconsistency, whether in naming, pagination, authentication, error handling or response formats, adds cognitive load for developers integrating with our platform, while a consistent surface lets them transfer knowledge from one endpoint to another.
For merchants, the effect is direct. A consistent, predictable API surface shortens integration time, reduces the chance of integration errors, and means less back-and-forth with our support teams. In payments, that predictability is not a nicety, since an integration mistake can result in failed payments and lost revenue for the merchant.
Consistency also benefits us internally. Shared conventions make it easier for engineers to move between services, simplify code reviews, and improve the quality of generated clients and documentation.
The vacuum checks mentioned earlier validate our OpenAPI specifications against engineering-wide API standards. These checks run as part of our CI pipelines, providing fast feedback during development and ensuring that new APIs and changes consistently follow our established conventions and best practices. Here are a few examples of our vacuum rules:
These rules let us scale our API ecosystem while maintaining a high level of quality and consistency across all services.
The case for API-first
API-first shifts the effort to where it pays off most, which is designing a clear contract before we write any code. That one contract then drives client and server generation, documentation, mock servers, contract tests, security tooling, and increasingly, AI integrations through MCP. In practice, this means we hand-write far less boilerplate and spend less time reconciling implementations with their specs, which leaves more room for business logic. Combined with shared standards enforced in CI, it lets us scale the number of services and APIs while keeping quality and consistency high.
.png)


%20(1).png)
.png)
.avif)