Insights sobre versionamento de APIs

Neste artigo, faço uma pesquisa sobre como algumas das principais APIs do mercado tratam o assunto versionamento. Para conhecer alguns conceitos básicos sobre versionamento, recomendo primeiro a leitura do capítulo sobre versionamento no Guia de Design REST.

No quadro abaixo, listo algumas APIs independente do segmento, idade, qualidade da modelagem ou aderência ao REST. Observe como cada uma das empresas optou por versionar suas APIs.

API Onde Como
Direções do Google Maps
maps.googleapis.com/maps/api/directions/json
Não versiona
Maps em Javascript
maps.googleapis.com/maps/api/js ?v=3.39
query strings v=weekly, v=quarterly, v=n.nn (ex: v=3.39)
Busca de Tweets no Twitter
api.twitter.com/1.1/search/tweets.json
path parameter 1.1
Facebook
graph.facebook.com/v2.2/me/adaccounts
path parameter 2.2
Twillio
api.twilio.com/2010-04-01/Accounts/123/Addresses.json
path parameter 2010-04-01
Last FM
ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist=Cher
&api_key=YOUR_API_KEY&format=json
path parameter 2.0
Azure SQL Databases
management.azure.com/subscriptions/
/resourceGroups/
/providers/Microsoft.Sql/servers/
/databases/?api-version=2017-10-01-preview
query parameter ?api-version=2017-10-01-preview
Four Square
api.foursquare.com/v2/venues/search?client_id=CLIENT_ID
&client_secret=CLIENT_SECRET&ll=40.7,-74
&query=sushi&v=20180323
path parameter, query parameter /v2 e ?v=20180323
Linkedin
api.linkedin.com/v2/me
path parameter v2
PayPal
api.sandbox.paypal.com/v2/invoicing/invoices
query parameter v2
Indeed
api.indeed.com/ads/apisearch?publisher=1234&q=java+developer &v=2
query parameter v=2
Banco do Brasil
/consultas-financeiras/v2/statements/checking-account
path parameter v2
BBVA
apis.bbva.com/customers/v1/me/globalposition-basic
path parameter v1
Vimeo
api.vimeo.com/videos/174560759/comments
Accept: application/vnd.vimeo.user+json;version=3.0
header Accept: application/vnd.vimeo.user+json;version=3.0
Google Cloud Datastore
datastore.googleapis.com/v1/projects/
datastore.googleapis.com/v1beta1/projects/
path parameter v1
Instagram
api.instagram.com/v1/self/media/recent
path parameter v1

Com base no quadro acima, podemos observar alguns pontos:

Preferência por path parameter e query parameter

Apesar de ter buscado por APIs diversas, nenhuma delas optou pelo versionamento via host ou header. O Vimeo foi o único a adotar a abordagem com content-type. A preferência geral foi por utilizar path parameter (na estrutura da URL) ou passar como query parameter.

Foram utilizadas Major somente, Major.Minor e datas

Em ordem crescente, as empresas preferiram optar por versionar considerando apenas o Major, Major.Minor e uma minoria com data.

Adotam uma única forma de versionar

A maioria optou por um único ponto para versionar a API (path parameter ou query). Apenas a API do Four Square que optou por colocar a Major na URL e opcionalmente, uma versão específica via query string.

Algumas endereçaram versões preview, beta e builds recorrentes

Apesar de menos frequente, algumas strings de versionamento endereçam builds recorrentes, como weekly, quarterly como na Google Maps em Javascript; v1beta1 na Google Datastore e 2017-10-01-preview na Azure Sql Databases.

E o Oscar vai para "versão MAJOR na URL"

Quando falamos versionamento semântico, alterações em Minor e Path não devem fazer com que quem já consome a versão tenha qualquer problema por quebra de contrato, ou seja, nenhum campo não obrigatório passa a ser obrigatório e nenhum campo é removido. Neste tipo de alteração, são apenas adicionados novos campos ou feitas alterações em metadados. Assim, pequenas evoluções podem ser feitas na API e notificadas aos desenvolvedores, no entanto, sem demandar que atualizações sejam obrigatoriamente feitas nos seus clientes.

Entendo que clientes que venham a "quebrar" por conta da adição de novos campos, provavelmente fizeram o consumo da API de forma atípica, pra não falar errada, talvez, processando manualmente o payload e buscando os campos por posição, por exemplo. Martin Fawler em TolerantReader reforça conceitos do comportamento que é esperado de um consumidor de serviços.

Outro ponto a ser considerado é que, por mais que não se quebre diretamente o contrato, quem produz a API pode optar por incrementar uma MAJOR em casos raros, mas possíveis, como aumento considerável do tamanho ao payload, aumento considerável do tempo de processamento, alteração nas regras de quantidade de registros retornados etc.

