17 de set. de 2025·8 min de leitura

Versionamento de API para apps móveis: evolua endpoints com segurança

Versionamento de API para apps móveis explicado com um plano de rollout simples, mudanças retrocompatíveis e passos de depreciação para que versões antigas continuem funcionando.

Versionamento de API para apps móveis: evolua endpoints com segurança

Por que mudanças na API quebram usuários móveis

Apps móveis não atualizam todos de uma vez. Mesmo que você lance uma correção hoje, muita gente continuará usando uma versão antiga por dias ou semanas. Alguns desligam atualizações automáticas. Alguns têm pouco espaço. Alguns simplesmente não abrem o app com frequência. O tempo de revisão nas lojas de apps e os rollouts em etapas adicionam ainda mais atraso.

Essa lacuna importa porque seu backend normalmente evolui mais rápido que os clientes móveis. Se o servidor muda um endpoint e o app antigo ainda o chama, o app pode quebrar mesmo que nada tenha mudado no telefone do usuário.

A quebra raramente aparece como uma mensagem de erro clara. Geralmente parece uma dor de produto do dia a dia:

  • Login ou cadastro falham depois de um deploy no backend
  • Listas aparecem vazias porque um campo foi renomeado ou movido
  • O app crasha ao ler um valor ausente
  • Pagamentos falham porque a validação ficou mais rígida
  • Recursos desaparecem silenciosamente porque o formato da resposta mudou

O objetivo do versionamento é simples: continue entregando melhorias no servidor sem forçar todo mundo a atualizar imediatamente. Trate sua API como um contrato de longo prazo. Versões novas do app devem funcionar com comportamento novo do servidor, e versões antigas devem continuar funcionando tempo suficiente para os ciclos reais de atualização.

Para a maioria dos apps de consumo, espere suportar múltiplas versões do app ao mesmo tempo. Apps internos às vezes podem andar mais rápido, mas raramente é instantâneo. Planejar sobreposição mantém rollouts graduais tranquilos em vez de transformar cada release do backend em um pico de suporte.

O que “compatível” significa para um contrato de API

Um contrato de API é a promessa entre seu app móvel e seu servidor: qual URL chamar, quais entradas são aceitas, como a resposta se parece e o que cada campo significa. Quando o app depende dessa promessa e o servidor a muda, os usuários sentem isso como crashes, dados faltando ou recursos que deixam de funcionar.

Uma mudança é compatível quando versões antigas do app podem continuar usando a API sem alterar código. Na prática, isso significa que o servidor ainda entende o que apps antigos enviam e ainda retorna respostas que apps antigos conseguem parsear.

Uma forma rápida de separar mudanças seguras de mudanças arriscadas:

  • Quebras: remover ou renomear um campo, mudar um tipo (número para string), tornar um campo opcional em obrigatório, mudar o formato de erro, endurecer validação de um modo que apps antigos não atendam.
  • Geralmente seguras: adicionar um novo campo opcional, adicionar um novo endpoint, aceitar formatos de requisição antigos e novos, adicionar novos valores de enum (apenas se o app tratar valores desconhecidos como “outro”).

Compatibilidade também precisa de um plano de fim de vida. Aposentar comportamento antigo é aceitável, mas deve ser agendado (por exemplo, “manter v1 por 90 dias depois que v2 for lançada”) para que você evolua sem surpreender os usuários.

Abordagens comuns de versionamento e seus trade-offs

Versionamento é sobre dar um contrato estável para builds antigas enquanto você segue em frente. Há algumas abordagens comuns, e cada uma coloca a complexidade em um lugar diferente.

Versionamento na URL

Colocar a versão no caminho (como /v1/ e /v2/) é o mais fácil de ver e debugar. Também funciona bem com caching, logs e roteamento porque a versão faz parte da URL. A desvantagem é que times podem acabar mantendo handlers paralelos por mais tempo do que o esperado, mesmo quando a diferença é pequena.

Versionamento por header

Com versionamento por header, o cliente envia a versão em um header (por exemplo, um Accept ou um header customizado). As URLs ficam limpas e você pode evoluir a API sem mudar todo caminho. A desvantagem é visibilidade: proxies, logs e humanos frequentemente perdem a versão a não ser que você seja cuidadoso, e clientes móveis precisam definir o header em cada requisição de forma confiável.

