Версионирование API: реальные примеры команд
Twilio поддерживает 14 активных версий API. Stripe фиксирует каждого клиента на версии, активной на момент регистрации, и держит версии аж с 2011 года. GitHub REST параллельно ведёт три major-версии и шлёт deprecation-заголовки за 12 месяцев до отключения. Ваша команда, скорее всего, пытается выжить с одной — и спорит, куда пихать версию: в URL, header или Accept.
Этот спор на самом деле — три разных решения, склеенных в один аргумент: где живёт версия, как скоупятся breaking changes и когда старые версии умирают. Правильный ответ на одно не спасёт, если два других кривые. Это плейбук на основе того, как реально работают компании с публичным API на скейле, плюс что мы видим внутри PanDev Metrics у клиентов с внутренними API на 20-200 потребителей.
{/* truncate */}
Проблема: стратегии версионирования, которую никто не записал
У большинства внутренних API нет «стратегии». Есть три геологических слоя истории:
- Оригинальные endpoints, написанные до того, как кто-то подумал про версии
- Префикс
/v2/, добавленный после первого breaking change - Header
X-API-Version: 2026-01-15, появившийся после того, как кто-то прочитал блог Stripe
Клиенты бьются во все три одновременно. Новая SDK ломает мобильных клиентов на v1. Deprecation объявлен в Slack, но в OpenAPI-спеке этого нет. Команда тратит три спринта на распутывание.
Postman State of the API Report 2024 показывает: 58% разработчиков назвали breaking changes главной болью интеграции — выше, чем качество доков, rate limits и сложность auth. В API guidelines Microsoft (Brandon Werner, Mark Stafford) прямым текстом: «Breaking changes должны сопровождаться инкрементом версии». Команды, которые пропускают инкремент, шипят инциденты.
Дебаты не про «какую схему версионирования взять», а про «что изменилось и кого это затронет». Схема — это сантехника; скоуп — это сложная часть.
Где живёт версия: три варианта, конкретные компромиссы
Версия в пути URL (/v1/users)
Используют: Twilio, GitLab, AWS (для большинства сервисов), Kubernetes
| Плюсы | Минусы |
|---|---|
| Видно в логах и ошибках | Связывает routing с версионированием |
| Легко роутить через load balancer / gateway | Провоцирует «big bang» major-версии |
| Не нужен tooling на клиенте | Ломает REST-пуризм («у ресурса один URI») |
| Дебажится curl-ом в одно действие | Сложно делать per-field deprecation |
Когда брать: внутренние API, B2B-платформы с небольшим числом major-версий, gateway-heavy архитектуры.
Версия в заголовке (Accept: application/vnd.api+json; version=3)
Используют: GitHub (до 2022), Atlassian, Azure
| Плюсы | Минусы |
|---|---|
| URI стабильны и «REST-ovые» | Не видно в браузере, дебажить сложнее |
| Fine-grained версионирование на каждый запрос | Сложнее роутить на gateway |
| Поддерживает dual-shipping ответов | Клиенты должны знать танец; SDK обычно это прячут |
Когда брать: публичные API, где стабильность URL важна для SEO/закладок, или где нужно держать несколько версий на одном endpoint.
Date-based (Stripe-Version: 2024-06-20)
Используют: Stripe, Shopify (REST), часть Google API
| Плюсы | Минусы |
|---|---|
| Клиент фиксируется на регистрации; никто не «решает» апгрейдиться | Нужна серьёзная инфраструктура совместимости на сервере |
| Мелкие breaking changes можно шипать еженедельно без фанфар | Сложно поднять, если вы не Stripe-масштаба |
| «Latest» становится opt-in, а не дефолтом | Сложнее объяснить, что поменялось между 2024-06-20 и 2024-07-10 |
Когда брать: SaaS-платформы с долго живущими интеграциями, где клиенту не хочется трогать работающий код. Требует серьёзных вложений в compat-слой — инженерный блог Stripe признаёт, что у них на это выделена отдельная команда.
GraphQL «без версий»
Используют: GitHub GraphQL, Shopify Storefront API, Meta
GraphQL-адепты утверждают, что версии — это антипаттерн: клиент выбирает поля, сервер помечает старые @deprecated, поля сосуществуют, пока кто-то их запрашивает. Работает для read-heavy API с умными клиентами. Не работает для mutations с меняющейся семантикой (например, авторизация платежей) — там нужны явные версии даже внутри GraphQL.
Как скоупятся breaking changes: вопрос, который большинство пропускают
Скоуп важнее схемы. Четыре класса изменений в порядке нарастания боли:
| Класс | Пример | Бампать версию? |
|---|---|---|
| Additive (новое опциональное поле / endpoint) | Добавили customer.preferred_language | Нет |
| Additive с новым обязательным input | Новый endpoint требует новый auth header | Минорно (soft) |
| Semantic (то же поле, другой смысл) | amount стал post-tax вместо pre-tax | Major — всегда |
| Removal / rename поля или endpoint | Убрали user.legacy_id | Major — всегда |
Первые два большинство делает правильно, последние два — криво. Semantic change без бампа версии — худший тип бага: ответ валидируется, парсится, сериализуется, но молча ломает математику downstream. Мы видели, как такое у клиента-ecommerce сломало финансовый отчёт на 3 недели — команда зашипила breaking semantic change как «minor bug fix».
Правило: если меняется семантика поля на сервере, это новое поле. Вводите amount_post_tax рядом с amount, помечаете amount deprecated, убираете после окна. Не «чините» amount на месте.
Deprecation window: математика, которая меняет решения
Про это спорит каждая API-команда. Реальные числа из публичных постмортемов:
| Компания | Deprecation window | Способ оповещения |
|---|---|---|
| Stripe | 12+ месяцев (minor) / несколько лет (major) | Email на аккаунт + dashboard + changelog |
| Twilio | Минимум 12 месяцев | Changelog + email + sunset header |
| GitHub REST | Обычно 18 месяцев | Deprecation-заголовки + блог + docs warning |
| Atlassian Cloud | Минимум 6 месяцев | Deprecation notice на endpoint + email клиентам |
| Google Maps Platform | Обычно 12 месяцев | Changelog + deprecation header |
Floor для платного B2B API в 2026 — 12 месяцев. Меньше — будут инциденты у клиентов; инциденты у клиентов — churn. Исключение — внутренний API с известными потребителями, где можно координироваться напрямую: окна 30-90 дней ок, если у вас есть Slack до каждой потребляющей команды.
Контринтуитивно: длина окна важна меньше, чем сигнал. Окно в 6 месяцев с Sunset: header, Deprecation: header, еженедельными напоминаниями и dashboard у клиента работает лучше, чем 12 месяцев, которых никто не замечает. RFC 8594 (Sunset HTTP Header) — ближайший стандарт; внедряйте его рано.
Шаблон политики для команды 10-100 инженеров
Это то, что мы рекомендуем клиентам, когда у внутренних API появляется >5 потребителей:
1. Схему выбираете один раз на API и записываете
- Сервис-сервис внутри → версия в URL
- Внешний SDK / клиентский API → date-based ИЛИ URL (одно из двух, не оба)
- Публичный read-heavy граф → GraphQL с
@deprecated
2. Политика breaking changes (стабильные правила)
- Additive шипится в текущей версии
- Rename / removal — новая версия И окно сосуществования
- Semantic — новое поле, а не правка на месте
3. Коммуникация deprecation (обязательно для breaking)
- Response header
Deprecation: true Sunset: Sat, 31 Dec 2026 23:59:59 GMT(формат RFC 8594)- Запись в changelog в момент анонса и в момент sunset
- Dashboard-метрика: вызовы к deprecated-версиям по клиенту
4. Процесс sunset (автоматизация, не тикеты)
- Feature flag, который возвращает 410 Gone после sunset-даты
- Вызовы до sunset логируются в выделенный канал
- Customer-success на T-90 и T-30 дней
Типовые ошибки
- Анонс в блоге, который никто не читает. Deprecation живёт в ответе API, а не в контент-маркетинге. Если ответ не кричит «я уйду», инженерные команды узнают в день отключения.
- Версионировать всё подряд. Rename одного внутреннего endpoint — не повод делать v2-всего. Версия скоупится на namespace.
- 5+ активных major-версий. Twilio это вывозит, но инвестиции у них такие, что большинство команд не потянут. Три активные major — практический потолок для команды в 20 человек.
- Тихие breaking changes под видом bug fix. «Мы починили расчёт налога» — поздравляем, вы только что выпустили major без migration path.
- Версия одновременно в URL И в header. Выбирайте одно. Два — неоднозначно, клиенты будут спорить, кто побеждает.
Как измерять, что версионирование работает
Три реально полезные метрики:
| Метрика | Здорово | Внимание | Плохо |
|---|---|---|---|
| % запросов на последней версии | >70% | 40-70% | <40% |
| Ср. возраст (дни) самой используемой версии | <180 | 180-365 | >365 |
| Инциденты из-за непрокоммуницированных breaking changes | 0/квартал | 1-2/квартал | 3+/квартал |
PanDev Metrics не видит объёмы ваших API-запросов напрямую — наш датасет это IDE + Git, не gateway-логи. Зато мы видим, как version bumps превращаются в инженерную работу: время на поддержку backward-compat слоя, lead time между «deprecated» и «removed» коммитами, сколько раз PR с версией отбивается от ревью. У команд с чёткой политикой версионирования lead time на API-change PR на 30-40% короче, чем у команд, которые спорят о схеме прямо в ревью. Это перекликается с исследованиями DORA — ясность процесса сокращает cycle time сильнее, чем сырая скорость.
Связка с code review прямая: ревьюеры, знающие политику, не спорят; они её применяют. Ревьюеры, не знающие политики, пишут комменты на 400 слов, чтобы её выработать.
Честный лимит
Наша сила — IDE-телеметрия; мы не видим gateways и клиентские интеграции. Цифры по deprecation window — из публичных инженерных блогов и RFC 8594, не из нашей телеметрии. Зато мы коррелируем инженерные усилия с API-change коммитами, и паттерн ясный: команды с записанной политикой тратят вдвое меньше времени на API-change PR.
Ещё честность: если вы 5 человек, делаете greenfield внутренний API с 2 потребителями — пропустите большинство этого. URL-версионирование, бампайте, когда надо, и не стройте фреймворк для проблемы, которой у вас пока нет. Цена преждевременной инфраструктуры реальна.
Самый жёсткий тезис
Спор о схеме версионирования — это прокси для настоящего спора: «есть ли у нас политика breaking changes?» Команды без политики будут часами спорить URL-vs-header и всё равно зашипят breaking changes без бампа. Команды с политикой год проживут на одной схеме и поймут, что схема-то и не решала. Напишите политику раньше, чем выбираете сантехнику.
По теме
- DORA метрики: полное руководство — lead time и change failure rate применительно к API
- Code Review Checklist: 11 правил, которые сокращают ревью вдвое — шаг, где политика версионирования начинает работать
- Внешнее: RFC 8594 — The Sunset HTTP Header Field — внедряйте рано, поблагодарите себя позже
- Внешнее: Stripe API Versioning — глубокий разбор date-based compat-слоя от Amy Lin
