Most API teams treat the contract as an afterthought. They write code first, then generate a spec, then watch the two drift apart. Git-native API design flips that order. You treat the API contract as source code, version it in Git, and review every change the same way you review application logic.
This guide is about the discipline, not a single tool. You’ll learn how to design contracts in branches, review them in pull requests, and turn a committed spec into mocks, tests, and docs. The goal is a workflow where your Git history is your API history.
If you already know what Spec-First tooling looks like and want the product walkthrough, read the companion piece on the git-native API workflow. This article stays focused on practice.
What “git-native” means for API work
Git-native means your API definition lives in your repository as a plain text file. Not in a proprietary cloud database. Not behind a vendor login. A .yaml or .json file sitting next to your code, tracked by the same version control your team already uses.
Contrast this with cloud-locked tools. Many API design platforms store the contract in their own backend. You edit through a web UI, and the canonical version lives on their servers. Your repo holds a stale export at best. When the vendor’s database is the source of truth, your Git history tells you nothing about how the API evolved.

The git-native model inverts that relationship. The file in main is the contract. Everything else, including any GUI, is a view onto that file. This single shift unlocks history, blame, rollback, and review for your API surface.
A git-native setup has three properties. The spec is a text file in the repo. Changes flow through normal Git operations like branch, commit, and merge. And every downstream artifact, from mocks to docs, derives from the committed file rather than a separate database.
Why design and develop APIs in Git
You already trust Git with your most valuable asset: your code. Your API contract deserves the same treatment.
History is the first reason. When someone asks “when did we add the cursor pagination parameter,” git log answers in seconds. The commit that introduced it carries a message, an author, and a date. No screenshots, no changelog archaeology.
Blame is the second. Run git blame on the spec file and you see exactly who changed each line and why. A confusing field name traces back to the PR that added it, with the discussion attached. Accountability becomes automatic.
Rollback is the third. A bad design ships. With Git, you git revert the merge and the contract returns to its prior state. Downstream codegen, mocks, and docs regenerate from the reverted file. There’s no manual cleanup in a separate system.
Review is the fourth, and it’s the one teams underuse. A pull request is the natural place to debate an API design before it becomes real. Reviewers comment on a + line that adds a required field. The conversation lives next to the change forever.
Single source of truth ties it together. When the contract is one file in main, there’s no ambiguity about which version is real. Frontend, backend, QA, and docs all read the same line of YAML. This is the core promise of a git-based API specification workflow.
The git-native API design loop
The loop has five steps: design the contract, commit, open a PR, review, and merge. Implementation follows the merge, not the other way around.
Start by writing the contract. Suppose you’re adding an endpoint to fetch a user’s invoices. You create a branch and edit the OpenAPI file.
# openapi.yaml (excerpt added on branch feat/invoices-list)
paths:
/users/{userId}/invoices:
get:
operationId: listUserInvoices
summary: List invoices for a user
parameters:
- name: userId
in: path
required: true
schema: { type: string, format: uuid }
- name: status
in: query
required: false
schema:
type: string
enum: [draft, open, paid, void]
responses:
"200":
description: A page of invoices
content:
application/json:
schema:
$ref: "#/components/schemas/InvoiceList"
"404":
description: User not found
Commit that change with a clear message: git commit -m "Add GET /users/{userId}/invoices contract". The commit is small and focused. It describes one design decision.
Now open a pull request. The diff shows reviewers exactly what’s new: one path, one operation, two parameters, two responses. Your teammates discuss naming, the enum values, and whether 404 is the right code for a missing user. They might push for cursor pagination before a single line of handler code exists.
Once the PR is approved, merge it. The contract in main now includes the invoices endpoint. Implementation comes next, and it’s constrained by the spec you all agreed on. This ordering is what people mean by spec-first API development: the agreement precedes the code.
The payoff is that design debates happen cheaply. Changing a YAML field in review costs minutes. Changing a shipped, implemented, documented endpoint costs days.
Branching strategy for API contracts
Treat contract changes like any other change: one branch per logical unit of work. A branch per endpoint or per modification keeps PRs small and diffs readable.
Name branches so the intent is obvious. Use a prefix that signals the change class. This helps reviewers and CI route the work.
| Change type | Branch prefix | Example | Review weight |
|---|---|---|---|
| New endpoint | feat/api- |
feat/api-invoices-list |
Standard |
| Additive field | feat/api- |
feat/api-invoice-currency |
Light |
| Breaking change | break/api- |
break/api-remove-legacy-id |
Heavy, needs sign-off |
| Bug fix in spec | fix/api- |
fix/api-status-enum-typo |
Light |
| Refactor only | chore/api- |
chore/api-reorder-schemas |
Light |
The prefix carries meaning. A break/api- branch tells reviewers to slow down and check every consumer. A chore/api- branch signals no semantic change, so review can move fast.
You also choose a branching model. Trunk-based development suits most API teams. Short-lived branches merge into main daily, and the spec stays close to a single line of truth. Gitflow, with long-running develop and release branches, fits teams that batch API changes into scheduled releases.
| Model | Best for | API tradeoff |
|---|---|---|
| Trunk-based | Continuous delivery, small teams | Contract evolves in small steps; less merge pain |
| Gitflow | Scheduled releases, regulated shipping | Spec diverges on develop; bigger, riskier merges |
For most API work, prefer trunk-based. Small, frequent contract changes produce small, frequent diffs. Long-lived branches let the spec drift, and YAML merge conflicts get ugly fast when two branches restructure the same file.
Reviewing API design in pull requests
A spec PR is a design review, not a syntax check. Reviewers look at semantics, and a few questions cover most of the risk.
Does this break existing consumers? Removing a field, renaming a path, or tightening a type are all breaking. A reviewer checks whether the change is additive or breaking, and breaking changes demand a version bump or a deprecation path.
Is the naming consistent? If every collection endpoint uses plural nouns, a new singular path stands out. If errors return a code field elsewhere, a new endpoint should too. Reviewers enforce the patterns the API already established.
Is it diff-friendly? Keep the YAML stable so diffs stay small. Order keys consistently. Add new paths in a predictable place. A reviewer can read a five-line diff in seconds, but a reordered file produces a hundred-line diff that hides the real change.
reviewer-friendly breaking change. The - lines flag exactly what disappears.
# Diff a reviewer sees in the PR
parameters:
- name: status
in: query
schema:
type: string
- enum: [draft, open, paid, void]
+ enum: [draft, open, paid, void, uncollectible]
That diff is additive to the enum, so it’s safe. Compare it to a - line that removes void entirely, which would break any client sending that value. The diff makes the difference visible at a glance.
Encourage reviewers to comment inline on the spec, the same way they comment on code. The discussion stays attached to the line and survives in the merged history.
From design to development
Once the contract is in main, it becomes the source for everything downstream. You generate, you don’t hand-write.