Versionamento por parâmetro de query

Versionamento por query (como ?v=2) parece simples, mas fica bagunçado. Parâmetros são copiados em bookmarks, ferramentas de analytics e scripts, e você pode acabar com múltiplas “versões” flutuando sem propriedade clara.

Se quiser uma comparação rápida:

  • URL versioning: mais fácil de inspecionar, mas pode criar APIs paralelas de longa duração
  • Header versioning: URLs limpas, mas mais difícil de solucionar problemas
  • Query versioning: rápido para começar, fácil de usar errado

Feature flags são outra ferramenta. Elas deixam você mudar comportamento por trás do mesmo contrato (por exemplo, um novo algoritmo de ranking) sem criar uma nova versão de API. Mas não substituem versionamento quando o formato de requisição ou resposta precisa mudar.

Escolha uma abordagem e mantenha-a. Consistência importa mais que a “escolha perfeita”.

Regras práticas para mudanças retrocompatíveis

A mentalidade mais segura é: clientes antigos devem continuar funcionando mesmo que nunca conheçam sua nova funcionalidade. Isso normalmente significa adicionar coisas, não mudar o que já existe.

Prefira mudanças aditivas: novos campos, novos endpoints, parâmetros opcionais. Quando você adiciona algo, torne-o verdadeiramente opcional do ponto de vista do servidor. Se um app antigo não enviar, o servidor deve se comportar exatamente como antes.

Alguns hábitos evitam a maioria das quebras:

  • Adicione campos, mas não mude o tipo ou o significado de campos existentes.
  • Trate entradas ausentes como normais e use defaults sensatos.
  • Ignore campos de requisição desconhecidos para que clientes antigos e novos coexistam.
  • Mantenha formatos de erro estáveis. Se precisar mudá-los, versione o payload de erro.
  • Se o comportamento mudar, introduza um novo endpoint ou uma nova versão em vez de um ajuste “silencioso”.

Evite mudar o significado de um campo existente sem um bump de versão. Por exemplo, se status=1 antes significava “paid” e você o reaproveita para significar “authorized”, apps antigos tomarão decisões erradas e você pode só notar quando os usuários reclamarem.

Renomes e remoções precisam de um plano. O padrão mais seguro é manter o campo antigo e adicionar o novo lado a lado por um tempo. Preencha ambos nas respostas, aceite ambos nas requisições e registre quem ainda usa o campo antigo. Remova-o somente depois do fim da janela de depreciação.

Um hábito pequeno mas poderoso: quando introduzir uma nova regra de negócio obrigatória, não torne o cliente responsável por ela no primeiro dia. Aplique a regra no servidor com um default primeiro e, depois que a maioria dos usuários atualizar, passe a exigir que o cliente envie o novo valor.

Defina uma política simples de versionamento e depreciação

Publique mudanças sem causar falhas
Adicione novos campos e endpoints mantendo seu contrato estável.
Construir agora

Versionamento funciona melhor quando as regras são chatas e documentadas. Mantenha a política curta o suficiente para que produto, mobile e backend realmente a sigam.

Comece com janelas de suporte. Decida por quanto tempo manterá versões antigas da API ativas após o lançamento de uma nova (por exemplo, 6–12 meses), mais exceções (problemas de segurança, mudanças legais).

Em seguida, defina como avisar clientes antes de quebrá-los. Escolha um sinal de depreciação e use-o sempre. Opções comuns incluem um header de resposta como Deprecation: true com uma data de aposentadoria, ou um campo JSON como "deprecation": {"will_stop_working_on": "2026-04-01"} em respostas selecionadas. O que importa é consistência: clientes detectam, dashboards reportam e equipes de suporte explicam.

Defina uma versão mínima de app suportada e seja explícito sobre a aplicação dessa regra. Evite bloqueios-surpresa. Uma abordagem prática:

  1. Retorne um aviso suave (por exemplo, um campo que aciona um prompt de atualização no app).
  2. Aplique somente após um prazo comunicado.

Se bloquear requisições, retorne um payload de erro claro com mensagem humana e um código legível por máquina.

