Como impedir que alterações em uma API interna quebrem outros projetos?

8

Temos de 20 a 30 módulos / soluções independentes. Cada um deles tem cerca de 7 a 10 projetos com diferentes classes, componentes, etc. Estes são todos utilizados internamente em nossa empresa.

Nosso problema é que, quando fazemos uma alteração em um módulo, precisamos garantir a atualização de todos os outros módulos que acessam esse código. Isso é difícil de saber porque está em diferentes bases de código.

Como podemos documentar onde estão todos os usos externos de uma API? Ou impedir que pequenas alterações quebrem outros módulos?

Matemática
fonte
6
Como impedir que alterações na API quebrem o código? Umm ... não mude a API. Realmente, que resposta poderia haver além disso?
@ dan1111 e se precisarmos fazer alterações na API e não soubermos quais outros módulos vamos quebrar, como isso pode ser evitado? fazê-lo
Matemática
O que há de errado em pesquisar na sua base de código por chamadas de método? Se você não possui documentação do que usa o quê, essa é uma maneira muito boa de fazer isso. Você pode escrever facilmente um pequeno script que pesquisa todo o seu código em busca de todas as chamadas e relatórios da API.
1
você pode nos dar mais detalhes? qual linguagem de programação? a compatibilidade do código fonte está ok ou você precisa de compatibilidade binária? "Não altera a assinatura da API existente", mas apenas adiciona novas assinaturas de método para resolver seu problema? O seu idioma suporta "parâmetros padrão"
k3b

Respostas:

13

A solução mais simples, a IMO, é ter um número decente de testes automatizados para cada produto. Ao atualizar uma biblioteca, você executa o conjunto de testes para cada produto. Se os testes falharem, você saberá quais produtos precisam ser atualizados.

Bryan Oakley
fonte
5

Eu sugeriria não tentar documentar isso (pelo menos manualmente) como se você exigisse que as pessoas atualizassem, será necessário um alto nível de precisão para funcionar corretamente. Mas você nunca terá esse nível de precisão, porque adicionar esse tipo de documentação ... não seria divertido e ninguém fará isso.

Algumas opções melhores:

  • Tenha um script que gere uma lista utilizável de todas as chamadas de método em todos os outros módulos, com base nos critérios de pesquisa. Simples, bruto, mas provavelmente eficaz.
  • Algum tipo de sistema de versão em que você não quebra a compatibilidade com versões anteriores, exceto nos principais números de versão. Portanto, se você alterar o método foo da versão 2.1 para a 2.2, todo o código de referência da versão 2.X continuará funcionando. Quando precisar quebrar essa compatibilidade, atualize o número da versão principal (neste caso, 3.0) e anuncie isso a todos os proprietários do projeto responsáveis ​​por outros módulos. Dependendo de como o processo de liberação funciona, isso pode ser simples - ou incrivelmente complicado - de implementar.
  • Faça testes automatizados ou um processo de criação de IC em que cada vez que você pressiona o código, uma construção executa testes de algum tipo. Isso permitirá que você identifique onde os problemas acontecem. Meu palpite é com o que você está descrevendo como o problema que você ainda não tem.
  • Algum tipo de sistema de documentação automática para todos os seus módulos / repositórios

Você também pode considerar que refatorar lentamente as APIs não é tão frágil, mas espero que isso esteja fora do escopo do que você pode razoavelmente realizar se for um indivíduo e tiver mais de 20 módulos em grande escala para trabalhar.

enderland
fonte
1
+1 para controle de versão, além de link para controle de versão semântico
xpy
2

Primeiro de tudo, uma API com usos externos não deve mudar.

