API lining and static analysis
1) Why link API
API - a contract between teams and external integrators. Linting and static analysis:- Prevent incompatible and implicit changes
- unify statuses, errors, pagination, security;
- make specifications machine-verifiable and releases predictable;
- reduce the cost of review and onboarding time.
Principle: "contracts are checked automatically; PR without green linting does not hold."
2) Linting facilities
1. Contracts: OpenAPI/AsyncAPI/GraphQL SDL, Protobuf/Avro/JSON Schema.
2. Implementation: REST/gRPC pens, middleware, status codes/headers.
3. Infrastructure: security headers, limits, cache policies.
4. Related artifacts: examples, Postman collections, error schemes.
3) Basic rules for HTTP API (recommended profile)
3. 1 Notation and URL
snake_case in JSON bodies, kebab-case in paths, or uniform kebab-case/'/v1/... '.
Resources - plural: '/v1/payments', nested - '/v1/wallets/{ id }/transactions'.
Identifiers as path-params: '/v1/payments/{ payment _ id} '(type: string, format: uuid).
3. 2 Methods and statuses
'GET '- 200/206;' POST '- 201 (+' Location '), conflicts - 409; validation - 422; limits - 429 (+ 'Retry-After').
Do not return 200 for errors. Conditional Queries - 304 by'If-None-Match '.
3. 3 Errors (single format)
json
{ "code":"validation_error", "message":"amount must be ≥ 1", "trace_id":"...", "details":[{"field":"amount","reason":"min:1"}] }
Required: 'code', 'message', 'trace _ id'; locale - via 'Content-Language'.
3. 4 Pagination/filters
Cursor-based: `page_size`, `page_token`, ответ: `next_page_token`.
Filters and sorting - whitelists documented in 'parameters'.
3. 5 Safety
Uniform security scheme: OAuth2/OIDC scopes or mTLS; deny 'http' (only 'https').
Do not return sensitive headers, mask tokens in examples.
3. 6 Limitations and dimensions
Title/body limits: 413/414/431; Document the maximum allowed values.
4) Tools and ecosystem
4. 1 OpenAPI
Spectral (JSON/YAML lint), Redocly linter, oas-diff/openapi-diff (semantic diff), schemathesis/dredd (checks in progress).
4. 2 Protobuf/gRPC
buf (lint + breaking check), protolint, SDK generators; gnostic for analysis.
4. 3 GraphQL
graphql-schema-linter, graphql-inspector (breaking).
4. 4 Code Linters and SAST
ESLint, golangci-lint, Detekt/Ktlint, Pylint/Flake8, Semgrep (API odor and security templates).
5) Rule examples: Spectral/Redocly
5. 1 Spectral (example 'spectral. yaml`)
yaml extends: ["spectral:oas", "spectral:asyncapi"]
rules:
openapi-tags: off info-contact: error no-http: error path-kebab-case:
description: "Paths must be kebab-case"
given: "$.paths[]~"
severity: error then:
function: pattern functionOptions: { match: "^/(?:[a-z0-9]+(?--[a-z0-9]+)/?)+$" }
response-error-schema:
description: "Error responses must use standard schema"
given: "$.paths[][].responses[?(@property >= '400')]"
then:
field: "content.application/json.schema.$ref"
function: truthy id-as-uuid:
given: "$..parameters[?(@.name =~ /.id$/i)]"
then:
field: schema.format function: enumeration functionOptions: { values: ["uuid"] }
5. 2 Redocly (fragment '.redocly. yaml`)
yaml apis:
main: openapi.yaml lint:
extends:
- recommended rules:
no-ambiguous-paths: error operation-2xx-only: off operation-success-response:
severity: error where:
subject: response filterInParentKeys: ["200","201","204"]
operation-security-defined: error no-plain-http: error
6) Protobuf/gRPC: buf profile
6. 1 `buf. yaml`
yaml version: v2 modules:
- path: proto lint:
use:
- DEFAULT except:
- PACKAGE_VERSION_SUFFIX # используем v1 в package breaking:
use:
- WIRE_JSON deps: []
Recommendations:
- Do not reuse field numbers; deleted - in'reserved '.
- New fields - 'optional' or with defaults; do not change types/semantics.
7) Semantic diff and "breaking" changes
7. 1 HTTP
Breaking examples:- Change field type/mandatory
- Delete status/route/parameter
- enum/range narrowing;
- change of id (uuid → string) format.
- Add optional fields
- New statuses that do not affect the happy path (for example, documented '422')
- enum extension.
7. 2 gRPC/Protobuf
Deleting a field without'reserved '/renumbering - breaking.
Type change (int32 → string) - breaking.
Adding a new tag as optional is usually safe.
8) Contract and Code Linking
Consistency is provided by two threads:1. Contract → code: generation of SDK/server stubs, negative examples in tests.
2. Contract → code: specification tests, automatic check of statuses/headers.
Semgrep ideas:- disallowing'return 200'when'error! = nil';
- mandatory'Idempotency-Key 'on write payment routes;
- masking tokens in logs.
9) CI/CD pipeline (reference)
pre-commit: spectral lint, redocly lint
PR gate: openapi-diff (base..PR), buf breaking-check, graphql-inspector build: schemathesis smoke, unit/integration linters (ESLint/golangci-lint)
release: publish contracts (artifact/broker), sign & tag
PR shall fall if:
- there is breaking-diff;
- Basic rules have been violated (statuses/security/errors)
- there are no examples/descriptions of parameters.
10) Rules catalog (template for your organization)
Identifiers and types
`_id` — `string`, `format: uuid`.
Money fields - 'string '/' decimal' with scale; currency - ISO-4217.
Mistakes
Unified scheme (see § 3. 3), codes: '400/401/403/404/409/422/429/5xx'.
Always' trace _ id ';' Retry-After'for 429/503.
Pagination
Cursor only; max'page _ size'is documented.
Safety
All operations - 'security' block; 'scopes' are described.
No 'http:' links; TLS 1. 2+.
Cache/idempotency
Для GET — `ETag/Last-Modified`; for write - 'Idempotency-Key' (where applicable).
Documentation
'summary ',' description ', examples of requests/responses (valid).
11) Examples of automated checks
11. 1 Verification of mandatory security headers (Spectral)
yaml security-headers:
given: "$.paths[][].responses['200'].headers"
then:
function: truthy
11. 2 openapi-diff (pseudo CI step)
openapi-diff --fail-on-incompatible base.yaml pr.yaml
11. 3 buf breaking-check
buf breaking --against '.git#branch=main'
12) Observability of quality of contracts
Metrics: share of PRs with linking errors, fix time, number of breaking attempts, "debts" according to the rules.
Dashboards: migration progress to the unified error scheme, coverage with examples, version stability.
13) Antipatterns
"Doc" lives separately from the code → desynchronization. Keep the contract close to the service and release a versioned artifact.
Linter only by hand. Need a hard PR-gate.
Random examples (non-deterministic) - flakes in checks.
No negative examples and error codes.
Reinvention of the error scheme for each service.
Ignoring Protobuf breaking checks (changing tags "by eye").
14) Specifics of iGaming/Finance
Currency fields - fixed scale/rounding; float prohibition.
Mandatory headers' X-Tenant ',' X-Region'and trace' traceparent '.
Payment write-handles: checking for 'Idempotency-Key', 'Retry-After' and correct 409/201 semantics.
Webhooks PSP/KYC: HMAC/mTLS are described in 'securitySchemes'; anti-replay ('X-Timestamp', window).
Regional restrictions and error localization ('Content-Language').
15) Prod Readiness Checklist
- Spectral/Redocly profiles are designed and connected in pre-commit and PR-gate.
- Single error pattern and statuses - committed and checked.
- openapi-diff/GraphQL Inspector/buf - block breaking changes.
- Examples of requests/responses are valid; pagination/filters documented.
- SecuritySchemes and scopes are filled; There are no http links.
- For Protobuf: 'reserved' on deleted tags; new fields - optional.
- Semgrep/code linters enabled; masking secrets in logs.
- CI publishes contract artifacts and linting reports.
- Playbook: how to act when breaking-diffa (rollback, hotfix, notifications to integrators).
16) TL; DR
Implement automatic contract linting (Spectral/Redocly, buf/GraphQL Inspector) and semantic diff, fix a single error/status/pagination/security scheme, connect PR-gate and publish contracts as artifacts. Any breaking diff is a brake light. For money/payments - special rules (idempotency, 'Retry-After', HMAC/mTLS).