Finalmente, decida quem pode aprovar mudanças breaking e qual documentação é necessária. Mantenha simples:

  • Um dono aprova mudanças breaking.
  • Uma nota curta explica o que mudou, quem é afetado e o caminho de migração.
  • Um plano de testes inclui ao menos uma versão antiga do app.
  • Uma data de aposentadoria é definida quando a depreciação começa.

Plano passo a passo para rollout que mantém apps antigos funcionando

Construa clientes com sua API
Crie apps web e nativos que permaneçam resilientes a mudanças no backend.
Construir App

Usuários móveis não atualizam no primeiro dia. A abordagem mais segura é lançar uma nova API deixando a antiga intacta, e então migrar o tráfego gradualmente.

Primeiro, defina o que a v2 muda e trave o comportamento da v1. Trate v1 como uma promessa: mesmos campos, mesmos significados, mesmos códigos de erro. Se v2 precisa de um formato de resposta diferente, não ajuste v1 para combiná-la.

Em seguida, rode v2 em paralelo. Isso pode significar rotas separadas (como /v1/... e /v2/...) ou handlers separados por trás do mesmo gateway. Mantenha lógica compartilhada em um lugar, mas mantenha contratos separados para que um refactor na v2 não mude acidentalmente a v1.

Depois, atualize o app móvel para preferir v2. Construa um fallback simples: se v2 retornar “not supported” (ou outro erro conhecido), tente v1. Isso ajuda durante rollouts em etapas e quando redes móveis ficam inconsistentes.

Após liberar o app, monitore adoção e erros. Checagens úteis incluem:

  • volume de requisições v1 vs v2 por versão do app
  • taxa de erro e latência da v2
  • falhas de parsing de resposta
  • crashes ligados a telas que fazem requisições

Quando v2 estiver estável, adicione avisos claros de depreciação para v1 e comunique um cronograma. Aposente v1 somente quando o uso cair abaixo de um limite aceitável (por exemplo, abaixo de 1–2% por várias semanas).

Exemplo: você muda GET /orders para suportar filtros e novos statuses. v2 adiciona status_details enquanto v1 permanece igual. O app novo chama v2, mas se encontrar um caso extremo faz fallback para v1 e ainda mostra a lista de pedidos.

Dicas de implementação no servidor

A maioria das quebras durante rollout acontece porque o tratamento de versão está espalhado por controllers, helpers e código de banco. Mantenha a decisão “qual versão é esta requisição?” em um único lugar, e o resto da lógica previsível.

Coloque o roteamento de versão atrás de um único portão

Escolha um sinal (segmento de URL, header ou número de build do app) e normalize-o cedo. Roteie para o handler correto em um único módulo ou middleware para que cada requisição siga o mesmo caminho.

Um padrão prático:

  • Parse a versão uma vez (e logue).
  • Mapeie versão para um handler (v1, v2, ...) em um registro único.
  • Mantenha utilitários compartilhados agnósticos à versão (parsing de datas, checagens de auth), não lógica de formato de resposta.

Cuidado ao compartilhar código entre versões. Corrigir um bug da v2 em código “compartilhado” pode mudar acidentalmente o comportamento da v1. Se a lógica afeta campos de saída ou regras de validação, mantenha-a versionada ou cubra com testes específicos por versão.

Mantenha mudanças de dados compatíveis durante o rollout

Migrações de banco devem funcionar para ambas as versões ao mesmo tempo. Adicione colunas primeiro, backfille quando necessário e só depois remova ou aperte constraints. Evite renomear ou mudar significados no meio do rollout. Se precisar alternar formatos, considere escrever ambos os formatos por um período até que a maioria dos clientes migre.

Torne erros previsíveis. Apps antigos frequentemente tratam erros desconhecidos como “algo deu errado”. Use códigos de status consistentes, identificadores de erro estáveis e mensagens curtas que ajudem o cliente a decidir o que fazer (retry, re-auth, mostrar atualização).

Finalmente, previna campos faltantes que apps antigos não enviam. Use defaults seguros e valide com detalhes de erro claros e estáveis.

Considerações no app móvel que afetam versionamento

Vá mais rápido com no-code
Construa ferramentas internas e portais com mais velocidade, com APIs que podem evoluir com segurança.
Experimentar AppMaster

