Your API contract probably lives in three places: a wiki diagram, an exported Postman collection, and a Markdown file that no longer matches production. When those disagree, developers guess. Guessing breaks integrations.
Spec-as-code fixes this by making one OpenAPI file the source of truth. You commit it to Git, review it in pull requests, and generate mocks, tests, docs, and SDKs from that same file.
This guide shows how to treat your OpenAPI spec like application code and wire it into a practical development workflow.
What spec-as-code means
Spec-as-code means your API definition is a plain-text file stored in version control. It is not a database record, a hosted document, or a manually updated wiki page. It is a file your team can diff, branch, review, merge, and roll back.
The pattern is similar to docs-as-code: keep the artifact close to the code and ship it through the same workflow. With APIs, the artifact is usually an OpenAPI document in YAML or JSON.
A typical repo layout looks like this:
my-service/
├── api/
│ └── openapi.yaml
├── src/
├── tests/
└── package.json
That api/openapi.yaml file becomes the contract. Developers check it before implementing endpoints. QA uses it to build contract tests. Consumers use it to generate SDKs or read reference docs.
Why treat the API spec like code?
Once the spec is in Git, you can apply the same controls you already use for source code.
Review changes in pull requests
Endpoint changes become visible diffs.
Instead of discovering a breaking change after deployment, reviewers can catch it before merge:
status:
type: string
- enum: [pending, shipped, delivered]
+ enum: [pending, shipped, delivered, canceled]
That diff immediately shows a contract change. Reviewers can decide whether it is safe, backward-compatible, or needs a version bump.
This is the core of a Git-native API workflow.
Keep history and rollback simple
Every spec change has:
- a commit
- an author
- a timestamp
- a pull request discussion
- a rollback path
If a spec change causes problems, rollback is standard Git:
git revert <commit-sha>
For API versioning strategies, see OpenAPI version control with Git.
Generate downstream outputs from one file
When mocks, tests, documentation, and SDKs all come from the same OpenAPI file, they cannot drift independently.
Update the spec, regenerate outputs, and your API ecosystem stays aligned.
| Concern | Spec in a hosted tool | API spec as code |
|---|---|---|
| Change review | Manual, easy to miss | PR diff, blocking review |
| History | Limited or vendor-locked | Full Git log |
| Rollback | Often manual | git revert |
| Source of truth | Ambiguous | The committed file |
| CI integration | Bolt-on | Native |
Use OpenAPI as the artifact
OpenAPI is the practical choice because it is widely supported, human-readable, and machine-parseable.
Here is a small OpenAPI 3.1 example you could keep at api/openapi.yaml:
openapi: 3.1.0
info:
title: Orders API
version: 1.2.0
paths:
/orders/{orderId}:
get:
summary: Get an order by ID
operationId: getOrder
parameters:
- name: orderId
in: path
required: true
schema:
type: string
format: uuid
responses:
"200":
description: The requested order
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
"404":
description: Order not found
components:
schemas:
Order:
type: object
required:
- id
- status
- total
properties:
id:
type: string
format: uuid
status:
type: string
enum:
- pending
- shipped
- delivered
total:
type: number
format: float
Now every contract change is explicit.
Adding a property becomes a small diff:
properties:
total:
type: number
format: float
+ currency:
+ type: string
+ example: USD
Changing a response code is also visible:
responses:
"200":
description: The requested order
+ "401":
+ description: Unauthorized
"404":
description: Order not found
That is the main benefit: API design decisions become reviewable source changes.
Generate mocks, tests, docs, and SDKs
The value of spec-as-code comes from wiring outputs to the spec.
1. Generate mocks
Point a mock server at the OpenAPI file so frontend and mobile teams can start integration before the backend is complete.
The workflow is:
OpenAPI spec -> mock server -> frontend/mobile integration
When the spec changes, the mock changes with it.
2. Generate contract tests
Contract tests verify that the running service still matches the committed spec.
A useful CI check should catch issues like:
- endpoint missing from implementation
- undocumented response status
- missing documented response field
- response type mismatch
- invalid request schema
The workflow is:
OpenAPI spec -> contract test -> live service validation
Without this check, the spec can still drift from production.
3. Generate documentation
Reference docs should be rendered from the OpenAPI file instead of maintained by hand.
The workflow is:
OpenAPI spec -> API reference docs
That keeps endpoint paths, parameters, schemas, and response examples aligned with the contract.
4. Generate SDKs
Client SDKs can be generated from the same file.
The workflow is:
OpenAPI spec -> generated SDKs -> API consumers
This gives consumers typed clients that reflect the current API contract.
How Apidog fits into a spec-as-code workflow
Running spec-as-code manually usually means connecting several tools:
- OpenAPI editor
- linter
- mock server
- docs generator
- contract test runner
- Git workflow
Apidog’s Spec-First Mode is designed around the OpenAPI file as the authoritative API definition. You can design endpoints against the spec and keep mocks, tests, and documentation aligned with that contract.
The key workflow is two-way Git sync. Apidog can read the OpenAPI file from a repository and write changes back to it. That lets teams work visually when useful, edit YAML directly when needed, and still keep Git as the source of truth.
For the mechanics, see how to sync your OpenAPI spec to GitHub.
The important point: visual tooling should sit on top of the committed OpenAPI file, not replace it.
Common implementation pitfalls
Pitfall 1: No CI check
Committing the spec is not enough. If CI never validates it, invalid OpenAPI can still merge.
Add a CI step that lints the file on every pull request.
Example workflow:
Pull request opened
↓
Lint OpenAPI spec
↓
Run contract checks
↓
Merge only if checks pass
Pitfall 2: Spec and implementation drift
If the running API is not tested against the spec, the two can diverge quietly.
Fix this by adding contract tests that compare live responses against the committed OpenAPI document.
Pitfall 3: Two sources of truth
Avoid mixing two competing workflows:
Code annotations generate spec
Manual spec edits generate docs
Pick one authoritative direction.
If the OpenAPI file is the source of truth, then implementation should be checked against it. If code annotations are the source, then generated OpenAPI should be treated as the output.
Do not let both become master copies.
Pitfall 4: Treating the spec as documentation only
A spec you only read is documentation.
A spec that generates mocks, tests, docs, and SDKs is a contract.
The value comes from connecting the file to your development pipeline.
Pitfall 5: Skipping review
A spec in Git without review is just another unchecked file.
Require pull request review for API contract changes, especially changes to:
- request schemas
- response schemas
- status codes
- authentication requirements
- path or query parameters
- enum values
- required fields
A practical adoption plan
You can adopt spec-as-code incrementally.
Step 1: Commit the OpenAPI file
Move the spec into your service repo:
api/openapi.yaml
Commit it like any other source file.
git add api/openapi.yaml
git commit -m "Add OpenAPI contract"
Step 2: Require pull request review
Make API changes go through PRs.
A reviewer should be able to answer:
- Did the endpoint path change?
- Did any required fields change?
- Were new response codes added?
- Is the change backward-compatible?
- Does this require a version bump?
Step 3: Generate one output
Start small. Pick one generated output:
- mocks
- documentation
- contract tests
- SDKs
Do not try to automate everything on day one.
Step 4: Add a spec lint check
Add CI validation so malformed OpenAPI cannot merge.
At minimum, check that:
- the file is valid OpenAPI
- required metadata exists
- paths and operations are well formed
- schemas are valid
Step 5: Add contract tests
Once the spec is stable, test the live service against it.
This closes the loop:
Committed spec -> implementation -> contract test -> CI result
If the API changes without a matching spec update, CI should fail.
Final takeaway
Spec-as-code is a small workflow change with a large impact:
One OpenAPI file
↓
Committed to Git
↓
Reviewed in pull requests
↓
Used to generate mocks, tests, docs, and SDKs
That makes your API contract visible, reviewable, and enforceable.
If you want visual editing and Git sync built into the workflow, try Apidog’s Spec-First Mode and keep your OpenAPI file as the single source of truth.
FAQ
Is “API spec as code” the same as “docs-as-code”?
They use the same principle: keep the artifact in version control and ship it through your normal workflow.
Docs-as-code applies that to documentation. Spec-as-code applies it to the API contract. When documentation is generated from a committed OpenAPI file, the two workflows reinforce each other.
What format should the spec file use?
OpenAPI in YAML is the common choice. YAML is readable, diff-friendly, and supported by many API tools.
JSON also works, but YAML usually produces cleaner pull request diffs.
How do I stop the spec from drifting away from the real API?
Add contract tests to CI. The tests should compare the running service against the committed OpenAPI file.
If an endpoint returns an undocumented field, omits a documented field, or changes a response shape, the build should fail.



Top comments (0)