"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Versionamento de API na Monite

Versionamento de API na Monite

Publicado em 2024-11-08
Navegar:869

Todos nós adoramos ter ferramentas novas e brilhantes, mas odiamos a tarefa de atualizá-las constantemente. Isso se aplica a qualquer coisa: sistemas operacionais, aplicativos, APIs, pacotes Linux. É doloroso quando nosso código para de funcionar por causa de uma atualização e é o dobro da dor quando a atualização nem mesmo foi iniciada por nós.

No desenvolvimento de APIs web, você corre constantemente o risco de quebrar o código de seus usuários a cada nova atualização. Se o seu produto for uma API, essas atualizações serão sempre assustadoras. Os principais produtos da Monite são nossa API e nosso SDK de marca branca. Somos uma empresa que prioriza APIs, por isso tomamos muito cuidado para manter nossa API estável e fácil de usar. Portanto, o problema de alterações significativas está próximo do topo da nossa lista de prioridades.

Uma solução comum é emitir avisos de descontinuação para seus clientes e raramente liberar alterações significativas. De repente, seus lançamentos podem levar meses e alguns recursos precisam permanecer ocultos ou até mesmo não mesclados até o próximo lançamento. Isso retarda seu desenvolvimento e força seus usuários a atualizar sua integração a cada poucos meses.

Se você tornar os lançamentos mais rápidos, seus usuários terão que atualizar sua integração com muita frequência. Se você prolongar o tempo entre os lançamentos, sua empresa avançará mais lentamente. Quanto mais inconveniente você tornar isso para os usuários, mais conveniente será para você e vice-versa. Este certamente não é um cenário ideal. Queríamos avançar em nosso próprio ritmo, sem prejudicar nada para os clientes existentes, o que seria impossível com uma abordagem de descontinuação regular. É por isso que escolhemos uma solução alternativa: versionamento de API.

É uma ideia bastante simples: liberar quaisquer alterações importantes a qualquer momento, mas ocultá-las em uma nova versão da API. Ele garante o melhor dos dois mundos: os usuários não terão suas integrações quebradas rotineiramente e você poderá se mover na velocidade que desejar. Os usuários migrarão quando quiserem – livres de qualquer pressão.

Pela simplicidade da ideia, ela parece perfeita para qualquer empresa. Isso é o que você esperaria ler em um blog típico de engenharia. Infelizmente, não é tão simples.

Cuidado com o preço

O controle de versão da API é difícil, muito difícil. Sua simplicidade ilusória desaparece rapidamente quando você começa a implementá-la. Infelizmente, a Internet nunca avisa você, pois há surpreendentemente poucos recursos sobre o assunto. A maioria absoluta deles discute onde colocar a versão da API, mas apenas alguns artigos escassos tentam responder: "Como podemos implementá-la?". Os mais comuns são:

  • colocar versões diferentes do mesmo aplicativo da web em implantações separadas
  • copiando rotas únicas que foram alteradas entre versões
  • copiando todo o aplicativo versionado para cada versão

Implantações separadas podem ser muito caras e difíceis de suportar, copiar rotas únicas não se adapta muito bem a grandes mudanças e copiar o aplicativo inteiro cria tanto código extra que você começará a se afogar nele depois de apenas algumas versões.

Mesmo se você tentar escolher o mais barato, o fardo do controle de versão aumentará em breve. A princípio, parecerá simples: adicione outro esquema aqui, outra ramificação na lógica de negócios ali e duplique algumas rotas no final. Mas, com versões suficientes, sua lógica de negócios se tornará rapidamente incontrolável, muitos de seus desenvolvedores confundirão versões de aplicativos e versões de API e começarão a versionar os dados em seu banco de dados, e seu aplicativo se tornará impossível de manter.

Você pode esperar nunca ter mais de duas ou três versões de API ao mesmo tempo; que você poderá excluir versões antigas a cada poucos meses. É verdade se você apoiar apenas um pequeno número de consumidores internos. Mas os clientes fora da sua organização não vão gostar da experiência de serem forçados a atualizar a cada poucos meses.

O versionamento de API pode rapidamente se tornar uma das partes mais caras da sua infraestrutura, por isso é fundamental fazer pesquisas diligentes com antecedência. Se você oferece suporte apenas a consumidores internos, talvez seja mais fácil usar algo como GraphQL, mas pode rapidamente ficar tão caro quanto o versionamento.

Se você é uma startup, seria aconselhável adiar o versionamento da API até os estágios posteriores do seu desenvolvimento, quando você tiver recursos para fazer isso da maneira certa. Até então, as descontinuações e a estratégia de mudança aditiva podem ser suficientes. Sua API nem sempre terá uma ótima aparência, mas pelo menos você economizará muito dinheiro evitando o versionamento explícito.

Como implementamos o versionamento de API?

Depois de algumas tentativas e muitos erros, chegamos a uma encruzilhada: nossas abordagens de versionamento anteriores que mencionamos acima eram muito caras para manter. Como resultado de nossas lutas, desenvolvi a seguinte lista de requisitos que seriam exigidos para uma estrutura de versionamento perfeita:

  1. "Manter um grande número de versões é fácil" para garantir que o controle de versão não atrase o desenvolvimento de nossos recursos
  2. "Excluir versões antigas é fácil" para garantir que possamos limpar nossa base de código sem esforço
  3. "Criar novas versões não é muito fácil" para garantir que nossos desenvolvedores ainda sejam incentivados a tentar resolver problemas sem versões.
  4. "Manter um changelog entre versões é fácil" para garantir que tanto nós quanto nossos clientes possamos sempre ter certeza sobre as diferenças reais entre as versões