Como usuários podem ficar em builds antigas por semanas, o versionamento deve assumir que múltiplas versões do cliente vão atingir seus servidores ao mesmo tempo.

Um grande ganho é tolerância no lado do cliente. Se o app crasha ou falha ao parsear quando o servidor adiciona um campo, você sentirá isso como bugs “aleatórios” no rollout.

  • Ignore campos JSON desconhecidos.
  • Trate campos ausentes como normais e use defaults.
  • Lide com nulls com segurança (campos podem se tornar nullable durante migrações).
  • Não dependa da ordem de arrays a menos que o contrato garanta isso.
  • Mantenha o tratamento de erro amigável ao usuário (um estado de retry é melhor que uma tela em branco).

O comportamento de rede também importa. Durante o rollout você pode ter brevemente versões mistas do servidor atrás de load balancers ou caches, e redes móveis amplificam pequenos problemas.

Defina regras claras de timeout e retry: timeouts curtos para chamadas de leitura, ligeiramente maiores para uploads, e retries limitados com backoff. Torne idempotência padrão para chamadas de criação ou pagamentos para que um retry não duplique submissões.

Mudanças de auth são a forma mais rápida de bloquear apps antigos. Se você mudar o formato de token, scopes necessários ou regras de sessão, mantenha uma janela de sobreposição onde tokens antigos e novos funcionem. Se precisar rotacionar chaves ou claims, planeje uma migração em etapas, não um corte no mesmo dia.

Envie metadata do app em cada requisição (por exemplo, versão do app e plataforma). Isso facilita retornar avisos direcionados sem bifurcar toda a API.

Monitoramento e rollouts em fases sem surpresas

Um rollout em fases só funciona se você enxergar o que diferentes versões do app estão fazendo. O objetivo é simples: saber quem ainda está em endpoints antigos e pegar problemas antes que atinjam todo mundo.

Comece rastreando uso por versão da API diariamente. Não conte apenas requisições totais. Rastreie dispositivos ativos e detalhe endpoints críticos como login, perfil e pagamentos. Isso mostra se uma versão antiga ainda está “viva” mesmo que o tráfego total pareça pequeno.

Depois, observe erros por versão e tipo. Um salto em 4xx costuma indicar incompatibilidade de contrato (campo obrigatório mudou, valores de enum alteraram, regras de auth). Um salto em 5xx normalmente aponta para regressões no servidor (deploy ruim, queries lentas, falhas em dependências). Ver ambos por versão ajuda a decidir a correção certa rapidamente.

Use rollouts graduais nas lojas de apps para limitar o raio do impacto. Aumente exposição em passos e observe os mesmos dashboards após cada etapa (por exemplo, 5%, 25%, 50%). Se a versão mais nova mostrar problemas, pare o rollout antes de virar outage.

Tenha gatilhos de rollback documentados antes, não decididos na hora da crise. Gatilhos comuns incluem:

  • taxa de erro acima de um limite por 15–30 minutos
  • queda na taxa de sucesso de login (ou aumento em falhas de refresh de token)
  • aumento de falhas em pagamentos (ou timeouts no checkout)
  • pico de tickets de suporte ligados a uma versão específica
  • aumento de latência em um endpoint crítico

Mantenha um playbook curto para incidentes relacionados a versões: quem acionar, como desabilitar uma flag arriscada, qual release do servidor reverter e como estender a janela de depreciação se clientes antigos ainda estiverem ativos.

Exemplo: evoluindo um endpoint durante um release real

Prototipe o fluxo completo
Teste seu plano de rollout com uma API funcional e apps clientes a partir de um único projeto.
Prototipar agora

Checkout é uma mudança clássica. Você começa com um fluxo simples, depois adiciona um passo de pagamento novo (por exemplo, autenticação mais forte) e renomeia campos para alinhar com a linguagem do negócio.

Suponha que seu app chame POST /checkout.

O que permanece em v1 vs o que muda em v2

Na v1, mantenha a requisição e comportamento existentes para que builds antigas possam finalizar pagamentos sem surpresas. Na v2, introduza o novo fluxo e nomes mais limpos.

  • v1 mantém: amount, currency, card_token e uma resposta simples como status=paid|failed.
  • v2 adiciona: payment_method_id (substituindo card_token) e um campo next_action para o app lidar com uma etapa extra (verificar, tentar novamente, redirecionar).
  • v2 renomeia: amount para total_amount e currency para billing_currency.

