1 pontos por GN⁺ 4 시간 전 | 1 comentários | Compartilhar no WhatsApp
  • Quando uma API web pública usa ao mesmo tempo um nome como Product API e o caminho /api/v1, a versão semântica da própria API e sua estrutura podem ficar desalinhadas
  • Ao usar em paralelo o caminho /v1/ e major.minor.patch, rotas e o contrato da API acabam se misturando, e o primeiro número da versão semântica fica preso à URL
  • Mudanças que quebram compatibilidade passam a exigir um novo caminho e rotas de proxy reverso, fazendo com que as informações de contrato fiquem espalhadas entre a URL e o número da versão
  • Se APIs posteriores forem criadas ao mesmo tempo, a API existente fica, na prática, presa ao v1, e depois, em mudanças incompatíveis, o significado do nome e do caminho se torna ambíguo
  • Trata-se da preocupação em encontrar abordagens de versionamento de APIs web públicas que repetidamente incomodam e buscar princípios de design melhores

1 comentários

 
GN⁺ 4 시간 전
Opiniões no Lobste.rs
  • Colocar /v1/ na URL é, na verdade, uma das grandes vantagens. Porque isso obriga você a não quebrar a API para os usuários até desativar o endpoint

  • Evolving HTTP APIs e outros textos do mesmo autor trazem conselhos úteis

  • Em geral, colocamos /v1/, /v2/ etc. em cada rota para indicar mudanças incompatíveis. Se for uma API pública em operação, e não uma tentativa de definir um padrão que funcione em vários hosts, quase não há motivo para fazer versionamento semântico completo (semantic versioning)
    O versionamento semântico existe para que outros desenvolvedores possam atualizar dependências com confiança sem precisar passar 20 minutos lendo changelogs, mas numa API em produção as pessoas não podem escolher quando adotar uma nova versão minor ou de correção
    O que conta como mudança incompatível é alterar um comportamento documentado ou quebrar clientes existentes que dependem desse comportamento documentado. Há lugares que também tratam mudanças em comportamentos não documentados como quebra, mas isso traz muitos riscos

  • No Google, fazem assim: AIP-185: API Versioning, AIP-180: Bacwards compatibility
    Esses documentos de design parecem bem específicos da forma como o Google trabalha, mas eu os venho usando como referência ao projetar APIs, e algumas ideias dali foram muito úteis

  • Em geral, acho que toda API deveria tentar minimizar ao máximo as mudanças incompatíveis. Por exemplo, se você quiser renomear uma propriedade, parece melhor adicionar o novo nome em duplicidade do que remover a propriedade antiga
    Ainda assim, a forma como o pessoal da Buttondown faz isso também é elegante. Eles definem migrações entre versões da API, assim o consumidor pode fixar sua versão da API por header, enquanto o provedor continua evoluindo o serviço

    • Duplicar propriedades funcionou muito bem para propriedades de saída. Mas, na entrada, você precisa lidar com casos em que o cliente envia as duas propriedades com valores diferentes
      A resposta óbvia parece ser “o nome novo sempre tem prioridade”, mas isso pode falhar se o cliente fizer uma sequência de leitura-modificação-escrita e reenviar uma versão modificada de um objeto criado pelo servidor. Nesse caso, o cliente pode atualizar só a propriedade antiga e devolver a nova sem mexer nela
    • Como o provedor da API explicou, parece bom oferecer migrações entre versões da API, mas usar um header de requisição HTTP para versionamento pode causar problemas
    • Esse link explica muito bem como lidar com a forma dos dados. Mas isso é só uma parte; fico me perguntando como lidar quando o próprio comportamento muda
      Parece que esse tipo de transformação também poderia ser usado para mapeamento de comportamento, mas, a menos que eu tenha deixado passar algo, isso não foi abordado
  • Idealmente, a versão deveria estar incluída no caminho, e novas versões deveriam ser de natureza adicional. Assim, a API da versão antiga poderia redirecionar internamente a requisição para uma versão mais nova da API, aplicando as transformações necessárias de entrada e saída
    Depois de alguns anos, quando ninguém mais estiver usando uma versão antiga, ela pode ser removida, e a rota /v1/ passa a retornar erro

  • Há um tempo li um pouco sobre fazer versionamento de API com negociação de conteúdo via header Accept. Se alguém já fez versionamento de API desse jeito, eu gostaria de saber como foi a experiência
    Pela minha experiência, versionamento por recurso ou versionamento global foram as abordagens mais intuitivas. Para descontinuação, usar o header de resposta HTTP Deprecation (RFC 9745) e, no fim, retornar algo como 410 Gone para endpoints antigos parece uma forma razoável de levar clientes a migrarem para a nova versão
    Além disso, também fico realmente curioso se alguém já construiu uma API evolutiva. Quero dizer, uma abordagem em que requisições para versões antigas são traduzidas internamente para requisições da versão nova da API e, depois que os clientes migram ou passa certo tempo, a versão antiga é de fato removida