API linting і статичний аналіз
1) Навіщо лінтити API
API - контракт між командами і зовнішніми інтеграторами. Лінтинг і статичний аналіз:- запобігають несумісним і неявним змінам;
- уніфікують статуси, помилки, пагінацію, безпеку;
- роблять специфікації машино-перевіряються і релізи передбачуваними;
- скорочують вартість рев'ю і час онбордингу.
Принцип: "контракти перевіряються автоматично; PR без зеленого лінтингу не мержиться".
2) Об'єкти лінтингу
1. Контракти: OpenAPI/AsyncAPI/GraphQL SDL, Protobuf/Avro/JSON Schema.
2. Реалізація: REST/gRPC-ручки, middleware, коди статусів/заголовки.
3. Інфраструктура: заголовки безпеки, ліміти, політики кешу.
4. Супутні артефакти: приклади (examples), Postman-колекції, схеми помилок.
3) Базові правила для HTTP API (рекомендований профіль)
3. 1 Нотація та URL
snake_case в тілах JSON, kebab-case в шляхах або однаковий kebab-case/'/v1/...'.
Ресурси - множина: '/v1/payments', вкладені - '/v1/wallets/{ id }/transactions'.
Ідентифікатори як path-params: `/v1/payments/{payment_id}` (тип: string, формат: uuid).
3. 2 Методи і статуси
'GET'- 200/206;'POST'- 201 (+'Location'), конфлікти - 409; валідація - 422; ліміти - 429 (+'Retry-After').
Не повертати 200 для помилок. Умовні запити - 304 по'If-None-Match'.
3. 3 Помилки (єдиний формат)
json
{ "code":"validation_error", "message":"amount must be ≥ 1", "trace_id":"...", "details":[{"field":"amount","reason":"min:1"}] }
Обов'язкові: `code`, `message`, `trace_id`; локаль - через'Content-Language'.
3. 4 Пагінація/фільтри
Cursor-based: 'page _ size','page _ token', відповідь: `next_page_token`.
Фільтри та сортування - whitelists, документовані в'parameters'.
3. 5 Безпека
Однобразовий security-схема: OAuth2/OIDC scopes или mTLS; забороняти «http» (лише «https»).
Не повертати чутливі заголовки, маскувати токени в прикладах.
3. 6 Обмеження та розміри
Ліміти заголовків/тіла: 413/414/431; документуйте максимально допустимі значення.
4) Інструменти та екосистема
4. 1 OpenAPI
Spectral (JSON/YAML linт), Redocly linter, oas-diff/openapi-diff (semantic diff), schemathesis/dredd (виконувані перевірки).
4. 2 Protobuf/gRPC
buf (lint + breaking check), protolint, генератори SDK; gnostic для аналізу.
4. 3 GraphQL
graphql-schema-linter, graphql-inspector (breaking).
4. 4 Кодові лінтери і SAST
ESLint, golangci-lint, Detekt/Ktlint, Pylint/Flake8, Semgrep (шаблони API-запахів і security).
5) Приклади правил: Spectral/Redocly
5. 1 Spectral (приклад'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 (фрагмент'.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-профіль
6. 1 `buf. yaml`
yaml version: v2 modules:
- path: proto lint:
use:
- DEFAULT except:
- PACKAGE_VERSION_SUFFIX # используем v1 в package breaking:
use:
- WIRE_JSON deps: []
Рекомендації:
- Не перевикориставати номери полів; видаляються - в «reserved».
- Нові поля - «optional» або з дефолтами; не змінювати типи/семантику.
7) Семантичний diff і «ламаючі» зміни
7. 1 HTTP
Breaking-приклади:- зміна типу/обов'язковості поля;
- видалення статусу/маршруту/параметра;
- звуження enum/діапазону;
- зміна формату id (uuid → string).
- додавання необов'язкових полів;
- нові статуси, що не впливають на happy-path (наприклад, документований'422');
- розширення enum.
7. 2 gRPC/Protobuf
Видалення поля без'reserved '/зміна номера - breaking.
Зміна типу (int32 → string) - breaking.
Додавання нового тегу як optional - зазвичай safe.
8) Зв'язок лінтингу контрактів і коду
Узгодженість забезпечується двома потоками:1. Контракт → код: генерація SDK/серверних заглушок, негативні приклади в тестах.
2. Код → контракт: специфікаційні тести, автоматична перевірка статусів/заголовків.
Semgrep-ідеї:- заборона'return 200'при'error! = nil';
- обов'язковий'Idempotency-Key'на write-маршрутах платежів;
- маскування токенів в логах.
9) CI/CD пайплайн (референс)
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 повинен падати, якщо:
- є breaking-diff;
- порушені базові правила (статуси/безпека/помилки);
- відсутні приклади/описи параметрів.
10) Каталог правил (шаблон для вашої організації)
Ідентифікатори та типи
`_id` — `string`, `format: uuid`.
Грошові поля - «string »/« decimal» з scale; валюта - ISO-4217.
Помилки
Єдина схема (див. § 3. 3), коди: `400/401/403/404/409/422/429/5xx`.
Завжди'trace _ id';'Retry-After'для 429/503.
Пагінація
Тільки cursor; max'page _ size'документований.
Безпека
Всі операції -'security'блок; описані «scopes».
Ні'http:'посилань; TLS 1. 2+.
Кеш/ідемпотентність
Для GET — `ETag/Last-Modified`; для write -'Idempotency-Key'( де застосовується).
Документація
'summary','description', приклади запитів/відповідей (валідні).
11) Приклади автоматизованих перевірок
11. 1 Перевірка обов'язкових заголовків безпеки (Spectral)
yaml security-headers:
given: "$.paths[][].responses['200'].headers"
then:
function: truthy
11. 2 openapi-diff (псевдо CI-крок)
openapi-diff --fail-on-incompatible base.yaml pr.yaml
11. 3 buf breaking-check
buf breaking --against '.git#branch=main'
12) Спостережуваність якості контрактів
Метрики: частка PR з помилками лінтингу, час фіксу, кількість breaking-спроб, «борги» за правилами.
Дашборди: прогрес міграції на уніфіковану схему помилок, покриття прикладами, стабільність версій.
13) Антипатерни
«Док» живе окремо від коду → розсинхронізація. Тримайте контракт поруч з сервісом і випускайте версіонованим артефактом.
Лінтер тільки вручну. Потрібен жорсткий PR-gate.
Випадкові приклади (non-deterministic) - флейки в перевірках.
Відсутність негативних прикладів і кодів помилок.
Перевидобування схеми помилок для кожного сервісу.
Ігнорування Protobuf breaking-перевірок (змінюють теги «на вічко»).
14) Специфіка iGaming/фінансів
Грошові поля - фіксований масштаб/округлення; заборона float.
Обов'язкові заголовки'X-Tenant','X-Region'і трасування'traceparent'.
Платіжні write-ручки: перевірка наявності'Idempotency-Key','Retry-After'і коректних 409/201 семантик.
Вебхуки PSP/KYC: HMAC/mTLS описані в'securitySchemes'; anti-replay («X-Timestamp», вікно).
Регіональні обмеження та локалізація помилок ('Content-Language').
15) Чек-лист prod-готовності
- Spectral/Redocly профілі оформлені і підключені в pre-commit і PR-gate.
- Єдина схема помилок і статуси - зафіксовані і перевіряються.
- openapi-diff/GraphQL Inspector/buf - блокують breaking-зміни.
- Приклади запитів/відповідей валідні; пагінація/фільтри документовані.
- SecuritySchemes і scopes заповнені; http-посилань немає.
- Для Protobuf: 'reserved'на видалених тегах; нові поля - optional.
- Semgrep/лінтери коду включені; маскування секретів в логах.
- CI публікує артефакти контрактів і звіти лінтингу.
- Плейбук: як діяти при breaking-диффі (rollback, hotfix, повідомлення інтеграторам).
16) TL; DR
Впроваджуйте автоматичний лінтинг контрактів (Spectral/Redocly, buf/GraphQL Inspector) і семантичний diff, зафіксуйте єдину схему помилок/статусів/пагінації/безпеки, підключіть PR-gate і публікацію контрактів як Артефакти. Будь-який breaking-дифф - стоп-сигнал. Для грошей/платежів - особливі правила (ідемпотентність,'Retry-After', HMAC/mTLS).