Infelizmente, havia pouca ou nenhuma alternativa às nossas abordagens existentes. Foi quando uma ideia maluca me veio à mente: e se tentarmos construir algo sofisticado, algo perfeito para o trabalho - algo como o versionamento de API do Stripe?

Como resultado de inúmeros experimentos, agora temos o Cadwyn: uma estrutura de versionamento de API de código aberto que não apenas implementa a abordagem do Stripe, mas também se baseia significativamente nela. Estaremos falando sobre sua implementação Fastapi e Pydantic, mas os princípios básicos são independentes de linguagem e estrutura.

Como funciona Cadwyn

Mudanças de versão

O problema de todas as outras abordagens de versionamento é que estamos duplicando demais. Por que duplicaríamos toda a rota, controlador ou mesmo aplicativo quando apenas uma pequena parte do nosso contrato foi quebrada?

Com Cadwyn, sempre que os mantenedores de API precisam criar uma nova versão, eles aplicam as alterações mais recentes em seus esquemas, modelos e lógica de negócios mais recentes. Em seguida, eles criam uma mudança de versão – uma classe que encapsula todas as diferenças entre a nova versão e uma versão anterior.

Por exemplo, digamos que anteriormente nossos clientes pudessem criar um usuário com um endereço, mas agora gostaríamos de permitir que eles especificassem vários endereços em vez de um único. A mudança de versão ficaria assim:

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 são usados ​​​​por Cadwyn para gerar código para versões de esquemas de API mais antigas e as duas funções de conversor são o truque que nos permite manter quantas versões quisermos. O processo é semelhante ao seguinte:

  1. Cadwyn converte todas as solicitações de usuários de versões mais antigas da API para a versão mais recente da API usando o conversor change_address_to_multiple_items e as canaliza para nossa lógica de negócios
  2. A lógica de negócios, suas respostas de API e modelos de banco de dados são sempre adaptados à versão mais recente da API (é claro, ela ainda deve suportar recursos antigos, mesmo que tenham sido descartados em novas versões)
  3. Depois que a lógica de negócios produz a resposta, Cadwyn a converte para a versão mais antiga da API em que o cliente solicitante está usando o conversor change_addresses_to_single_item.

Depois que nossos mantenedores da API criaram a mudança de versão, eles precisam adicioná-la ao nosso VersionBundle para informar ao Cadwyn que este VersionChange será incluído em alguma versão:

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

É isso: adicionamos uma alteração significativa, mas nossa lógica de negócios lida apenas com uma única versão – a mais recente. Mesmo depois de adicionarmos dezenas de versões de API, nossa lógica de negócios ainda estará livre de lógica de versionamento, renomeação constante, if's e conversores de dados.

Encadeamento de versões

As alterações de versão dependem da interface pública da API e quase nunca adicionamos alterações significativas às versões existentes da API. Isso significa que depois de lançarmos a versão – ela não será quebrada.

Como as alterações de versão descrevem alterações significativas dentro de versões e não há alterações significativas em versões antigas, podemos ter certeza de que nossas alterações de versão são completamente imutáveis ​​– elas nunca terão um motivo para mudar. Entidades imutáveis ​​são muito mais fáceis de manter do que se fizessem parte da lógica de negócios porque estão sempre evoluindo. As alterações de versão também são aplicadas uma após a outra - formando uma cadeia de transformadores entre versões que podem migrar qualquer solicitação para qualquer versão mais recente e qualquer resposta para qualquer versão mais antiga.

API Versioning at Monite

Efeitos colaterais

Os contratos de API são muito mais complexos do que apenas esquemas e campos. Eles consistem em todos os endpoints, códigos de status, erros, mensagens de erro e até mesmo comportamentos de lógica de negócios. Cadwyn usa a mesma DSL que descrevemos acima para lidar com endpoints e códigos de status, mas erros e comportamentos de lógica de negócios são uma história diferente: eles são impossíveis de descrever usando uma DSL, eles precisam ser incorporados à lógica de negócios.

Isso torna a manutenção dessas alterações de versão muito mais cara do que todas as outras porque afetam a lógica de negócios. Chamamos esta propriedade de “efeito colateral” e tentamos evitá-los a todo custo devido ao seu ônus de manutenção. Todas as alterações de versão que desejam modificar a lógica de negócios precisarão ser marcadas como tendo efeitos colaterais. Servirá como uma forma de saber quais alterações de versão são "perigosas":

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

Isso também permitirá que os mantenedores da API verifiquem se a solicitação do cliente usa uma versão da API que inclui este efeito colateral:

if RequireCompanyToBeAttachedForPayment.is_applied:
    validate_company_id_is_attached(user)

Sem balas de prata

Cadwyn tem muitos benefícios: reduz muito a carga de nossos desenvolvedores e pode ser integrado à nossa infraestrutura para gerar automaticamente o changelog e melhorar nossos documentos de API.

No entanto, o fardo do controle de versão ainda existe e mesmo uma estrutura sofisticada não é uma solução mágica. Fazemos o possível para usar o versionamento de API apenas quando for absolutamente necessário. Também tentamos corrigir nossa API na primeira tentativa, tendo um "Conselho de API" especial. Todas as alterações significativas da API são revisadas por nossos melhores desenvolvedores, testadores e redatores de tecnologia antes de qualquer implementação ser iniciada.

Agradecimentos especiais a Brandur Leach por seu artigo sobre versionamento de API no Stripe e pela ajuda que ele me ofereceu quando implementei o Cadwyn: isso não seria possível sem a ajuda dele.

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/monite/api-versioning-at-monite-3ba3?1 Se houver alguma violação, entre em contato com [email protected] para excluí-lo
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3