Versionar uma API é uma relação, não um número
A versão na URL é a parte menos importante. O que importa é quem depende de você e o que você deve a essas pessoas.
Todo debate sobre versionamento de API que presenciei começou no lugar errado. Caminho ou header? v1 ou uma data? Semântico ou incremental? São perguntas de formatação. Decidem como uma versão se escreve, não o que ela significa, e um time que responde isso primeiro costuma lançar uma v2 que quebra os mesmos consumidores da v1.
Uma versão é uma promessa feita às pessoas que construíram em cima do seu trabalho. O número é só a etiqueta da promessa. Se você não sabe quem são essas pessoas, o que elas chamam e com que velocidade conseguem mudar, a etiqueta é decoração.
Quem está do outro lado
A primeira coisa que pergunto ao auditar uma API não é como ela é versionada, mas quem a consome. As respostas caem em três grupos, e cada um precisa de um tipo diferente de relação.
Times internos podem ser procurados. Você abre um pull request no repositório deles, ou pelo menos um ticket com prazo. Mudanças incompatíveis são negociáveis, e subir a versão muitas vezes é mais cerimônia do que proteção.
Parceiros externos conhecidos têm contratos, às vezes literais. Fazem deploy no próprio ritmo, geralmente mais lento que o seu. Uma mudança incompatível aqui custa uma reunião, uma janela de migração e boa vontade.
Consumidores anônimos, atrás de chaves públicas de API, aplicativos móveis espalhados por aí, ou um widget que alguém incorporou há três anos, não podem ser procurados. Para eles a versão é o único canal que você tem, e você precisa assumir que a versão antiga será chamada para sempre.
A maioria das APIs serve os três grupos. Um único esquema global trata o time da sala ao lado como um aplicativo anônimo de 2022: rígido demais para um, frouxo demais para o outro.
O que realmente quebra as pessoas
Consumidores não quebram por número de versão. Quebram por formato. Em ordem de frequência: um campo removido, um campo renomeado, um campo nulo que antes sempre vinha preenchido, um enum com um valor novo que o switch do cliente não esperava, uma validação que ficou mais rígida, e um padrão que mudou.
A maioria disso não é 'incompatível' pela definição estrita que muitos times usam. E ainda assim cada um derruba um cliente que confiava no comportamento antigo. Uma política de versionamento que só cobre campos removidos protege contra a falha menos comum.
Então minha regra é: expansão é grátis, contração é uma versão. Você pode adicionar campos, endpoints, parâmetros opcionais e valores de enum, desde que tenha documentado que os clientes precisam tolerar valores desconhecidos. Não pode remover, renomear, apertar ou mudar o significado sem versão nova e caminho de migração. E 'significado' inclui se um valor está em centavos ou em unidades.
A mecânica que eu recomendo de fato
A mecânica quase se escolhe sozinha. Coloque a versão maior no caminho da URL, porque ela aparece nos logs, nos comandos curl e no navegador, e porque consumidores anônimos a encontram sem ler headers. Não versione mudanças menores; torne-as aditivas.
Mantenha no máximo duas versões maiores vivas, e dê à antiga uma data de fim no dia em que a nova sai. Não 'deprecated', que ninguém lê, mas uma data, na documentação e num header de resposta, com um aviso que fica mais alto conforme ela se aproxima.
Depois, instrumente. Uma versão que você não consegue medir é uma versão que você não consegue aposentar. Marque toda requisição com sua versão e a identidade do consumidor, para que, quando a data chegar, você saiba exatamente quais três parceiros ainda estão na v1 e possa chamá-los pelo nome.
A parte da relação
Eis o que o número não faz. Ele não avisa um parceiro que uma mudança está vindo, nem por quê. Não dá a ele um sandbox com o formato novo seis semanas antes. Não conta que um dos seus maiores consumidores é um job em lote que roda uma vez por trimestre e não vai perceber nada até falhar em produção.
Essas coisas são feitas por pessoas, com changelogs, uma página de descontinuação e, para os consumidores que importam, uma conversa. Os times que versionam APIs bem tratam cada versão maior como um projeto com partes interessadas, não como um branch. Conhecem a cadência de deploy dos consumidores. Escrevem o guia de migração antes do código. Observam o tráfego migrar e vão atrás de quem não migrou.
Os times que versionam mal têm URLs v3 lindas e uma v1 que nunca será desligada, porque ninguém sabe quem ainda a chama, e desligar parece cortar um fio no escuro.
Uma versão de API é uma relação com as pessoas que confiaram em você o suficiente para construir sobre o seu trabalho. Escreva do jeito que quiser. Só não confunda a grafia com a promessa.