Aikido

Como evitar quebrar contratos de API pública: mantendo a compatibilidade retroativa

Manutenibilidade

Regra

Evitar quebrar pública API contratos.
Alterações para públicos API endpoints que poderia quebrar
existentes cliente pedidos são de rutura mudanças.
Pense de a API contrato como a promessa - mudando
a promessa depois de clientes dependem dele dela quebra o seu código.

Linguagens suportadas: PHP, Java, C#, Python, JavaScript, TypeScript

Introdução

APIs públicas são contratos entre seu serviço e seus consumidores. Uma vez que os clientes dependem do formato da requisição, estrutura da resposta ou comportamento de um endpoint, alterá-lo quebra o código deles. Alterações que quebram a compatibilidade forçam todos os clientes a atualizar simultaneamente, o que é frequentemente impossível quando você não controla os clientes. Aplicativos móveis não podem ser atualizados à força, integrações de terceiros precisam de tempo para migração, e sistemas legados podem nunca ser atualizados.

Por que isso importa

Interrupção e confiança do cliente: Mudanças disruptivas na API causam falhas imediatas em aplicações cliente em produção. Usuários experimentam erros, perda de dados ou interrupções completas do serviço. Isso prejudica a confiança entre provedores e consumidores de API e viola o contrato implícito de que APIs estáveis permanecem estáveis.

Custos de Coordenação: Coordenar mudanças disruptivas entre várias equipes de clientes é caro e lento. Cada equipe precisa de tempo para atualizar o código, testar as mudanças e implantar. Para APIs públicas com clientes desconhecidos (aplicativos móveis, integrações de terceiros), a coordenação é impossível.

Proliferação de versões: Alterações disruptivas mal gerenciadas levam à manutenção de múltiplas versões de API simultaneamente. Cada versão requer caminhos de código, testes, documentação e correções de bugs separados, multiplicando exponencialmente a carga de manutenção.

Exemplos de código

❌ Não-conforme:

// Version 1: Original API
app.get('/api/users/:id', async (req, res) => {
    const user = await db.users.findById(req.params.id);
    res.json({ id: user.id, name: user.name });
});

// Version 2: Breaking change - renamed field
app.get('/api/users/:id', async (req, res) => {
    const user = await db.users.findById(req.params.id);
    res.json({
        id: user.id,
        fullName: user.name  // Breaking: 'name' renamed to 'fullName'
    });
});

Por que está errado: Renomear 'name' para 'fullName' quebra todos os clientes existentes que esperam o campo 'name'. O código do cliente acessando response.name receberá 'undefined', causando erros. Essa mudança força todos os clientes a atualizar simultaneamente ou falhar.

✅ Compatível:

// Version 2: Additive change - keeps old field, adds new
app.get('/api/users/:id', async (req, res) => {
    const user = await db.users.findById(req.params.id);
    res.json({
        id: user.id,
        name: user.name,           // Keep for backward compatibility
        fullName: user.name        // Add new field (deprecated 'name')
    });
});

// Or use API versioning
app.get('/api/v2/users/:id', async (req, res) => {
    const user = await db.users.findById(req.params.id);
    res.json({ id: user.id, fullName: user.name });
});

Por que isso importa: Mantendo o antigo nome campo mantém compatibilidade retroativa enquanto adiciona Nome Completo para novos clientes. Alternativamente, criando um novo endpoint versionado (/api/v2/) permite alterações disruptivas sem afetar clientes existentes que ainda utilizam /api/v1/.

Conclusão

Evolua APIs através de mudanças aditivas: adicione novos campos, adicione novos endpoints, adicione parâmetros opcionais. Quando mudanças disruptivas forem inevitáveis, use o versionamento de API para executar versões antigas e novas simultaneamente. Deprecie campos antigos com cronogramas claros e guias de migração antes de removê-los.

FAQs

Dúvidas?

Quais mudanças são consideradas de quebra?

Remover campos de respostas, renomear campos, alterar tipos de campo (string para número), tornar parâmetros opcionais obrigatórios, mudar códigos de status HTTP para condições existentes, alterar requisitos de autenticação e modificar formatos de resposta de erro. Adicionar novos parâmetros de requisição obrigatórios ou remover endpoints inteiramente também são breaking changes.

Como adicionar novos campos obrigatórios sem quebrar os clientes?

Torne o novo campo opcional inicialmente com um valor padrão sensato. Documente a mudança e dê aos clientes tempo para adotá-la. Após tempo suficiente (6-12 meses para APIs públicas), torne o campo obrigatório em uma nova versão da API. Nunca torne campos opcionais existentes obrigatórios sem versionamento.

Qual é a diferença entre versionamento de API e depreciação?

O versionamento cria um novo endpoint (/v2/users) ao lado do antigo, permitindo que ambos coexistam. A depreciação marca um endpoint ou campo antigo como obsoleto, mantendo-o funcional, com um cronograma para eventual remoção. Use o versionamento para grandes mudanças, e a depreciação para a desativação gradual de funcionalidades menores.

Por quanto tempo devo manter versões de API depreciadas?

Para APIs públicas, mantenha versões descontinuadas por pelo menos 12-18 meses. Para APIs internas, coordene com as equipes de clientes para um cronograma de migração. Sempre forneça aviso prévio (mínimo de 3-6 meses) antes de remover endpoints descontinuados. Monitore as métricas de uso para garantir que os clientes migraram antes do desligamento.

Posso alterar a ordem dos campos de resposta?

Sim, a ordem dos campos de um objeto JSON não faz parte do contrato da API. Clientes bem desenvolvidos analisam JSON pelo nome do campo, não pela posição. No entanto, teste exaustivamente, pois alguns clientes mal desenvolvidos podem depender da ordem dos campos. Para arrays, a ordem geralmente importa e não deve mudar, a menos que documentado.

Como versionar APIs sem alterações no caminho da URL?

Utilize cabeçalhos HTTP: Accept: application/vnd.myapi.v2+json ou cabeçalhos personalizados como API-Version: 2. Parâmetros de consulta também funcionam: /api/users?version=2. A negociação de conteúdo via cabeçalhos é mais limpa, mas mais difícil de testar em navegadores. Escolha uma estratégia e use-a consistentemente.

Fique seguro agora

Proteja seu código, Cloud e runtime em um único sistema centralizado.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados da varredura em 32 segundos.