# Design-First vs. Code-First API Development — Routebase

> Two workflows for building APIs, and why the order you write the contract in changes everything downstream: parallel work, review quality, and drift.

Canonical page: https://routebase.dev/blog/design-first-vs-code-first/
Published: 2026-07-03 · The Routebase Team · API Design, Workflow, OpenAPI

Every API has a contract. The only question is when it gets written — before
the implementation, as a deliberate design act, or after, as an export of
whatever the code happens to do. That single ordering decision shapes how
your team reviews changes, how fast your frontend can move, and how much you
trust your own documentation.

## The two workflows

**Code-first:** you write the implementation — controllers, handlers,
annotations — and generate the OpenAPI spec from it. The spec is a build
artifact. This is the default in most frameworks because it requires no
process change: add a route, get docs for free.

**Design-first:** you write the spec before the implementation. The contract
gets reviewed and agreed on as its own artifact, then backend and consumers
build against it — in parallel.

Code-first's appeal is real: one source file, no spec to keep in sync,
tooling included. For a solo service with one consumer, it's often enough.
The costs show up when more people depend on the contract than write the
implementation.

## What ordering changes

### Review happens at the right altitude

In a code-first workflow, API design review happens inside implementation
review — a new endpoint arrives as 400 lines of handler logic, validation,
and tests, with the contract implicit in the diff. Reviewers respond to the
code ("this method is long") because the design ("should this be two
resources?") is invisible.

Design-first makes the contract the diff:

```yaml
# The entire review surface for a new endpoint
/projects/{projectId}/environments:
  post:
    summary: Create an environment in a project
    requestBody:
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/CreateEnvironmentRequest"
    responses:
      "201":
        description: Environment created
      "409":
        description: An environment with this name already exists
```

Twenty lines. A reviewer can see the naming, the status codes, the resource
shape — and object *before* anyone has invested a week implementing the wrong
design. Breaking changes stop being archaeology and start being visible in
review.

### Consumers stop waiting

With a code-first flow, the contract exists when the backend is done — so
frontend, mobile, and partner teams start integrating at the end. With an
agreed spec, they start on day one, against [a mock server generated from the
contract](/api-mocking/):

- Backend implements the spec.
- Frontend builds against mocks that honor the same spec.
- Both meet at integration with no surprises, because neither invented
  anything.

This is the most measurable win of design-first: the spec turns a sequential
dependency into parallel work.

_Figure: The backend does the same amount of work in both plans. What changes is when everyone else can start: code-first hands the contract over at the end, so frontend, mobile and partner teams queue behind it, while an agreed spec lets all four run at once. The bars carry no durations on purpose — the claim is about ordering, not about a measured speed-up._

### The spec becomes enforceable

A generated spec can't disagree with the code — which sounds like a feature
until you realize it means the spec can't *catch* anything either. Whatever
the code does is, by definition, the contract. Rename a field in a refactor
and the spec silently renames it too; your consumers find out in production.

A designed spec is an independent statement of intent. That makes two things
possible: [contract testing](/api-testing/) (does the implementation actually
match?) and governance (does the design follow our conventions?). Linting a
generated spec is just critiquing the past; linting a designed spec prevents
the mistake.

## The honest objections

**"The spec and code will drift apart."** They will — if the spec lives in a
wiki and nothing checks it. Drift isn't a property of design-first; it's a
property of unenforced contracts. Contract tests in CI turn drift from a
slow rot into a failing build.

**"Writing YAML by hand is miserable."** Agreed, and nobody should. The
answer is [purpose-built design tooling](/api-design/) with schema reuse,
validation, and diffing — the spec is the artifact, not the editing
experience.

**"It's slower to start."** The first endpoint ships later; the tenth ships
faster. Design-first front-loads an hour of thinking to save the days that
integration surprises and breaking-change incidents cost later. For a
two-endpoint internal tool, that trade may not pay — for anything with
multiple consumers or a public surface, it pays quickly.

## Picking for your team

Code-first fits when one team owns both sides of the API, consumers are few,
and the contract is low-stakes. Design-first earns its process cost as soon
as the contract is load-bearing: multiple consumer teams, external users, or
[AI agents](/blog/designing-agent-ready-apis/) that read your spec as their
only source of truth.

The direction of travel is one-way, though. Teams that adopt design-first
rarely go back — because once the contract is where design, review, mocking,
and testing all happen, generating it from code feels like publishing your
blueprint after the building is up.

---

[Routebase](https://routebase.dev/) — [Sign up](https://app.routebase.dev/): Every account starts with a 14-day Pro trial — no credit card required.
