How consistency rules for the API specification get enforced automatically instead of through manual code reviews
An OpenAPI specification checked only occasionally by manual code review drifts into inconsistency over time almost inevitably: mismatched field naming, missing error response definitions, inconsistent path structures. Spectral, a dedicated OpenAPI linting tool, makes these consistency rules machine-checkable and automatically enforceable in the CI pipeline, instead of relying on individual reviewers' attention.
Table of Contents
- 1. Why manual reviews aren't enough for API consistency
- 2. Spectral basics: built-in rulesets as a starting point
- 3. Writing custom rules for project-specific conventions
- 4. Integrating Spectral into the CI pipeline
- 5. Breaking change detection as a complement to pure style linting
- 6. Gradual introduction into an existing, unchecked API
- 7. Securing team buy-in through clear error messages
- 8. Versioning and centrally maintaining the ruleset itself
- 9. Spectral at a glance
- 10. Summary
- 11. FAQ
1. Why manual reviews aren't enough for API consistency
A reviewer checking a new OpenAPI definition needs to keep implicit knowledge of all prior naming conventions, error formats, and structural decisions in their head, which becomes increasingly unrealistic as teams and API surfaces grow. Small inconsistencies (sometimes camelCase, sometimes snake_case, sometimes a missing 404 response definition) creep in gradually, not because of a single review mistake, but because of the sheer volume of details to check.
An automated linting tool like Spectral codifies these conventions once as a machine-readable ruleset and checks every new or changed OpenAPI definition against it consistently and without fatigue, letting reviewers focus on substantive, non-mechanical aspects while stylistic and structural consistency is ensured automatically.
This effect intensifies further once multiple teams work simultaneously on different parts of the same API, since a central, automatically enforced ruleset prevents each team from implicitly developing its own, slightly diverging conventions that would later need to be painstakingly unified again.
2. Spectral basics: built-in rulesets as a starting point
Spectral ships several built-in rulesets, including spectral:oas for general OpenAPI best practices (such as mandatory operationId, mandatory descriptions) and spectral:asyncapi for AsyncAPI specifications. These built-in rules cover a solid baseline but should almost always be supplemented with project-specific rules that reflect the concrete naming conventions and structural decisions of your own API.
Configuration happens via a .spectral.yaml file at the project root, which extends built-in rulesets and adds custom, individual rules, giving a team full control over the actually enforced ruleset instead of relying entirely on a generic default configuration. This file should be versioned like any other project configuration and maintained in the same repository as the OpenAPI specification.
# .spectral.yaml
extends: [[spectral:oas, all]]
rules:
path-must-be-kebab-case:
description: API paths must use kebab-case
given: "$.paths[*]~"
severity: error
then:
function: pattern
functionOptions:
match: "^\/[a-z0-9\-\/{}]+$"
operation-must-have-error-response:
description: Every operation needs a 4xx error response
given: "$.paths[*][*].responses"
severity: warn
then:
field: "4XX"
function: truthy
no-http-verbs-in-path:
description: Paths must not contain HTTP verbs
given: "$.paths[*]~"
severity: error
then:
function: pattern
functionOptions:
notMatch: "(get|create|update|delete)"
3. Writing custom rules for project-specific conventions
The biggest practical benefit of Spectral comes from custom, project-specific rules that go beyond generic best practices: mandatory pagination parameters for all list endpoints, a unified error response schema for all operations, mandatory rate limit header documentation. Each of these rules codifies a design decision the team has already made, but which would otherwise need to be manually re-enforced for every new endpoint without automation.
Spectral rules use JSONPath expressions (given) to select relevant parts of the specification, and functions (then) like truthy, pattern, length, or custom JavaScript functions to perform the actual check. This flexibility allows encoding practically any conceivable structural convention as an automated rule. An iterative development process, where new rules are first tested against a small, representative sample of existing endpoints, prevents an unexpected flood of false positives on the first production run.
4. Integrating Spectral into the CI pipeline
Integration into GitHub Actions or GitLab CI is straightforward: a CI step runs spectral lint openapi.yaml and fails the build on rule violations with severity error, while violations with severity warn don't block the build but appear visibly in the CI log. This graduated severity allows introducing new rules as warnings first, before they become hard errors after a transition period.
It's important to run linting as early as possible in the development process, ideally already as a pre-commit hook or directly in the IDE via a Spectral extension, instead of discovering violations only in the CI run after the push, once the developer's context has already shifted away.
5. Breaking change detection as a complement to pure style linting
Spectral itself primarily checks the style and structure of a single specification version, but doesn't automatically detect whether a change compared to the previous version is backward-compatible. For this task, a separate tool like openapi-diff or oasdiff is usually used, comparing two OpenAPI versions and explicitly distinguishing between breaking and non-breaking changes (say, a removed required field as breaking, a new optional field as non-breaking).
The combination of Spectral for style consistency and a dedicated diff tool for breaking change detection covers two distinct but equally important aspects of API quality assurance and should run as two separate, sequential steps in the CI pipeline.
6. Gradual introduction into an existing, unchecked API
For an already existing, large OpenAPI specification with many historically grown inconsistencies, immediate, full rule enforcement typically produces hundreds of error messages, which tends to discourage rather than motivate the team. A more pragmatic approach is to first apply new rules only to newly added or changed parts of the specification, while leaving existing, unchanged parts exempt from checking for now.
Spectral doesn't directly support this gradual approach out of the box, but can be combined with a Git-diff-based CI script that checks only the actually changed paths of the OpenAPI file against the ruleset, while unchanged legacy areas are temporarily ignored until they get reworked as part of other work anyway.
7. Securing team buy-in through clear error messages
A Spectral rule that only delivers a cryptic error message without context frustrates developers and undermines acceptance of the entire linting process. Every custom rule should therefore include a clear, helpful description that not only describes what's wrong but also what it should look like correctly, ideally with a link to internal API design documentation for more detailed explanations.
A team that treats its Spectral rules as living documentation of design decisions, rather than a pure enforcement tool, additionally benefits from new team members effectively learning the API conventions through the rule error messages themselves, instead of having to read separate documentation.
8. Versioning and centrally maintaining the ruleset itself
With multiple teams each maintaining their own OpenAPI specifications, it's worth publishing the Spectral ruleset itself as its own, versioned npm package, instead of copying it separately into every repository and inevitably letting it drift apart. Each team repository then extends this central package, so rule changes get maintained in a single place and roll out to all dependent projects via a regular package version update.
This centralized approach requires a clear governance process for changes to the shared ruleset, for example via a dedicated API guild team that collects proposals from individual product teams and incorporates them into the central ruleset, instead of letting individual teams change rules uncoordinated.
9. Spectral at a glance
The table below summarizes the key use cases.
| Aspect | Tool | Purpose |
|---|---|---|
| Style and structure | Spectral with built-in and custom rules | Consistent naming conventions, mandatory fields |
| Breaking changes | openapi-diff or oasdiff | Detection of backward-incompatible changes |
| CI integration | GitHub Actions / GitLab CI | Automated checking on every pull request |
| IDE integration | Spectral VS Code extension | Immediate feedback during development |
Mironsoft
OpenAPI design, Symfony APIs, and API security
APIs that external teams can integrate without back-and-forth questions?
We review existing REST APIs for inconsistent error formats, missing OpenAPI documentation, and security gaps, then build an API that is clearly documented, versioned, and hardened against abuse.
API Review
Checking the OpenAPI spec, error formats, and status codes for consistency.
Symfony Implementation
Using DTOs, Serializer, and Validator for clean, type-safe request/response models.
Security Audit
Hardening rate limiting, auth schemes, and input validation against real attack surfaces.
10. Summary
Spectral Linting: The Essentials at a Glance
Why linting
Manual reviews aren't enough to reliably enforce consistency as the API surface grows.
Custom rules
The biggest benefit comes from project-specific rules that codify already-made design decisions.
CI mandatory
Severity error blocks the build on violations, severity warn allows a gradual rollout.
Complements breaking-change tools
Spectral checks style, a separate diff tool checks backward compatibility between versions.