Your OpenAPI spec is a contract. When it drifts, clients break, mocks go stale, and tests can pass against an API that no longer exists. Treat the spec like production code: put it in Git, branch it, review it, and validate it on every change.
This guide shows how to version-control OpenAPI with Git in a practical workflow: where to store the file, how to branch, what to review in a spec diff, how to validate changes in CI, and how to commit and push changes from Apidog.
Why version-control your OpenAPI spec
A spec stored in a wiki or shared drive has no reliable history. You cannot easily see who changed the POST /orders payload, why it changed, or how to roll it back.
Put openapi.yaml under version control and you get:
- History: every change is a commit with an author, timestamp, and message.
-
Blame:
git blame openapi.yamlshows who added a field and when. - Rollback: revert a bad commit if a contract change breaks clients.
-
Review: spec changes go through pull requests before they reach
main.
This is the foundation of a Git-native API workflow: the spec is source code, and source code belongs in Git.
Where to put the OpenAPI file
Keep the location predictable. For a single API version, use either the repo root or an api/ directory:
my-service/
├── api/
│ └── openapi.yaml
├── src/
└── README.md
For most teams, this is enough:
api/openapi.yaml
If you maintain multiple major versions in parallel, split by version:
api/
├── api-v1.yaml
└── api-v2.yaml
Use this structure when v1 must stay stable for existing clients while v2 evolves.
Benefits of version-specific files:
- Breaking changes stay isolated.
- Diffs are smaller.
- Reviewers know which contract is changing.
- CI can validate each version independently.
This is the core idea behind API spec as code: your API contract should be stored, reviewed, and shipped like code.
Pick one convention and document it in your repo. Avoid having multiple files that all claim to be the canonical spec.
Branching strategy for OpenAPI changes
You do not need a special branching model for API specs. Reuse your existing Git workflow:
- Branch from
main. - Change the spec.
- Validate locally.
- Open a pull request.
- Review and merge.
A clear branch naming convention helps reviewers spot contract changes quickly:
| Branch type | Pattern | Example |
|---|---|---|
| New endpoint | api/add-<resource> |
api/add-refunds |
| Field change | api/change-<resource>-<field> |
api/change-order-status |
| Breaking change | api/breaking-<description> |
api/breaking-v2-auth |
| Fix / cleanup | api/fix-<description> |
api/fix-pagination-schema |
The api/ prefix makes API contract changes easy to filter in pull requests and CI.
For breaking changes, use an explicit breaking- prefix. That signals that the PR may require:
- a major version bump,
- migration notes,
- client communication,
- additional review.
Keep spec branches short-lived. A branch that stays open for weeks is more likely to conflict with other contract changes.
Make OpenAPI diffs easy to review
A spec PR is a contract change. Reviewers should be able to read the diff without fighting formatting noise.
Write YAML consistently:
paths:
/orders/{orderId}:
get:
summary: Get an order
parameters:
- name: orderId
in: path
required: true
schema:
type: string
responses:
"200":
description: Order found
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
Use these formatting rules:
- consistent indentation,
- one property per line,
- stable ordering for paths and schemas,
- descriptive operation summaries,
- no unnecessary reformatting in unrelated sections.
A one-field change should look like a one-field diff.
What to review in a spec PR
When reviewing an OpenAPI pull request, check the contract impact first.
1. Breaking changes
Look for changes that can break existing clients:
- added required request fields,
- removed response fields,
- renamed fields,
- changed field types,
- removed enum values,
- changed path parameters,
- changed authentication requirements.
Example breaking change:
components:
schemas:
CreateOrderRequest:
type: object
required:
- customerId
- shippingAddress
- couponCode # New required field: breaking
Adding couponCode as required breaks clients that do not send it.
Prefer a backward-compatible shape:
components:
schemas:
CreateOrderRequest:
type: object
required:
- customerId
- shippingAddress
properties:
couponCode:
type: string
description: Optional coupon code.
2. Backward compatibility
Usually safe:
- adding a new endpoint,
- adding an optional request field,
- adding a response field,
- adding a new enum value if clients are designed to handle unknown values.
Risky or breaking:
- removing fields,
- changing field types,
- making optional fields required,
- changing response status codes,
- narrowing accepted input.
3. Naming consistency
Check whether the new endpoint follows the rest of the API:
GET /orders
GET /orders/{orderId}
POST /orders
Avoid introducing inconsistent patterns like:
GET /order-list
GET /getOrderById
4. Completeness
Every new operation should include:
-
summary, - request schema if applicable,
- response schemas,
- error responses,
- examples where useful,
- authentication requirements if applicable.
Example:
paths:
/refunds:
post:
summary: Create a refund
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateRefundRequest"
responses:
"201":
description: Refund created
content:
application/json:
schema:
$ref: "#/components/schemas/Refund"
"400":
description: Invalid refund request
"401":
description: Unauthorized
5. Examples
Examples make docs, mocks, and tests more useful.
components:
schemas:
Refund:
type: object
properties:
id:
type: string
example: "ref_123"
orderId:
type: string
example: "ord_456"
amount:
type: integer
example: 2500
currency:
type: string
example: "USD"
status:
type: string
example: "pending"
Do not rely only on manual review for breaking changes. Add CI checks that compare the PR spec against main and fail when incompatible changes are detected.
Commit and push from Apidog
Editing YAML by hand works, but a visual editor with live validation can catch mistakes earlier.
Apidog’s Spec-First Mode supports two-way Git sync: you can edit the spec in the UI, commit changes, push to your repo, and pull teammate changes back into Apidog.
A practical workflow:
- Connect your Git repo.
- Point Apidog to the OpenAPI file, such as
api/openapi.yaml. - Edit endpoints, schemas, parameters, and examples in the visual editor.
- Let Apidog write the updated YAML back to the file.
- Commit on a branch.
- Push the branch.
- Open a pull request in your Git provider.
Because Apidog serializes YAML consistently, diffs stay smaller and easier to review. You avoid noisy reordering or unrelated reformatting.
Read the setup guide in the Apidog Spec-First Mode docs. If you want to sync to a specific Git provider, see syncing your OpenAPI spec to GitHub.
Validate OpenAPI changes in CI
Never let an invalid spec reach main. Add validation to your pull-request checks.
A minimal GitHub Actions workflow using Redocly CLI:
name: Validate OpenAPI
on:
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint OpenAPI spec
run: npx @redocly/cli lint api/openapi.yaml
This catches structural and style issues before merge.
You can also validate multiple spec files:
name: Validate OpenAPI
on:
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint API v1
run: npx @redocly/cli lint api/api-v1.yaml
- name: Lint API v2
run: npx @redocly/cli lint api/api-v2.yaml
Detect breaking changes automatically
Linting verifies that the spec is valid. It does not always tell you whether the change is safe for existing clients.
Add a diff step that compares the PR version against main.
Example using oasdiff:
name: OpenAPI Breaking Change Check
on:
pull_request:
jobs:
breaking-changes:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install oasdiff
run: |
curl -fsSL https://raw.githubusercontent.com/Tufin/oasdiff/main/install.sh | sh
- name: Check for breaking changes
run: |
git show origin/main:api/openapi.yaml > /tmp/openapi-main.yaml
oasdiff breaking /tmp/openapi-main.yaml api/openapi.yaml
This kind of check can catch:
- removed fields,
- new required parameters,
- changed schema types,
- removed endpoints,
- changed response contracts.
Humans can miss these in a large diff. CI should not.
Best practices for OpenAPI version control
Use these habits to keep your spec workflow reliable.
Use semantic versioning
Update info.version when the contract changes.
info:
title: Orders API
version: 2.1.0
A common convention:
- breaking change → major version bump,
- backward-compatible feature → minor version bump,
- documentation or non-contract fix → patch version bump.
Keep a changelog
Add a CHANGELOG.md next to the spec:
api/
├── openapi.yaml
└── CHANGELOG.md
Example entry:
## 2.1.0
- Added `GET /refunds/{refundId}`.
- Added optional `refundReason` field to refund responses.
- Updated examples for refund creation.
Keep PRs small
One logical API change per PR.
Good:
Add refund creation endpoint
Avoid:
Update refunds, orders, auth, pagination, and examples
Large spec PRs make breaking changes harder to spot.
Write descriptive commit messages
Use messages that explain the contract change:
Add refundReason to refund request schema
Avoid:
update spec
Never edit main directly
Even small changes should go through a branch and PR. This keeps history, review, and CI consistent.
FAQ
How do I detect breaking changes in an OpenAPI spec?
Run a spec-diff tool in CI that compares the pull request spec against the version on main.
Tools like oasdiff can classify changes as breaking, non-breaking, or unclassified, then fail the build when a breaking change is detected.
This helps catch removed fields, new required parameters, changed types, and removed endpoints.
Should I keep one OpenAPI file or split it across many?
Start with one openapi.yaml. It is easier to review, validate, and version.
Split only when you need to:
- maintain multiple major versions, such as
api-v1.yamlandapi-v2.yaml, - reduce a very large file,
- reuse schemas with
$ref.
Do not split too early. One readable file is often better than several fragmented files.
Can I version-control my spec without writing YAML by hand?
Yes. Apidog’s Spec-First Mode lets you edit the spec in a visual editor and sync changes to Git in both directions.
You still get:
- Git history,
- branches,
- pull requests,
- CI validation,
- readable YAML diffs.

Top comments (0)