«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Управление версиями API в Monite

Управление версиями API в Monite

Опубликовано 8 ноября 2024 г.
Просматривать:245

Мы все любим иметь новые блестящие инструменты, но ненавидим рутинную работу по их постоянному обновлению. Это применимо ко всему: операционным системам, приложениям, API, пакетам Linux. Больно, когда наш код перестает работать из-за обновления, и вдвойне больно, когда обновление даже не было инициировано нами.

При разработке веб-API вы постоянно рискуете сломать пользовательский код с каждым новым обновлением. Если ваш продукт — API, то эти обновления каждый раз будут ужасать. Основными продуктами Monite являются наш API и SDK с белой этикеткой. Мы являемся компанией, ориентированной на API, поэтому мы очень заботимся о том, чтобы наш API был стабильным и простым в использовании. Следовательно, проблема нарушения изменений находится в верхней части нашего списка приоритетов.

Распространенное решение — выдавать клиентам предупреждения об устаревании и редко выпускать критические изменения. Внезапно выпуск ваших релизов теперь может занять месяцы, а некоторые функции должны оставаться скрытыми или даже не объединенными до каждого следующего выпуска. Это замедляет вашу разработку и вынуждает пользователей обновлять свою интеграцию каждые несколько месяцев.

Если вы будете выпускать релизы быстрее, вашим пользователям придется слишком часто обновлять свою интеграцию. Если вы увеличите время между выпусками, ваша компания будет двигаться медленнее. Чем неудобнее вы сделаете это для пользователей — тем удобнее будет для вас, и наоборот. Это, конечно, не оптимальный сценарий. Мы хотели двигаться в своем собственном темпе, не нарушая ничего для существующих клиентов, что было бы невозможно при обычном подходе к прекращению поддержки. Вот почему мы выбрали альтернативное решение: Управление версиями API.

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

Учитывая простоту идеи, она идеально подходит для любой компании. Это то, что вы ожидаете прочитать в типичном инженерном блоге. К сожалению, это не так просто.

Остерегайтесь цены

Управлять версиями API сложно, очень сложно. Его иллюзорная простота быстро исчезает, как только вы начинаете его реализовывать. К сожалению, Интернет никогда вас не предупреждает, поскольку ресурсов по этой теме на удивление мало. Абсолютное большинство из них спорят о том, куда поместить версию API, но лишь в нескольких немногочисленных статьях пытаются ответить: «Как нам реализовать это?». Наиболее распространенные из них:

  • размещение разных версий одного и того же веб-приложения в отдельных развертываниях
  • копирование отдельных маршрутов, которые изменились между версиями
  • копирование всего версионного приложения для каждой версии

Отдельные развертывания могут оказаться очень дорогими и сложными в поддержке, копирование отдельных маршрутов не очень хорошо масштабируется при больших изменениях, а копирование всего приложения создает так много дополнительного кода, что вы начнете тонуть в нем уже после нескольких версий.

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

Вы можете надеяться, что у вас никогда не будет более двух или трех версий API одновременно; что вы сможете удалять старые версии каждые несколько месяцев. Это верно, если вы поддерживаете лишь небольшое количество внутренних потребителей. Но клиенты за пределами вашей организации не будут получать удовольствие от необходимости обновляться каждые несколько месяцев.

Версии API могут быстро стать одной из самых дорогостоящих частей вашей инфраструктуры, поэтому крайне важно заранее провести тщательное исследование. Если вы поддерживаете только внутренних потребителей, вам может быть проще использовать что-то вроде GraphQL, но оно может быстро стать таким же дорогим, как и управление версиями.

Если вы стартап, было бы разумно отложить создание версий API до более поздних этапов разработки, когда у вас появятся ресурсы, чтобы сделать это правильно. До тех пор может быть достаточно отказа от поддержки и стратегии дополнительных изменений. Ваш API не всегда будет выглядеть великолепно, но, по крайней мере, вы сэкономите много денег, избегая явного управления версиями.

Как реализовать управление версиями API?

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

  1. "Поддерживать большое количество версий легко", чтобы гарантировать, что управление версиями не замедляет разработку наших функций.
  2. "Удалить старые версии легко", чтобы мы могли без труда очистить нашу кодовую базу
  3. "Создавать новые версии не так уж просто" чтобы убедиться, что у наших разработчиков по-прежнему есть стимул пытаться решать проблемы без версий.
  4. "Вести журнал изменений между версиями легко" чтобы быть уверенными, что и мы, и наши клиенты всегда можем быть уверены в реальных различиях между версиями

К сожалению, альтернатив нашим существующим подходам практически не было. Именно тогда мне в голову пришла сумасшедшая идея: что, если мы попытаемся создать что-то сложное, что-то идеальное для этой работы — что-то вроде системы управления версиями API Stripe?

В результате бесчисленных экспериментов у нас теперь есть Cadwyn: платформа управления версиями API с открытым исходным кодом, которая не только реализует подход Stripe, но и существенно дополняет его. Мы будем говорить о его реализации Fastapi и Pydantic, но основные принципы не зависят от языка и платформы.

Как работает Кадвин

