Safeguard
Developer Tools•

Code-first or spec-first OpenAPI: which gives scanners better input?

Code-first descriptions match what is deployed but miss intent. Spec-first files state intent but can drift. How to pick and what to check for a scanner.

Safeguard Security Team
4 min read

Neither is better on its own. A code-first description, generated from your framework, tracks the routes that exist but only records what annotations say. A spec-first file states intent but can drift from the code. For scanning, use whichever you can keep accurate, and compare the two in CI.

Definitions

Code-first means the description is generated from code at runtime or build time. FastAPI does this by default. Spring generates one through springdoc and NestJS through @nestjs/swagger and decorators, as covered in which frameworks serve docs by default.

Spec-first means a person writes and reviews the OpenAPI file, and code is built or checked against it. Rails API mode, which generates no description in the base framework, often lands here by necessity.

Comparison

AspectCode-firstSpec-first
Matches deployed routesUsually yesOnly if kept in sync
Captures security intentOnly what annotations or dependencies declareWhatever the author wrote
Review in pull requestsOutput changes are noisySmall readable diffs
Risk of driftLow for routes, high for descriptions of behaviorHigh
Scanner coverageGood for existing routesGood if complete, but may list routes that do not exist
Runs without exposing docs in productionNeeds an export stepAlready a file

What a scanner needs from either

The scanner needs routes, parameters, request bodies and security entries. The OpenAPI Specification says servers defaults to a single entry with a url of a slash if the field is absent or empty, and that an operation's security replaces the root one. Check both in whichever file you hand over. OWASP also advises generating documentation automatically from open standards as part of the CI/CD pipeline, which favors code-first plus a build artifact.

A hybrid that works

  1. Generate the description from code in CI and save it as an artifact.
  2. Commit a reviewed copy or a baseline.
  3. Diff the two on every pull request.
  4. Fail the build on unexplained changes, such as a new route or a newly empty security entry. The empty security array post explains that last case.
  5. Feed the generated file, not the live endpoint, to scanners, so production docs can stay off.

Steps to choose

  1. Does your framework generate a description? If yes, start code-first.
  2. Do request bodies and responses carry types in code? If not, the output will be thin, so add annotations or models.
  3. Do you have a design review process for APIs? If yes, spec-first may fit and the diff is the review.
  4. Whatever you choose, set an owner and a check. Unowned files rot within a few releases.
  5. Run a scan and measure coverage: how many documented operations received a test request, and how many deployed routes are missing from the file.

More on drift: OpenAPI spec drift, how to detect and fix it and writing an OpenAPI spec that helps security testing.

Try it yourself

Click through it at your own pace, or open it full screen. Notice how coverage depends on what the scan was given.

Safeguard's SBOM and dependency scanning is a companion step, and a free scan is available.

Quick check

Can a generated description be wrong?

Yes. It reflects code annotations, so missing metadata produces missing or empty entries.

Should scanners read the live docs endpoint?

They can, but exporting the file in CI lets you keep production docs disabled.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.