Open Banking: E quando o modelo é definido por uma entidade externa?

- Abordagem 1: versão MAJOR apenas e flexibilidade timing da migração.

No cenário de Open Banking, por exemplo, em que o contrato é definido por uma entidade externa e os clientes e servidores devem respeitá-lo, também seria possível manter o versionamento apenas com a MAJOR na URL.

Imagine o cenário de um contrato como o de baixo:

Data Versão Campos
01/2020 1.0 "nome", "telefone"
06/2020 1.1 "nome", "telefone", "endereco"
11/2020 1.2 "nome", "telefone", "endereco", "email"

Agora, segue um exercício de cliente e servidor implementando em "tempos" diferentes e sem que um impacte no outro.

Data URL Versão cliente Versão servidor Resultado
01/2020 /cadastros/v1/clientes 1.0
Lê os campos
"nome" e "telefone"
1.0
fornece os campos
"nome" e "telefone"
100% ok
07/2020 /cadastros/v1/clientes 1.1
Lê os campos
"nome", "telefone"
e implementa leitura opcional do campo "endereco"
1.0
fornece os campos
"nome" e "telefone"
Cliente não recebe ainda o "endereço", mas sua implementação está preparada para tal.
09/2020 /cadastros/v1/clientes 1.1
Lê os campos
"nome", "telefone"
e implementa leitura opcional do campo "endereco"
1.1
fornece os campos
"nome", "telefone" e "endereco"
Cliente passa a receber o "endereço" no momento que o servidor passou a enviar o dado.
12/2020 /cadastros/v1/clientes 1.1
Lê os campos
"nome", "telefone"
e implementa leitura opcional do campo "endereco"
1.2
fornece os campos
"nome", "telefone", "endereco" e "email"
Servidor fornece todos os campos da nova versão, mas cliente ignora existência do campo "email".

A vantagem desta abordagem é que permite que tanto o cliente, quanto servidor possam implementar as versões definidas pela entidade externa, cada um no seu tempo, independentemente se a outra parte da comunicação já o fez.

Imagina um app de financiamento de veículos que integraria com centenas de fornecedores bancários, seria desejável esse app poder atualizar a versão do seu software uma única vez quando o Open Banking definir uma nova versão, independentemente to timing de cada um dos fornecedores.

O que garantirá que as peças "se encaixem" será o contrato bem especificado com tamanho máximo, mínimo, tipo de dado, expressão regular, exemplos e boas descrições, de forma que a implementação em ambos os lados não tragam elementos surpresa quando estiver em produção.

O que garantirá a aderência do software aos contratos são as ferramentas já fornecidas pelas disciplinas de testes e qualidade de software conhecidas pelo mercado.

- Abordagem 2: versão MAJOR.MINOR e inflexibilidade no timing da migração.

Opcionalmente, se partirmos do princípio de que as práticas acima não serão seguidas, podemos utilizar a versão específica MAJOR.MINOR na URL, isso reduz o risco de alguma quebra do cliente, pois o servidor passa a ofertar sempre 'n' versões do software, no entanto, o cliente fica dependente do servidor implementar primeiro a versão para depois ele poder consumir.

As URLs, neste caso ficariam assim:

- Abordagem 3: versão MAJOR ou MAJOR.MINOR e flexibilidade no timing da migração.

Neste modelo, podemos ter 4 URLs:

Assim ofereceríamos flexibilidade aos clientes, dado que ele escolhe se prefere trabalhar com a abordagem 1 ou a 2.

Quando o cliente chamar a URL v1, ele sempre trará a implementação mais recente existente da versão 1. Logo, o comportamento seria o mesmo da abordagem 1, no entanto, além da v1, o servidor disponibilizaria as v1.0, v1.1 e v1.2 para aqueles clientes que prefiram a segurança de ter um contrato congelado no tempo, mas com o trade-off de esperar o servidor implementar a versão.

Conclusão

Sobre Open Banking, penso no cliente que é um APP e precisará acessar ao mesmo tempo 'n' implementações de bancos diferentes. Acredito que ele prefira a flexibilidade de implementar uma única vez a capacidade de ler a nova versão e colher os benefícios de novos atributos o quanto antes, independente de os seus vários fornecedores de informação já estarem preparados para disponibilizá-las. Por isso, gosto da abordagem 3, que me parece atender a qualquer um dos cenários. Inclusive daquele que optar por aguardar todos os fornecedores disponibilizarem a versão nova.

Sobre os modelos de versionamento de forma geral, talvez com um número bem maior de APIs, outros insights poderiam ter aparecido, no entanto, não me surpreendeu que após este pequeno o levantamento, não tenha aparecido nada de muito novo, senão o que já é recorrente em guias de boas práticas. Ou seja, já existe uma convergência no mercado para alguns modelos principais.