Изменения версий

Проблема всех других подходов к управлению версиями заключается в том, что мы слишком много дублируем. Зачем нам дублировать весь маршрут, контроллер или даже приложение, если нарушена лишь небольшая часть нашего контракта?

При использовании Cadwyn всякий раз, когда разработчикам API необходимо создать новую версию, они вносят критические изменения в свои последние схемы, модели и бизнес-логику. Затем они создают изменение версии — класс, который инкапсулирует все различия между новой версией и предыдущей версией.

Например, предположим, что раньше наши клиенты могли создавать пользователя с адресом, но теперь мы хотели бы разрешить им указывать несколько адресов вместо одного. Изменение версии будет выглядеть так:

class ChangeUserAddressToAList(VersionChange):
    description = (
        "Renamed `User.address` to `User.addresses` and "
        "changed its type to an array of strings"
    )
    instructions_to_migrate_to_previous_version = (
        schema(User).field("addresses").didnt_exist,
        schema(User).field("address").existed_as(type=str),
    )

    @convert_request_to_next_version_for(UserCreateRequest)
    def change_address_to_multiple_items(request):
        request.body["addresses"] = [request.body.pop("address")]

    @convert_response_to_previous_version_for(UserResource)
    def change_addresses_to_single_item(response):
        response.body["address"] = response.body.pop("addresses")[0]

instructions_to_migrate_to_previous_version используются Cadwyn для генерации кода для старых версий схем API, а две функции конвертера — это трюк, который позволяет нам поддерживать столько версий, сколько нам нужно. Процесс выглядит следующим образом:

  1. Cadwyn преобразует все пользовательские запросы из старых версий API в последнюю версию API с помощью преобразователя Change_address_to_multiple_items и передает их в нашу бизнес-логику.
  2. Бизнес-логика, ответы API и модели баз данных всегда адаптируются к последней версии API (конечно, она по-прежнему должна поддерживать старые функции, даже если они были удалены в новых версиях).
  3. После того, как бизнес-логика выдает ответ, Cadwyn преобразует его в более старую версию API, на которой сейчас работает запрашивающая сторона, с помощью преобразователя Change_addresses_to_single_item.

После того, как наши сопровождающие API создали изменение версии, им необходимо добавить его в наш VersionBundle, чтобы сообщить Cadwyn, что это VersionChange будет включено в какую-то версию:

VersionBundle(
    Version(
        date(2023, 4, 27),
        ChangeUserAddressToAList
    ),
    Version(
        date(2023, 4, 12),
        CollapseUserAvatarInfoIntoAnID,
        MakeUserSurnameRequired,
    ),
    Version(date(2023, 3, 15)),
)

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

Цепочка версий

Изменения версий зависят от общедоступного интерфейса API, и мы почти никогда не добавляем критические изменения в существующие версии API. Это означает, что как только мы выпустим версию, она не будет сломана.

Поскольку изменения версий описывают критические изменения внутри версий, а в старых версиях критических изменений нет, мы можем быть уверены, что изменения наших версий полностью неизменяемы — у них никогда не будет причин для изменения. Неизменяемые сущности гораздо проще поддерживать, чем если бы они были частью бизнес-логики, поскольку она постоянно развивается. Изменения версий также применяются одно за другим, образуя цепочку преобразователей между версиями, которые могут перенести любой запрос на любую более новую версию и любой ответ на любую более старую версию.

API Versioning at Monite

Побочные эффекты

Контракты API гораздо сложнее, чем просто схемы и поля. Они состоят из всех конечных точек, кодов состояния, ошибок, сообщений об ошибках и даже поведения бизнес-логики. Cadwyn использует тот же DSL, который мы описали выше, для обработки конечных точек и кодов состояния, но ошибки и поведение бизнес-логики — это совсем другая история: их невозможно описать с помощью DSL, их необходимо встроить в бизнес-логику.

Из-за этого поддерживать такие изменения версий намного дороже, чем все остальные, поскольку они влияют на бизнес-логику. Мы называем это свойство «побочным эффектом» и стараемся избегать их любой ценой из-за необходимости их обслуживания. Все изменения версии, которые изменят бизнес-логику, должны быть помечены как имеющие побочные эффекты. Это позволит узнать, какие изменения версии «опасны»:

class RequireCompanyAttachedForPayment(VersionChangeWithSideEffects):
    description = (
        "User must now have a company_id in their account "
        "if they want to make new payments"
    )

Это также позволит специалистам по обслуживанию API проверять, использует ли клиентский запрос версию API, которая включает этот побочный эффект:

if RequireCompanyToBeAttachedForPayment.is_applied:
    validate_company_id_is_attached(user)

Никаких серебряных пуль

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

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

Особая благодарность Брандуру Личу за его статью о версиях API на Stripe и за помощь, которую он оказал мне, когда я реализовал Cadwyn: без его помощи это было бы невозможно.

Заявление о выпуске Эта статья воспроизведена по адресу: https://dev.to/monite/api-versioning-at-monite-3ba3?1. Если есть какие-либо нарушения, свяжитесь с [email protected], чтобы удалить их.
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3