Версионирование API: подходы и цена совместимости
Версия API защищает клиентов от внезапных изменений, но добавляет маршруты, тесты, документацию и поддержку старых контрактов. Сравниваем URL, заголовки и медиатипы и разбираем, где каждый подход начинает дорого обходиться.

Версионирование API не делает изменения бесплатными: после выпуска второй версии команда часто поддерживает два контракта, две матрицы тестов и несколько вариантов документации. Ниже — практическое сравнение подходов для HTTP API, ориентированное на OpenAPI 3.1 и современные REST-сервисы: что выбрать для нового проекта, как считать цену совместимости и где версия начинает мешать эксплуатации.
Что именно вы версионируете
Версия должна описывать публичный контракт, а не состояние исходного кода. Контракт включает URL, HTTP-методы, параметры, формат запроса и ответа, коды ошибок, правила авторизации, ограничения по частоте запросов и смысл полей.
Изменение названия внутреннего класса не требует новой версии, если внешний ответ не поменялся. А удаление поля, изменение его типа или перевод обязательного параметра в другой формат — это потенциально несовместимое изменение, даже если правка заняла одну строку.
Совместимое изменение не ломает корректно написанного клиента. Добавление необязательного поля обычно относят к таким изменениям, но полагаться на это можно только после проверки реальных клиентов: строгий десериализатор способен упасть даже на новом поле.
Версия — это не номер релиза сервера. Это обещание клиенту: такой набор правил будет работать достаточно долго, чтобы он успел перейти на новый контракт.
Сноска. Контракт API — формальное описание того, как клиент и сервер обмениваются данными. Для HTTP API его часто хранят в OpenAPI-документе, но сама спецификация не заставляет выбирать конкретную схему версионирования.
Четыре подхода и их цена
Версия в URL
Клиент обращается к адресам вроде /api/v1/orders и /api/v2/orders. Это самый заметный вариант: версию видно в логах, настройках прокси, документации и браузерных запросах.
Главный плюс — простая маршрутизация и диагностика. Можно отдельно ограничивать трафик, кэшировать ответы и считать ошибки по каждой версии. Главная цена — URL становится частью публичного контракта. При переходе на новый формат появляются параллельные обработчики, тесты и страницы документации.
Версия в пути хорошо подходит для публичного REST API, которым пользуются разные команды или внешние разработчики. Не стоит создавать новую версию из-за каждого добавленного необязательного поля: иначе API быстро превратится в набор почти одинаковых маршрутов.
Версия в заголовке
Один URL обслуживает разные контракты, а клиент передаёт требуемую версию в HTTP-заголовке. Это может быть отдельный заголовок вроде Api-Version или согласованное значение в заголовке Accept.
Подход сохраняет чистые URL и позволяет точнее управлять форматом ответа. Но версия хуже видна человеку: обычная ссылка из лога не объясняет, какой контракт вызвал ошибку. Прокси, CDN и кэш должны учитывать заголовок при разделении ответов. Если это не настроить, клиент может получить закэшированный ответ другой версии.
Такой вариант разумен для внутренней платформы, где единый клиентский SDK централизованно добавляет заголовок. Для открытого API нужно заранее проверить, как документация, инструменты тестирования и сторонние интеграторы будут задавать эту версию.
Версия через медиатип
Клиент указывает нужный формат в заголовке Accept, например через параметр медиатипа. URL остаётся прежним, а сервер выбирает представление ресурса по заголовку.
Это наиболее аккуратная модель с точки зрения HTTP-семантики: версия относится именно к представлению ресурса, а не к его адресу. Цена — более высокий порог входа. Команде нужно правильно настроить согласование форматов, документацию и кэширование. Ошибка в заголовке может привести к ответу по умолчанию, который клиент не умеет обрабатывать.
Медиатип оправдан, если команда уже уверенно работает с HTTP-кэшами и явно разделяет представление ресурса и сам ресурс. Для простого B2B API его преимущества нередко не окупают дополнительную сложность.
Версии по датам или снимкам контракта
Вместо v1 клиент выбирает дату или зафиксированный снимок API. Такой подход встречается у платформ, где изменения выходят регулярно, а клиенту нужно надолго закрепить поведение.
Дата хорошо показывает, к какому состоянию контракта привязан клиент. Но она не отвечает на вопрос, какие именно изменения вошли в снимок: без журнала изменений и машиночитаемой схемы название даты мало что объясняет. Серверу приходится хранить правила преобразования для множества дат, а не для двух-трёх крупных поколений.
| Подход | Что выигрывает команда | Основная цена | Кому подходит |
|---|---|---|---|
| Версия в URL | Видимость, простая маршрутизация, понятные логи | Параллельные маршруты и документация | Публичный REST API, внешние клиенты |
| Заголовок | Один URL, гибкое переключение формата | Сложные логи, кэш и ручное тестирование | Внутренняя платформа, общий SDK |
| Медиатип | Чёткое разделение ресурса и представления | Сложное согласование и отладка | Команда, хорошо знающая HTTP |
| Дата или снимок | Точная фиксация контракта для клиента | Рост числа поддерживаемых вариантов | Платформы с частыми изменениями |
Цена версии: считать нужно не только разработку
Сделать новый обработчик обычно проще, чем потом поддерживать его годами. Перед выбором схемы посчитайте пять потоков работы:
- Код. Нужно ли дублировать контроллеры, схемы валидации и преобразование ответа или достаточно отдельного адаптера.
- Тесты. Для каждой активной версии нужны проверки успешных сценариев, ошибок, авторизации и ограничений по запросам.
- Документация. Клиент должен видеть, чем отличаются контракты, какие поля устарели и куда переходить.
- Наблюдаемость. Метрики и логи должны разделять версии, иначе нельзя понять, кто создаёт ошибки и сколько трафика останется после отключения старого контракта.
- Поддержка. Нужно отвечать на вопросы пользователей старой версии и держать обратную совместимость в базе данных, очередях и фоновых задачах.
Особенно дорого обходится версия, которая проходит через всю систему до хранилища. Если разница касается только формата ответа, лучше оставить единую внутреннюю модель и преобразовывать её на границе API. Если меняется бизнес-смысл операции или жизненный цикл данных, простой слой преобразования может скрыть несовместимость и создать ошибки в заказах, платежах или правах доступа.
Как выбрать подход
Выбирайте версию в URL, если API публичный, клиентов много, а команда хочет быстро находить несовместимые вызовы в логах. Это не самый элегантный, но обычно самый дешёвый в эксплуатации вариант.
Выбирайте заголовок, если все клиенты контролируются одной организацией и общий SDK может добавлять нужное значение автоматически. До запуска проверьте CDN, reverse proxy и правила кэширования: ключ кэша обязан учитывать версию.
Выбирайте медиатип, если у команды уже есть дисциплина работы с согласованием представлений и строгая HTTP-инфраструктура. Если разработчики постоянно тестируют запросы вручную по URL, скрытая в заголовке версия увеличит время диагностики.
Версии по датам оправданы, когда изменения выпускаются часто и клиентам нужен точный снимок поведения. В этом случае обязательны журнал изменений, архив схем и понятное правило: какие даты ещё поддерживаются.
Что ломается чаще всего
Новая версия меняет только ответ, но не ошибки
Команда обновляет структуру успешного ответа, но оставляет старый формат ошибок без документации. Клиент обрабатывает код ответа, а затем не может разобрать тело ошибки. Версия должна описывать оба пути: успех и отказ.
Старую версию отключают без измерения трафика
Наличие документации не означает, что клиентов больше нет. Считайте запросы по версии, приложению, ключу или идентификатору клиента. Отдельно отслеживайте фоновые задачи и мобильные приложения: они могут обращаться к старому контракту месяцами.
Совместимость проверяют только интеграционными тестами
Интеграционные тесты покрывают известные сценарии, но не гарантируют, что новый ответ принимают все реальные клиенты. Добавьте проверку схемы в CI, тесты для обязательных полей и контрактные тесты между владельцем API и потребителем.
Версия есть в URL, но нет политики удаления
Без даты прекращения поддержки старая версия становится постоянной. Для каждой версии заранее зафиксируйте срок предупреждения, канал уведомлений, критерий отключения и ответственного за миграцию. Срок зависит от типа клиентов и договора, поэтому универсальное число здесь не работает.
Минимальная политика для нового API
- Опишите контракт в OpenAPI 3.1 и отдельно перечислите несовместимые изменения.
- Выберите один механизм версии для всех ресурсов. Не смешивайте URL для одних методов и заголовок для других без сильной причины.
- Разделите внутреннюю модель и публичное представление, если версии отличаются только полями ответа.
- Добавьте в логи версию, идентификатор клиента, маршрут, код ответа и длительность запроса.
- Опубликуйте правила устаревания: как клиент узнает о новой версии, сколько живёт старая и где лежит миграционная инструкция.
- Перед отключением проверьте фактический трафик и получите подтверждение от владельцев оставшихся интеграций.
Устаревание — не просто объявление о будущем удалении. Это управляемый период, в который старый контракт ещё работает, но команда перестаёт расширять его и направляет клиентов на замену.
Что делать дальше
Возьмите один реальный endpoint и составьте таблицу изменений: какие поля добавляются, удаляются или меняют смысл, какие клиенты его вызывают и сколько запросов приходится на каждую версию. Затем проверьте, умеют ли ваши прокси, CDN, система логирования и OpenAPI-пайплайн различать выбранные варианты.
После этого зафиксируйте политику совместимости в репозитории рядом со схемой API. В продакшене первым показателем для контроля станет не число версий, а доля трафика старого контракта и количество ошибок миграции. Именно они показывают реальную цену решения.
Создатель презентаций: Структура и текст каждого слайда: заголовки-утверждения, одна мысль на слайд, заметки докладчику.
Спросить за 150 ₽Источники: Microsoft REST API Guidelines, Stripe API versioning
Частые вопросы
Нужно ли создавать новую версию при добавлении поля в JSON?
Что выбрать для публичного REST API: URL или заголовок?
Что делать, если у меня другая версия API-шлюза или прокси?
Что делать, если клиент получает ответ не той версии?
Можно ли поддерживать несколько версий в одном обработчике?
Есть ли риск отключить старую версию слишком рано?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.