01.08.2026

API меняется без версий: как не ломать партнёров

Мобильное приложение перестало работать после выкладки — нет версий API /v1 и /v2.

Бэкенд выкатили вечером. Утром мобильное приложение перестало логиниться у части пользователей. Поддержка тонет, маркетинг спрашивает про «падение конверсии», разработчики видят в логах 400/404 на полях, которых «уже нет». Причина простая и дорогая: breaking change в API без версии. Старые клиенты ждали /v1, прод отдал новую форму ответа как единственную истину.

Мобильные клиенты обновляются медленно. Магазины приложений, пользователи без автообновления, корпоративные устройства — всё это означает: сервер обязан жить с несколькими поколениями контракта одновременно. Без versioning стратегии каждый смелый релиз бэкенда — потенциальный простой на клиентах.

Как выглядят breaking changes на практике

Удалили или переименовали поле. Клиент парсит строго — падает. Или молча теряет данные и показывает пустые экраны.

Сменили типы и семантику: строка стала объектом, статус изменил словарь значений, дата сменила формат. «Почти совместимо» для сервера = полностью сломано для клиента.

Переехали URL без редиректа и без периода двойной поддержки. Старые сборки бьют в пустоту.

Поменяли ошибки и коды авторизации. Приложение не умеет обновить токен по новому сценарию — пользователь «разлогинен навсегда» до ручного обновления.

Почему команды ломают клиентов «случайно»

Нет явного контракта и потребителя как роли. API считают внутренним деталем, хотя им пользуются store-сборки.

Нет классификации изменений: additive vs breaking. Всё тащат в «просто поправили».

Нет периода deprecation. Старое выключили в тот же день, когда включили новое.

Тестируют только свежий клиент. Регрессии на предыдущих major-версиях приложения не входят в пайплайн.

Часто breaking change маскируют под «рефакторинг для удобства фронта». На веб-фронте, который деплоится вместе с API, это проходит. На мобильном клиенте с недельным циклом стора — нет. Правило простое: если потребитель обновляется независимо от сервера, совместимость — продуктовое требование, а не эстетика JSON.

Партнёрские интеграции усиливают эффект. Один «небольшой» rename поля может уронить обмен с логистикой или витриной партнёра, о котором вспомнили только после инцидента. Реестр потребителей API должен существовать так же явно, как реестр серверов.

Стратегии версионирования, которые спасают мобильные клиенты

URL-версии: /api/v1, /api/v2. Просто объяснить бизнесу и шлюзу. Минус — размножение маршрутов; плюс — явная изоляция.

Версия в заголовке Accept / custom header. Гибко, но требует дисциплины клиентов и документации.

Эволюционный контракт: только additive изменения в текущей версии; breaking — только в следующем major с параллельной поддержкой.

Политика deprecation: анонс, метрики использования старого пути, срок отключения, коммуникация в release notes и, если нужно, принудительное обновление приложения.

Контракт как артефакт: OpenAPI, примеры, consumer-driven проверки. Если контракт не в репозитории, его «помнят» люди — значит, его нет.

ИзменениеBreaking?Как выпускать
Новое опциональное полеОбычно нетВ текущей версии
Удаление поляДаНовая major + период поддержки
Смена типа поляДаvNext, миграция клиентов
Новый endpoint рядомНетМожно в текущей версии
Жёсткая смена auth-схемыДаПараллель, переключатели функций, срок

Ошибки, которые повторяют из релиза в релиз

«Мы контролируем оба клиента» — пока не вспомнят партнёрские интеграции и старые сборки.

Версионировать «потом», когда уже больно. Потом дороже: уже есть зоопарк обходов.

Держать v1 вечно без метрик. Версии должны жить по данным использования, не по страху.

Ломать ответы ради «красоты JSON». Эстетика сервера не стоит отзыва в сторе.

Не логировать version клиента и endpoint. Без этого deprecation превращается в гадание.

Практичный минимум для команды, которая уже обожглась: ввести /v1 как «текущий замороженный контракт», все несовместимые идеи складывать в /v2, включить логирование заголовка версии приложения, повесить дашборд ошибок по версиям. Даже грубая схема лучше, чем «один URL на всех». Дальше можно усложнять заголовками и gateway-правилами — но сначала остановить кровотечение.

Коммуникация тоже часть API. Мобильная команда должна узнавать о deprecation не из прод-инцидента, а из календарного анонса с датой и метрикой «можно отключать, когда доля v1 < N%». Без этой договорённости бэкенд всегда будет казаться «ломающим», а мобилка — «тормозящей прогресс».

Если сейчас в проде уже каша из несовместимых ответов, не начинайте с большой красоты. Поднимите совместимый слой, верните мобильным клиентам работоспособность, зафиксируйте контракт тестами — и только потом эволюционируйте. Чистота ради чистоты в момент простой стоит отзывов в сторе.

Для руководителя полезен простой критерий готовности API команды к релизам: можем ли выкатить несовместимое изменение так, чтобы старые сборки приложения продолжили работать заданное число недель. Если ответа нет, versioning ещё не внедрён — есть только надежда на удачный день.

Чек-лист перед релизом API

  • Изменения классифицированы: additive / breaking / fix.
  • Для breaking есть vNext и план параллельной поддержки.
  • OpenAPI/контракт обновлён до выкладки клиентов.
  • В пайплайне есть проверки на предыдущие поддерживаемые клиенты или контрактные тесты.
  • Метрики: доля трафика по версиям, ошибки 4xx по старым путям.
  • Дата deprecation объявлена и согласована с мобильной командой.
  • Откат сервера не ломает новую версию клиента (и наоборот) — сценарии продуманы.
  • Партнёры и внешние интеграции учтены в списке потребителей.

Частые вопросы

Обязательно ли /v1 в URL? Нет, но нужна хоть какая-то явная схема. Хуже всего — «версия в голове разработчика».

Сколько версий держать? Столько, сколько живых клиентов по метрикам плюс запас на store-апрув. Часто 2 major достаточно, если дисциплина релизов нормальная.

Можно ли ломать API при major обновлении приложения? Только если вы реально режете поддержку старых сборок и контролируете принуждение к обновлению. Иначе нет.

Что делать, если уже сломали прод? Вернуть совместимый слой или поднять прежнюю версию endpoint’ов, затем ввести versioning — не наоборот.

GraphQL спасает от версий? Не автоматически. Можно эволюционировать схему аккуратнее, но удаление полей и смена семантики всё равно breaking.

Кто владеет политикой версий? Обычно API/platform-владелец вместе с мобильной командой. Без совместного владельца deprecation снова станет сюрпризом в проде.

Следующий шаг

Если мобильное приложение уже один раз «умерло» после деплоя бэкенда, следующий релиз без политики версий — повтор той же лотереи. Имеет смысл зафиксировать контракт, /v1–/v2 (или эквивалент) и deprecation по метрикам. Настройка устойчивого API контура — в направлении Настройка API: версионирование, совместимость клиентов и контролируемые breaking changes.

Практичный старт на этой неделе: список потребителей API, текущий контракт в OpenAPI, правило «breaking только в новой major» и дашборд ошибок по версиям клиентов. Этого достаточно, чтобы следующий деплой не повторил внезапный отказ приложения в сторе.

Начать разговор

Нужна помощь с похожей задачей?

Опишите ситуацию — предложим решение на основе нашего опыта.