Apps antigos continuam funcionando porque o servidor aplica defaults seguros. Se uma requisição v1 não conhece next_action, o servidor completa o pagamento quando possível e retorna o mesmo resultado no estilo v1. Se o novo passo for obrigatório, v1 recebe um código de erro claro e estável como requires_update em vez de uma falha genérica confusa.

Adoção, aposentadoria e rollback

Monitore adoção por versão: qual parcela das chamadas de checkout bate na v2, taxas de erro e quantos usuários ainda usam builds que suportam apenas v1. Quando o uso da v2 estiver consistentemente alto (por exemplo, 95%+ por várias semanas) e o uso de v1 baixo, escolha uma data para aposentar v1 e comunique (notas de release, mensagens in-app).

Se algo der errado após o lançamento, o rollback deve ser tranquilo:

  • Redirecione mais tráfego para o comportamento da v1.
  • Desative o novo passo de pagamento com uma flag no servidor.
  • Continue aceitando ambos os conjuntos de campos e registre o que foi convertido automaticamente.

Erros comuns que causam quebras silenciosas

Padronize seus releases
Transforme sua checklist de depreciação em um fluxo de lançamento repetível.
Iniciar um projeto

A maioria das falhas em APIs móveis não é estrondosa. A requisição pode até ter sucesso, o app continua funcionando, mas usuários veem dados faltando, totais errados ou botões que não fazem nada. Esses problemas são difíceis de notar porque geralmente atingem versões antigas do app durante um rollout gradual.

Causas comuns:

  • Mudar ou remover campos (ou tipos) sem um plano de versão claro.
  • Tornar um novo campo de requisição obrigatório imediatamente, fazendo apps antigos serem rejeitados.
  • Fazer uma migração de banco assumindo que apenas o app novo existe.
  • Aposentar v1 com base em installs, não em uso ativo.
  • Esquecer jobs em background e webhooks que ainda enviam payloads antigos.

Um exemplo concreto: seu campo de resposta total era uma string ("12.50") e você o muda para número (12.5). Apps novos ficam ok. Apps antigos podem tratá-lo como zero, esconder ou crashar em certas telas. Se você não monitorar erros por versão do app, isso passa batido.

Checklist rápido e próximos passos

Versionamento é menos sobre nomes elegantes de endpoint e mais sobre repetir as mesmas verificações de segurança a cada release.

Checagens rápidas antes do release

  • Mantenha mudanças aditivas. Não remova nem renomeie campos que apps antigos já leem.
  • Forneça defaults seguros para que campos novos ausentes se comportem como o fluxo antigo.
  • Mantenha respostas de erro estáveis (status + forma + significado).
  • Trate enums com cuidado e não mude o significado de valores existentes.
  • Reproduza algumas requisições reais de versões antigas do app e confirme que as respostas continuam parseáveis.

Checagens rápidas durante o rollout e antes da aposentadoria

  • Monitore adoção por versão do app. Você quer uma curva clara de migração de v1 para v2, não uma linha plana.
  • Observe taxas de erro por versão. Um pico geralmente indica parsing ou validação que quebrou clientes antigos.
  • Corrija o endpoint com mais falhas primeiro, depois amplie o rollout.
  • Aposente só quando o uso ativo estiver realmente baixo, e comunique a data.
  • Remova código de fallback por último, após a janela de aposentadoria.

Escreva sua política de versionamento e depreciação em uma única página e transforme o checklist em um gate de release que seu time usa em cada lançamento.

Se você constrói ferramentas internas ou apps voltados ao cliente com uma plataforma no-code, ainda vale a pena tratar a API como um contrato com janela de depreciação clara. Para times que usam AppMaster (appmaster.io), manter v1 e v2 lado a lado costuma ser mais fácil porque você pode regenerar backend e clientes conforme mudam os requisitos, mantendo contratos antigos ativos durante o rollout.

FAQ

Por que mudanças no backend quebram apps móveis mesmo quando os usuários não atualizaram nada?

Usuários móveis não atualizam todos ao mesmo tempo, então builds antigas continuam chamando seu backend mesmo depois de você fazer mudanças. Se você alterar um endpoint, validação ou formato de resposta, essas builds antigas não conseguem se adaptar e falham de formas que parecem telas vazias, crashes ou pagamentos que não passam.