Como o @BryanOakley mencionou, o uso de testes de unidade automatizados é muito importante e pode salvar vidas nessas situações. Além disso, algumas sugestões que podem (ou não, de acordo com a situação) ajudá-lo

  • Muitas linguagens (como Java e C #) oferecem Function/Method Overriding. Idiomas como o Python oferecem a você passar (número ilimitado de) argumentos e argumentos de palavras-chave para uma função:

    Java:

    public void disp(char c)
    {
         System.out.println(c);
    }
    
    public void disp(char c, int num)  
    { 
         System.out.println(c + " " + num);
    }
    
    disp("A")
    disp("A", 3)

    Pitão

    def disp(c, *args):
        if args:
            num = args[0]
            print("%s %f" % (c, num))
        else:
            print("%s" % c)
    
    disp("A")
    disp("A", 3)
  • Muitas línguas ofertas public, privatee protectedmétodos. Você pode manipular a chamada de função em uma publicfunção e executar o trabalho em private/protectedfunções.

    Em python, não há definição pública / privada para métodos e funções, mas um sublinhado ( _) principal indica que um método é privado e não deve ser usado externamente. As chamadas externas à API são entregues por um método aberto ao mundo externo e todas as tarefas são realizadas nas chamadas funções locais :

    def format_input(a, b, *args, **kwargs):
        # This function is open to anyone. so use *args and **kwargs to get
        # all possible available arguments. Do not change this function
        # definition and function parameters
        a = _evaluate_input(a)
        b  =_evaluate_input(b)
        # c is not used by all APIs and may not documented in all external
        # API docs. So chech if it was sent as a keyword argument. If yes
        # evalaute it too
        if "c" in kwargs:
            c  =_evaluate_input(kwargs.get("c"))
        _check_extra_arguments(args)
        _check_extra_keyward_arguments(kwargs)
    
    def _evaluate_input(value):
        # This is a private method thus should not be called from the
        # outside world. You can change this method and parameter structure 
        # to fit your needs and do not care for outside world since no
        # outer API should use this function directly.
        ...
    
    def _check_extra_arguments(value):
        # We will check if any extra argument is passed and handle them accordingly
        ...

Como eu disse, uma definição de API que também é usada por aplicativos externos não deve ser alterada com tanta frequência. Você pode procurar maneiras de tornar suas funções externas mais flexíveis, para alterar como a API funciona sem interromper o estado atual.

Anjo caído
fonte
1

Nosso problema é que, quando fazemos uma alteração em um módulo, precisamos garantir a atualização de todos os outros módulos que acessam esse código. Isso é difícil de saber porque está em diferentes bases de código.

Eu sugeriria que isso é impossível saber.

Você é responsável pelos componentes e suas interfaces.
Você não é responsável por tudo e qualquer coisa que possa fazer uso deles.

Como podemos documentar onde estão todos os usos externos de uma API? Ou impedir que pequenas alterações quebrem outros módulos?

Resposta curta? Testes.

Escreva testes que exercitem as interfaces publicadas. Execute-os novamente sempre que fizer uma alteração. Enquanto os testes "passarem", você não quebrou nada. Quando um teste é interrompido (e será) (a) encontre e corrija o problema ou (b) se você puder justificar a alteração como legítima, reescreva o teste para acomodá-lo.

Phill W.
fonte
0

Eu vi e codifiquei APIs com números de versão nos caminhos e / ou nomes das funções.

Dessa forma, você pode ter diferentes versões da API disponíveis - APIs completas e versões diferentes das funções em uma API.

Isso coloca o trabalho de manter todas as versões da API no código da API - nenhum código de outros aplicativos precisa ser alterado além daquele para o qual o novo recurso da API foi produzido.

Eu acho que isso é especialmente importante ao escrever APIs que serão usadas por aplicativos fora da sua organização.

Como exemplo, aqui está um exemplo de código para enviar um SMS usando a API de bulksms:

http://developer.bulksms.com/eapi/code-samples/csharp/send_sms/

a partir daí é a linha:

string url = ".../submission/send_sms/2/2.0";

onde 2 e 2.0 são números de versão da API.

Como essa API é destinada ao uso por muitos clientes de SMS em massa, uma alteração nessa API poderia interromper muitos aplicativos e fazer com que o telefone de suporte tocasse rapidamente.

Mike Vincent
fonte