# 2021-09 · Версионирование API · тройное ревью Статус: **принят к публикации после независимой интеграции**. Исходно эта партия не была подключена к `web/data/editorial-revisions.mjs` и не изменяла `web/data/articles.json`. В revision намеренно нет `date` и `author`: overlay переопределяет только `slug`, `title`, `excerpt`, `contentHtml` и `readingMinutes` у трёх уже существующих архивных записей. ## Состав и границы - `editorial-2021-09-practice-api-versioning`; - `editorial-2021-09-mechanism-api-versioning`; - `editorial-2021-09-field-api-versioning`. Во всех трёх материалах один и тот же учебный endpoint: `POST /api/orders/{orderId}/confirm`. Одна детерминированная fixture работает в одном Node-процессе и использует только Array, Map и объекты. Она не поднимает HTTP, базу, SDK, gateway или настоящий deployment. Вне области пакета: registry, README, `articles.json`, audit-скрипты, очередь, общий standard, Git и любые чужие файлы, включая mascot PNG. ## Проход 1. Структура, тон и объём — пройдено Три текста написаны как М4 сентября 2021 года: короткая техническая речь, границы ownership/reader-а, проверяемое условие и осторожный вывод. В первых двух абзацах каждой статьи названы наблюдаемый разрыв и цена. Нет рассказа о реальном клиенте, деплое, SLA, метрике или инциденте. | Revision | Вопрос статьи | Итоговый body | Редакторский результат | | --- | --- | ---: | --- | | Практика | Как выбрать additive-изменение, объявить поддержку и остановить опасный retirement | **8 274** | endpoint, таблицы, fixture, rollout и маршрут действий есть | | Механизм | Почему request и response проверяются в разные стороны, а schema diff не ловит смену смысла | **8 944** | explicit policy unknown/absence/null и negative cases есть | | Полевой разбор | Как собрать evidence и выбрать pause вместо рискованного rollback | **9 241** | матрица симптомов, evidence packet и rollback-safe action есть | У каждой revision есть не менее пяти смысловых разделов, доступная таблица с `caption`/`thead`, figure с содержательным `alt`/`figcaption`, рабочий JavaScript-пример, нумерованный маршрут «симптом → причина → проверка → действие» и три первичных источника. Первое срабатывание draft gate показало, что в начале practice не было слова-маркера проблемы; текст исправлен до финального запуска, а не исключён из проверки. ## Проход 2. Техника, historical scope и fixture — пройдено Историческая рамка ограничена источниками, доступными не позднее сентября 2021 года: - OpenAPI Specification 3.0.3 от 20 февраля 2020 года; - OpenAPI Specification 3.1.0 от 15 февраля 2021 года; - RFC 8594 `Sunset` от мая 2019 года. Утверждения ограничены тем, что подтверждают эти документы: OpenAPI описывает интерфейс и форму request/response, а не сертифицирует поведение конкретного parser-а; `Sunset` — hint о будущей недоступности, а не гарантия и не замена плана migration. Не смешиваются версия спецификации OpenAPI, версия API, сегмент URL, schema compatibility и API compatibility. Fixture фиксирует следующие проверяемые условия: - v1 reader переживает добавленное optional response-поле `deliveryWindow`, сознательно игнорируя его в своей модели; - v2 reader различает отсутствие поля, `null` и объект окна; - current service принимает v1 request без нового поля и v2 request с допустимым `deliveryPreference`; - `null` в request означает очистку, отсутствие — отсутствие изменения; - опечатка `deliveryPrefrence` отклоняется как unknown request field; - removal обязательного `totalMinor` и semantic change `confirmed → accepted` не проходят compatibility check; - candidate retirement останавливается до изменения: legacy response остаётся, data mutation равна `false`. Во внутреннем техпроходе найдено ещё одно несоответствие: v2 reader наследовал список `ignoredFields` от v1 и мог одновременно читать `deliveryWindow` и показывать его игнорируемым. Reader исправлен: v2 исключает известное поле из списка неизвестных, затем финальная fixture снова выполнена. Это исправление не меняет договор v1 и не выдаёт учебную модель за поведение внешнего клиента. ## Проход 3. Визуал, источники и выпускной preflight — пройдено Три SVG самостоятельны: содержат `title`, `desc`, `role="img"`, не содержат JavaScript, `foreignObject`, inline event handler, external URL или raster data URI. На мобильном рендере 375 px: - rollout показывает пять ворот: contract, additive response, v2 request, failed retirement и pause до изменения; - compatibility-схема отделяет response→client от request→current service; - diagnosis-схема ведёт от evidence к первому нарушенному правилу и обратимому действию. В первом Sharp-рендере заголовок compatibility-схемы обрезался справа. Он был сокращён с «Совместимость = направление + reader» до «Совместимость API: reader и поток», SVG повторно отрендерен и просмотрен: обрезания, overlap и горизонтального overflow не осталось. ## Реальные команды и результаты ```text # cwd: /Users/gavrilovdev/tmp/progcode node --check web/scripts/upgrade-2021-09.mjs # PASS, code 0 # cwd: /Users/gavrilovdev/tmp/progcode/web npm run audit:draft -- scripts/upgrade-2021-09.mjs # PASS: 8 274 / 8 944 / 9 241 body chars # cwd: /Users/gavrilovdev/tmp/progcode node web/scripts/upgrade-2021-09.mjs --verify-fixture # PASS: 12/12 assertions; contract-recorded → additive-response-preflight-passed # → v2-request-preflight-passed → retirement-candidate-rejected # → rollback-safe-pause xmllint --noout \ web/public/assets/editorial/2021/api-versioning-rollout-2021.svg \ web/public/assets/editorial/2021/api-versioning-compatibility-2021.svg \ web/public/assets/editorial/2021/api-versioning-diagnosis-2021.svg # PASS, code 0 # SVG safety: Node readFile scan for