O que "retrocompatível" realmente significa para uma API móvel?

"Compatível" significa que um app antigo pode continuar fazendo as mesmas requisições e ainda receber respostas que consegue interpretar e usar corretamente, sem alterações de código. O modelo mental mais seguro é tratar sua API como um contrato: você pode adicionar capacidades novas, mas não deve alterar o significado dos campos e comportamentos existentes para clientes atuais.

Quais são as mudanças que mais comumente causam quebra em APIs móveis?

Uma mudança é breaking quando altera algo de que um app existente depende — por exemplo remover ou renomear campos, mudar o tipo de um campo, endurecer validação de modo que requisições antigas falhem, ou trocar o formato do payload de erro. Se um app antigo não conseguir analisar a resposta ou satisfazer as regras da requisição, é uma quebra mesmo que o servidor esteja “funcionando”.

Devo usar versionamento por URL ou por header para uma API móvel?

Versionamento na URL costuma ser o padrão mais simples porque a versão fica visível em logs, ferramentas de debugging e roteamento, e é difícil “esquecer” de enviá-la. Versionamento por header também funciona, mas pode ser mais fácil de perder em troubleshooting e exige que cada requisição do cliente defina corretamente o header.

Por quanto tempo devemos manter versões antigas da API ativas?

Escolha uma janela de suporte clara que combine com o comportamento real de atualização móvel e cumpra-a; muitas equipes escolhem meses, não dias. O essencial é ter uma data de aposentadoria publicada e medir uso ativo para não decidir na base do achismo quando desligar uma versão antiga.

Qual é uma forma prática de avisar clientes que uma versão da API será aposentada?

Use um sinal de depreciação consistente para que clientes e dashboards detectem com confiabilidade — por exemplo um header de resposta estável ou um pequeno campo JSON que inclua uma data de aposentadoria. Mantenha simples e previsível para que suporte e produto possam explicar sem vasculhar implementação.

Como podemos evoluir respostas sem quebrar versões antigas do app?

Prefira mudanças aditivas: acrescente campos opcionais ou novos endpoints, e mantenha os campos antigos funcionando com o mesmo significado. Quando precisar renomear, mantenha ambos os campos em paralelo por um período e preencha ambos para que apps antigos não percam dados enquanto os novos migram.

Como lidar com mudanças de banco de dados enquanto v1 e v2 estiverem ativas?

Projete migrações de banco de dados para que ambas as versões da API possam operar simultaneamente: adicione colunas primeiro, preencha os dados quando necessário e só depois endureça restrições ou remova campos antigos. Evite renomear ou mudar significados no meio do rollout, pois uma versão pode acabar escrevendo dados que a outra não lê.

O que os apps móveis devem fazer para serem mais resilientes a mudanças na API?

Torne o cliente tolerante: ignore campos JSON desconhecidos, trate campos ausentes como normais com defaults seguros e lide com nulls sem crashes. Isso reduz bugs “aleatórios” durante rollouts quando o servidor adiciona campos ou as respostas variam brevemente em deploys graduais.

O que devemos monitorar durante um rollout em fases, e quando é seguro aposentar a v1?

Monitore uso e erros por versão de API e por versão do app, especialmente para login e pagamentos, e amplie rollouts somente quando os dados estiverem estáveis. Um plano seguro mantém o comportamento de v1 bloqueado, roda v2 em paralelo e migra clientes gradualmente com fallback claro até que a adoção permita aposentar v1 com confiança.

Como aplicar isso em ferramentas no-code ou plataformas como AppMaster?

Mesmo em plataformas no-code, trate a API como um contrato com janela de depreciação clara. Para times que usam AppMaster (appmaster.io), manter v1 e v2 lado a lado costuma ser mais simples porque você pode regenerar backend e clientes conforme os requisitos mudam, mantendo contratos antigos ativos durante o rollout.

Fácil de começar
Criar algo espantoso

Experimente o AppMaster com plano gratuito.
Quando estiver pronto, você poderá escolher a assinatura adequada.

Comece
Versionamento de API para apps móveis: evolua endpoints com segurança | AppMaster