Версионирование API
Как только у API есть внешние клиенты, которых нельзя обновить одномоментно, встаёт вопрос эволюции контракта. Собес проверяет, отличаешь ли ты обратно совместимое изменение от ломающего и умеешь ли выбрать один способ версионирования вместо каши.
Типовые формулировки: «как версионировать API?», «какое изменение сломает клиентов?», «можно ли поменять тип поля в текущей версии».
Зачем и как версионировать
Версионируют публичный API, чтобы менять контракт, не ломая внешних клиентов, которых нельзя заставить обновиться разом. Способы: версия в пути (/v1 - наглядно и кэшируемо), в заголовке Accept (чище по URL), в query-параметре. Все рабочие - главное выбрать ОДИН и держаться его; смешивать в одном API нельзя, это путает интеграторов.
// Внутренний API между своими сервисами версионировать строго не обязательно - там клиентов обновляешь ты же; версионирование ценно именно для публичного контракта.
- версионирование
- параллельные версии контракта API для совместимости
- обратная совместимость
- изменение, не ломающее существующих клиентов
Совместимо против ломающего
Обратно совместимо - добавить НЕОБЯЗАТЕЛЬНОЕ поле: старые клиенты просто игнорируют неизвестное, ничего не ломается, новую версию заводить не надо. Ломающие изменения - переименование, удаление, смена типа или обязательности поля: они рвут парсинг у тех, кто читает контракт по-старому.
Отсюда правило: ломающие изменения катят в НОВУЮ версию, оставляя старую живой на переходный период. Сменить тип поля со строки на число «для единообразия» прямо в текущей версии - классика, которая роняет клиентов в проде.
// Практический признак: можешь ли ты выкатить изменение так, что существующий клиент не заметит? Да - совместимо, нет - новая версия.
- ломающее изменение
- переименование/удаление/смена типа поля
Как отвечать: «Какое изменение API сломает клиентов, а какое нет?»
Не сломает то, что старый клиент может просто не заметить: добавить необязательное поле в ответ или новый опциональный параметр - читающие по-старому его игнорируют. Сломает всё, что меняет уже существующий контракт: переименовать или удалить поле, сделать опциональное обязательным, поменять тип со строки на число. Такие вещи рвут парсинг у клиентов, которых я не могу обновить одномоментно, поэтому их я катаю в новую версию - /v2, и держу /v1 живой на переходный период. Проверочный вопрос себе: заметит ли изменение существующий клиент? Если да это ломающее.
Дан рабочий критерий (заметит ли старый клиент), перечислены оба класса с примерами и названа стратегия - новая версия плюс переходный период; практично и проверяемо.
На чём валят
- −Катить удаление или переименование поля в текущую версию - ломает читающих клиентов.
- −Менять тип поля со строки на число «для единообразия» - рвёт парсинг у клиентов.
- −Смешивать несколько способов версионирования в одном API - путаница для интеграторов.
- −Делать раньше опциональное поле обязательным - старые запросы без него начнут падать.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 11, остальные разбираются в тренажёре.
- Какие есть типичные способы версионировать REST API?A)Версию шлют в теле каждого запроса отдельным полемB)Версионирование задаётся исключительно кодом ответа сервераC)Префикс пути /v1, заголовок Accept или query-параметрD)Только смена доменного имени сервиса на каждую новую версию
показать ответ и разбор
+C)Префикс пути /v1, заголовок Accept или query-параметр// разбор: Распространены три подхода: версия в пути (/v1/users — просто и явно), в заголовке (Accept: application/vnd.api.v2+json — «чище» по URL), или query-параметром (?version=2). У пути лучшая наглядность и кэшируемость; заголовок ближе к «чистому REST». Главное — выбрать один и держаться его.
- Какое изменение ответа безопасно для существующих клиентов?A)Поменять тип поля со строки на число для единообразия схемыB)Добавить новое необязательное поле в тело ответаC)Удалить поле, которым, как кажется, никто уже не пользуетсяD)Переименовать существующее поле в более понятное название
показать ответ и разбор
+B)Добавить новое необязательное поле в тело ответа// разбор: Обратно совместимо — добавление необязательного поля: корректные клиенты игнорируют неизвестное. Ломают: переименование, удаление, смена типа или обязательности существующего поля, изменение семантики. Ломающие изменения выносят в новую версию, а не катят в текущую.
- Зачем публичному API вообще версионирование?A)Это чисто косметическое требование — на реальную работу API оно не влияетB)Ломающие изменения выкатывают в новой версии, не рушив старых клиентовC)Для ускорения API: новые версии работают быстрее предыдущихD)Чтобы вынудить клиентов обновиться до самой свежей версии
показать ответ и разбор
+B)Ломающие изменения выкатывают в новой версии, не рушив старых клиентов// разбор: У публичного API есть внешние потребители, которых нельзя ломать внезапно. Версионирование позволяет вводить несовместимые изменения в новой версии (v2), пока старая (v1) продолжает работать. Клиенты мигрируют в своём темпе, а вы объявляете политику устаревания. Без версий любое breaking change одномоментно рушит всех.
- Какое изменение API можно внести без новой версии, не сломав клиентов?A)Изменить тип поля с числа на строку ради единообразия форматов ответаB)Добавить необязательное поле или новый эндпоинт — старые клиенты его не заметятC)Переименовать существующее поле ответа на более понятное и удобное имяD)Сделать ранее необязательный параметр запроса теперь обязательным для всех
показать ответ и разбор
+B)Добавить необязательное поле или новый эндпоинт — старые клиенты его не заметят// разбор: Обратно совместимы (не требуют новой версии) аддитивные изменения: новое опциональное поле в ответе, новый эндпоинт, новый необязательный параметр. Старые клиенты их просто игнорируют. Ломают: удаление/переименование поля, смена типа, новое обязательное требование, изменение семантики. Правило: только добавлять, ничего молча не менять и не убирать.
- Версию API держат в пути (/v1/...) или в заголовке. Чем берёт вариант с путём?A)Он скрывает версию от клиента, снижая связанность между сторонами интеграцииB)Версия в заголовке считается устаревшей, путь пришёл ей на сменуC)Он виден и явен: версия читается прямо в URL, легко тестировать и кешироватьD)Он позволяет менять версию, не трогая URL, что удобнее для клиентов интеграции
показать ответ и разбор
+C)Он виден и явен: версия читается прямо в URL, легко тестировать и кешировать// разбор: Версия в пути (/v1/users) наглядна: видна в URL, тривиально дёрнуть curl'ом, кешируется и логируется как разные ресурсы. Минус — версия просачивается в идентификатор ресурса. Версия в заголовке (Accept с медиа-типом или X-API-Version) держит URL чистым и «правильнее» по REST, но менее очевидна и сложнее в отладке. Оба подхода рабочие; путь популярнее за простоту.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.