Codegen comes first. Tools like openapi-generator produce server stubs and typed clients from the committed file. Your handlers fill in business logic, but the request and response shapes match the contract by construction. The spec and the code can’t disagree about the wire format.
Mocks come next. A mock server reads the spec and returns example responses for every endpoint. Frontend developers build against the mock before the backend exists. They start the moment the contract merges, not weeks later.
Tests follow. Contract tests assert that your running server matches the spec. Send a request, validate the response against the schema, and fail the build if they diverge. This is the guardrail against spec/code drift.
Docs generate too. Reference documentation renders straight from the OpenAPI file. When the contract changes, the docs change in the same commit. There’s no separate doc update to forget.
The principle is consistent. Every artifact derives from one committed file. Regenerate on each merge, and your mocks, clients, tests, and docs stay in lockstep with the contract.
Team conventions that scale
Conventions are what keep a git-native workflow from collapsing as the team grows. Decide them early and write them down.
First, choose between one spec file and many. A single openapi.yaml is simple but grows unwieldy past a few dozen endpoints. Splitting into multiple files with $ref references keeps each file readable, at the cost of a bundling step. A common pattern is one file per resource, bundled into a single spec at build time.
Second, version deliberately. Bump the OpenAPI info.version on every meaningful change, and follow semantic versioning. Additive changes bump the minor version. Breaking changes bump the major version and usually mean a new path prefix like /v2/.
Third, keep a changelog. A CHANGELOG.md next to the spec records what changed and why in human terms. Git history is precise but verbose; the changelog is the readable summary your consumers actually scan.
Fourth, protect the spec with CODEOWNERS. Require API stewards to approve any change to the contract file. This stops well-meaning contributors from shipping inconsistent designs.
# .github/CODEOWNERS
/api/openapi.yaml @api-stewards
/api/paths/ @api-stewards
Fifth, lint in CI. A linter catches style and consistency problems before review. Run it on every PR so humans review design, not formatting.
# .github/workflows/api-lint.yml
name: API Lint
on:
pull_request:
paths: ["api/"]
jobs:
spectral:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Spectral
run: npx @stoplight/spectral-cli lint api/openapi.yaml --fail-severity warn
With CI linting plus CODEOWNERS, every contract change gets automated checks and a human steward. That combination scales from three engineers to three hundred.
Common pitfalls and how to avoid them
Git-native API design has predictable failure modes. Knowing them lets you design around them.
Spec/code drift is the worst. The contract says one thing; the running server does another. Prevent it with contract tests in CI that validate live responses against the committed spec. If they diverge, the build fails. Drift becomes a broken pipeline, not a production surprise.
Giant PRs are the next trap. A single branch that adds twenty endpoints produces an unreviewable diff. Reviewers skim, approve, and miss problems. Split work into one endpoint or one change per PR. Small diffs get real review.
Hand-written artifacts cause silent inconsistency. When someone hand-writes a client instead of generating it, the client drifts from the spec. Generate clients, stubs, and docs from the committed file every time. Treat hand-written API artifacts as a smell.
YAML merge conflicts frustrate teams on long-lived branches. Two branches restructure the same file and Git can’t reconcile them. Avoid this with short-lived branches, a stable key order, and a split-file layout so changes touch different files. Trunk-based development plus small PRs removes most conflicts before they start.
The pattern across all four is the same. Keep changes small, derive artifacts from the spec, and let CI enforce the contract. Discipline beats heroics.
Where Apidog fits
You can run a git-native workflow with a text editor and a CLI. Many teams want a GUI for design without giving up Git as the source of truth. That’s the gap Apidog’s Spec-First Mode fills.
Spec-First Mode keeps the OpenAPI file in your Git repository and has two-way sync. You edit the contract in Apidog’s visual designer or in your editor, and both stay consistent with the file in your repo. The file in Git remains canonical, so branches, PRs, and history all work exactly as described here. You get the GUI without the cloud lock-in. See the Spec-First Mode documentation for setup details.
The point isn’t the tool. It’s that you can have a visual design surface and a git-native discipline at the same time. The repo stays the single source of truth, and Apidog becomes one view onto it.
FAQ
Is git-native API design only for OpenAPI?
No. The discipline applies to any text-based contract format. OpenAPI is the most common, but the same workflow works for AsyncAPI, gRPC .proto files, or GraphQL SDL. As long as the contract is a text file you can diff, branch, and review, it’s git-native.
How do I handle breaking changes in a git-native workflow?
Make breaking changes visible and deliberate. Use a break/api- branch prefix, bump the major version, and require steward sign-off through CODEOWNERS. Where possible, add the new shape alongside the old one and deprecate the old path on a timeline. The PR diff and version bump together signal the break to every consumer.
Should the API spec live in the same repo as the code?
Usually yes, when one team owns both. Co-locating the spec and the implementation means a single PR can change the contract and the handler together, and contract tests run in one pipeline. Put the spec in a separate repo only when many teams consume one shared API and need independent versioning.
How do I prevent spec and code from drifting apart?
Add contract tests to CI. They send real requests to your running server and validate every response against the committed spec. A divergence fails the build. Combined with generating stubs and clients from the spec, contract tests make drift a pipeline failure instead of a production bug.
Conclusion
Git-native API design is a discipline, not a product. You treat the contract as source code, evolve it in branches, review it in pull requests, and generate every downstream artifact from the committed file. History, blame, rollback, and review come for free because Git already gives them to you.
Start small. Move your spec into the repo, add a lint step, and require review on contract changes. Build from there with codegen, mocks, and contract tests. The workflow compounds: each convention makes the next one easier, and your Git history becomes a complete record of how your API grew.
If you want a visual design surface that keeps the spec in Git, try Spec-First Mode in Apidog and see how two-way sync fits the workflow above.



