Как деприкейтить API, не теряя разработчиков
5 мин чтения
Деприкейтить API означает объявить, что что-то ещё работает сегодня и перестанет работать к объявленной дате, а затем сдержать обе половины этого обещания. Большинство депрекаций проваливаются на второй половине: дата тихо сдвигается, или наступает, а вызывающие, никогда не видевшие уведомления, узнают об этом из ошибки. Депрекация завершена, когда каждый затронутый вызывающий либо мигрировал, либо был индивидуально уведомлён, что не сделал этого.
Что такое депрекация API?
Депрекация — это период между объявлением, что endpoint, поле или версия исчезнут, и их фактическим удалением. В этот период старое поведение продолжает работать, документация говорит, что оно уходит, и каждый ответ несёт машиночитаемое предупреждение. Удаление — это отдельное, более позднее событие, часто называемое sunset. Эти два путают, и эта путаница — то место, где происходит вред: «deprecated» начинает означать «возможно, уже исчезло», а вызывающие перестают доверять обоим словам.
| Термин | Значение | На что могут полагаться вызывающие |
|---|---|---|
| Deprecated | Объявлено как исчезающее, всё ещё работает | Полное поведение до даты sunset |
| Sunset | Дата, когда перестаёт работать | Ничего после этой даты |
| Retired / удалённое | Исчезло; запросы падают | Ошибка, в идеале называющая замену |
| Legacy | Неопределено. Избегайте этого слова | Ничего, что и есть проблема |
Сколько должен длиться период депрекации?
Достаточно долго, чтобы вызывающий узнал и выполнил работу, измеряется от момента, когда уведомление до него дошло, а не от момента, когда вы его написали. Девяносто дней — обычный минимум для публичного веб-API. Двенадцать месяцев нормально для всего, встроенного в софт, который устанавливают конечные пользователи, потому что исправление также должно пройти через их процесс релиза. Руководство Google по версионированию, AIP-185, требует разумного переходного периода и рекомендует 180 дней даже перед удалением бета-функциональности, а Kubernetes документирует свою политику депрекации в количестве релизов, а не месяцах, что является правильной единицей, когда ваши вызывающие обновляются по версии.
Выберите период, запишите его как политику, и прекратите решать это по каждому изменению. Опубликованная политика превращает каждую депрекацию из переговоров в применение правила.
Запись политики депрекации покрывает начало окна; закрытие версии API разбирает отдельное уведомление, нужное в конце, когда период реально истекает и версия перестаёт работать.
Расписание депрекации
Четыре даты, объявленные вместе в первый день. Каждая — отдельная запись changelog при наступлении, поэтому история рассказывается четыре раза каждому, кто читает только changelog.
- Объявите. Запись говорит, что депрекируется, почему, что это заменяет, и дату sunset. Документация старой вещи получает баннер, ведущий к миграции. Ответы получают заголовки, описанные ниже.
- Напомните, на полпути. Вторая запись, и прямое сообщение каждому вызывающему, всё ещё использующему старое поведение. Это шаг, требующий данных об использовании: если вы не можете перечислить, кто всё ещё вызывает депрекированный endpoint, вы не можете это сделать, и это стоит исправить до следующей депрекации.
- Brownout, незадолго до даты. Возвращайте ошибки для старого поведения в течение короткого окна, час или день, затем восстановите. Вызывающие, пропустившие каждое уведомление, узнают об этом сейчас, пока ещё есть время. GitHub использовал запланированные brownout перед выводом из эксплуатации аутентификации по паролю для API, и это самый эффективный отдельный шаг в этом списке.
- Sunset. Удалите это. Ошибка, заменяющая это, называет замену и ведёт к руководству по миграции. Держите ошибку на месте долго; 404 ничего не сообщает вызывающему.
Что должно говорить уведомление о депрекации?
Уведомление о депрекации говорит, что уходит, когда останавливается, что использовать вместо этого, и кого затрагивает. Вот форма, заполненная:
GET /v1/reports/dailyдепрекирован и перестаёт работать 1 марта 2027 года. Заменяется наGET /v2/reports?granularity=day, который возвращает те же данные со стабильной схемой и пагинацией. Затрагивает 214 интеграций, вызвавших endpoint v1 за последние 30 дней; если ваша одна из них, вы также получите это уведомление по почте. Руководство по миграции: [ссылка]. Ничего не меняется до 1 марта 2027 года. С этой даты endpoint v1 возвращает410 Goneсо ссылкой на эту запись.
Каждое предложение несёт что-то, что нужно читательнице. Количество затронутых интеграций сообщает каждой читательнице, стоит ли продолжать читать. «Ничего не меняется до» — это предложение, позволяющее незатронутым закрыть вкладку. Страница примеры changelog собирает записи от команд, последовательно пишущих эту форму, и стоит прочитать три перед написанием своей первой.
Какие заголовки должен посылать депрекированный endpoint?
Отправляйте Deprecation, Sunset и Link на преемника в каждом ответе от депрекированного
endpoint, с дня объявления. Заголовок Deprecation
несёт дату, когда депрекация вступила в силу; заголовок Sunset
несёт дату, когда endpoint перестаёт отвечать; Link: <url>; rel="successor-version" указывает,
что использовать вместо этого.
HTTP/1.1 200 OK
Deprecation: @1756425600
Sunset: Mon, 01 Mar 2027 00:00:00 GMT
Link: <https://api.example.com/v2/reports>; rel="successor-version"
Link: <https://example.com/changelog/daily-reports>; rel="deprecation"
Большинство вызывающих никогда сами не прочитают заголовки. Их ценность в том, что HTTP-клиент, шлюз или мониторинг вызывающего могут, что превращает вашу депрекацию в оповещение на их стороне вместо страницы на вашей. SDK, которые вы поставляете, должны логировать предупреждение, когда видят такое.
Кто был уведомлён, и откуда вы это знаете?
Это шаг, решающий, будет ли sunset тихим или станет инцидентом поддержки, и это самое сложное, что можно сделать только с changelog. Запись changelog уведомляет каждого, кто читает changelog. Депрекация должна достичь конкретных людей, чей код упадёт, и обычный способ их найти — те же данные об использовании, которые нужны напоминанию на полпути: API-ключи, приложения или аккаунты, недавно вызывавшие депрекированное поведение.
Цикл, который мы выполняем: запись составляется из pull request, добавляющего депрекацию, человек рецензирует формулировку и дату, а после публикации сама запись является уведомлением. Каждый, чей отзыв из виджета о проблеме или запрос замены стал GitHub issue, который закрывает этот pull request, получает в этом issue комментарий, говорящий, что это выпущено, со ссылкой на запись. Лента и виджет обслуживают ту же запись всем остальным, вместе с каждой другой записью в changelog API. Что мы не делаем — не позволяем депрекации стать «выпущенной» до того, как человек её опубликовал; уведомление с неправильной датой хуже, чем отсутствие уведомления.
Какими бы ни были ваши инструменты, вопрос, на который вы должны уметь ответить в день sunset: какие вызывающие всё ещё использовали это на прошлой неделе, и кому из них мы сказали напрямую? Если ответ «мы опубликовали об этом что-то», sunset не готов.
В чём разница между депрекацией и версионированием?
Версионирование — это то, как вы сохраняете старое поведение доступным, пока существует новое; депрекация — это то, как вы выводите старое из эксплуатации. Новая версия API без политики депрекации для предыдущей — это обязательство работать с обеими навсегда. Депрекация без версионирования — это breaking change с задержкой. Вам нужны оба, и версия — более лёгкая половина. GraphQL — исключение, которое стоит назвать: обычно там вообще нет номера версии для повышения, и депрекация схемы GraphQL разбирает, как одна общая схема выводит поле из эксплуатации директивой вместо этого.
FAQ
Должен ли депрекированный endpoint продолжать работать точно так же, как раньше? Да, до даты sunset. Единственные допустимые изменения — добавленные заголовки и, ближе к концу, запланированный brownout, который вы объявили заранее.
Какой код статуса должен возвращать выведенный из эксплуатации endpoint?
410 Gone, с телом и заголовком Link, указывающим на замену и запись changelog. 404 говорит,
что URL никогда не существовал, что ложно и бесполезно.
Можно ли сократить период депрекации? Только по соображениям безопасности. Если старое поведение эксплуатируемо, скажите это, сократите период, и уведомите каждого затронутого вызывающего напрямую, вместо того чтобы полагаться на changelog.
Нужно ли мне деприкейтить поле, или только целые endpoint? Поля, параметры, значения enum, значения по умолчанию и заголовки — все нуждаются в одинаковом обращении, потому что каждый может сломать корректного вызывающего. Удалённое поле — самая распространённая депрекация и наиболее часто пропускаемая.
Технические утверждения в этой статье не проходили независимую проверку. Если здесь что-то не так, сообщите нам, и мы исправим.