What Is Spec-First API Development?

Spec-first API development, explained with a real OpenAPI example. Generate mocks, tests, and docs from one spec, and do it in Apidog.

Ashley Innocent

Ashley Innocent

2 June 2026

What Is Spec-First API Development?

Apidog for Enterprise

On-Premises Deploy

SSO & RBAC

SOC 2 Compliant

Explore Apidog Enterprise

Most API bugs are not coding mistakes. They are agreement mistakes. The frontend expected userId, the backend sent user_id, and nobody noticed until QA. Spec-first API development fixes this by making the contract the first thing you write, not the last thing you document.

In this guide you will write a small OpenAPI spec by hand, then use that single file to generate mocks, tests, and docs before any server code exists. The same approach goes by a few names: spec-driven development, design-first, or contract-first. They all point to the same idea. Agree on the interface first, then build to it.

button

What spec-first API development is

Spec-first API development means you author a machine-readable contract, usually an OpenAPI document, before you implement the endpoint. That contract describes every path, parameter, request body, response shape, and status code. Once it exists, it becomes the source of truth for everyone touching the API.

The spec is not documentation that trails behind the code. It leads. Frontend teams build against a mock generated from it. QA writes tests against it. The backend implements to satisfy it. When all three agree with the same file, integration stops being a guessing game.

This flips the usual order. In code-first development, you write handlers and then describe them afterward, often by hand, often out of date within a sprint. In a design-first workflow, the description comes first and the code answers to it. That single change moves most integration problems to the start of the project, where they are cheap to fix.

Spec-first vs code-first lifecycle

The two approaches produce the same endpoints. They differ in when problems surface and who can start work in parallel.

The right-hand column is the payoff. When the contract exists first, three teams stop blocking each other and start building against one shared definition.

A worked OpenAPI example

Let’s design a small /users endpoint step by step. We will build it in pieces so each part is clear, then assemble the full file.

Start with the document header. This declares the OpenAPI version and basic metadata.

openapi: 3.0.3
info:
 title: Users API
 version: 1.0.0
servers:
 - url: https://api.example.com/v1

Next, define the User schema under components. Defining it once lets you reference it everywhere, so the shape stays consistent across requests and responses.

components:
 schemas:
 User:
 type: object
 required: [id, email, createdAt]
 properties:
 id:
 type: string
 format: uuid
 email:
 type: string
 format: email
 name:
 type: string
 createdAt:
 type: string
 format: date-time

Now add the GET /users path. It lists users and supports a limit query parameter. Notice how the response reuses the User schema with $ref instead of redefining it.

paths:
 /users:
 get:
 summary: List users
 operationId: listUsers
 parameters:
 - name: limit
 in: query
 schema:
 type: integer
 default: 20
 maximum: 100
 responses:
 "200":
 description: A list of users
 content:
 application/json:
 schema:
 type: array
 items:
 $ref: "#/components/schemas/User"

Add the POST /users operation to create a user. The request body defines what the client must send, and the 201 response returns the created record.

 post:
 summary: Create a user
 operationId: createUser
 requestBody:
 required: true
 content:
 application/json:
 schema:
 type: object
 required: [email]
 properties:
 email:
 type: string
 format: email
 name:
 type: string
 responses:
 "201":
 description: User created
 content:
 application/json:
 schema:
 $ref: "#/components/schemas/User"
 "400":
 description: Invalid request body

That is a complete, valid contract. It names every field, marks email as required, caps limit at 100, and declares both the success and error responses. Nobody has written a line of server code yet, but the agreement is locked.

Generate mocks, tests, and docs from the spec

The reason to write the spec first is leverage. One file drives three artifacts that teams usually build separately.

Mocks. A mock server reads your spec and returns example responses that match every schema. The format: uuid and format: email hints let tooling generate realistic sample data. Your frontend calls GET /users and gets a well-formed array of users on day one, long before the real handler exists. When the spec changes, the mock changes with it.

Tests. Because the spec declares required fields, types, and status codes, it doubles as a test oracle. Contract tests assert that the real response for POST /users returns a 201 with a body matching the User schema, and a 400 when email is missing. You are not inventing assertions. You are checking that the implementation matches what you already agreed to.

Docs. API reference documentation renders directly from the spec. Every endpoint, parameter, and example you see in the docs comes from the same YAML. There is no second copy to keep in sync, so the docs cannot drift from the contract.

This is also what makes spec-first pair well with a git-native API workflow: the spec is a plain text file, so every change to the contract shows up as a reviewable diff in a pull request. A reviewer can see that someone renamed email or dropped a required field before it ships.

Doing it in Apidog

Apidog supports this end to end through Spec-First Mode. Instead of treating the OpenAPI file as an export, it treats the file as the project. You edit the YAML directly and the rest of the workspace follows.

You write or paste the /users spec into the editor. Apidog parses it and immediately stands up a mock server for both operations, so your frontend can start calling them. The generated documentation updates as you type. When you are ready to verify the implementation, you run the spec’s operations as test cases against your real backend and confirm the responses match the schemas.

The part that keeps this honest is two-way Git sync. Your spec lives in a repository, and changes flow in both directions. Edit the YAML in your editor and push, and Apidog picks it up. Edit in Apidog, and the change lands as a commit your team can review. The contract never lives in two places at once. If you want the deeper comparison of where this sits next to a pure design-first approach, see spec-first vs design-first in Apidog.

A spec-first checklist

Use this before you call a spec ready to build against:

If every box is checked, your teams can build in parallel against one agreement instead of three guesses.

FAQ

Is spec-first API development the same as design-first?

Mostly yes. “Design-first” and “contract-first” describe the same principle: write the interface before the implementation. “Spec-first” is the most literal name because the OpenAPI spec file is the concrete artifact you start with. In practice the terms are used interchangeably.

Do I have to write YAML by hand?

No. You can author the spec in a visual editor and let it produce the YAML, or write the YAML directly if you prefer. The point is not the format you type, it is that the contract exists and is agreed upon before code. Many teams mix both, drafting visually and refining in YAML during review.

How do I stop the spec and the code from drifting apart?

Make the spec the source of truth and enforce it. Keep the spec in Git so changes are reviewed as diffs. Run contract tests in CI so the build fails when a response stops matching the schema. With two-way sync, edits in either place stay reconciled, which removes the most common cause of drift.

Spec-first API development is a small change in order with a large change in outcome. Write the contract, agree on it, then build to it. If you want to try the full loop, open Spec-First Mode in Apidog and point it at your repo.

button

Explore more

Is Bruno Request-First? When You Need a Design-First Tool

Is Bruno Request-First? When You Need a Design-First Tool

Bruno is request-first by design. Here's when a design-first, OpenAPI-native workflow wins, and how Apidog Spec-First Mode delivers it.

2 June 2026

What Does It Mean to Treat Your API Spec as Code?

What Does It Mean to Treat Your API Spec as Code?

Treat your API spec as code: version, diff, and review OpenAPI in Git. How spec-as-code makes the OpenAPI file your single source of truth.

2 June 2026

Spec-First or Design-First: Which Apidog Mode Should You Use?

Spec-First or Design-First: Which Apidog Mode Should You Use?

Spec-first or design-first? Compare Apidog's two API modes by Git workflow, editor, and team type, and learn when to switch between them.

2 June 2026

Practice API Design-first in Apidog

Discover an easier way to build and use APIs

What Is Spec-First API Development?