
Henry Ford disse uma vez: "O melhor carro é um carro novo". Então, no grupo de empresas Tinkoff, pensamos em lançamentos de software. A inércia no processo de entrega de recursos e correções urgentes, mais cedo ou mais tarde, gera uma grande dívida técnica para o cliente e geralmente termina com a estagnação do projeto como um todo.
Garantir um indicador de tempo até o mercado e manter a qualidade não é uma tarefa fácil. Do meu ponto de vista, é impossível construir trilhos imediatamente sobre os quais será possível realizar mudanças rápida e convenientemente muitos meses após o início. O crescimento de um projeto geralmente é acompanhado por um aumento no número de pessoas que trabalham nele, o que significa que ele cria uma fonte potencial de caos em seus lançamentos.
Nossa experiência dificilmente vale a pena considerar como instruções para o sucesso, mas várias idéias pareciam interessantes o suficiente para eu compartilhar com você. Vamos começar.
O ponto de partida da nossa história é tecnicamente o seguinte. O sistema consiste em vários serviços, cuja base de código é vasculhada por cerca de 30 pessoas - várias equipes divididas por áreas de negócios, por exemplo, atendendo pessoas físicas e jurídicas. Tentarei ignorar os detalhes técnicos e arquitetônicos do projeto para focar no próprio processo de lançamento.
Como trabalhamos no Gitflow , o artigo será principalmente interessante para aqueles que escolheram esse método de entrega específico.
Os problemas
O primeiro problema que encontramos está relacionado à automação insuficiente de processos . A implementação de todas as atividades de liberação em nossa equipe está vinculada à função de gerente de liberação (RM).
Aqui estão alguns deles:
- Deixando ramificações de liberação e criando tags.
- Mesclando com master e desenvolvendo branches.
- Construa e implemente artefatos incluídos no release.
- Comunicação com especialistas envolvidos no processo - por exemplo, com controle de qualidade ou administradores.
Essas tarefas rotineiras exigem mais e mais recursos à medida que o projeto se expande (no nosso caso, um aumento no número de serviços); portanto, o primeiro passo é automatizar tudo o que pode ser automatizado. Integramos com a ferramenta CI / CD para desviar e mesclar ramificações de lançamento, lançar construções de teste e implantar artefatos usando o botão; com a ferramenta de rastreamento de tarefas e o messenger corporativo para notificação oportuna do participante responsável pela próxima etapa - por exemplo, altere o status da tarefa e configure o gancho para enviar uma notificação.
Trabalhos de automação Gitflow Um exemplo de notificação em um messenger O gerente de versão ainda precisará resolver manualmente os possíveis conflitos decorrentes da junção de ramificações, mas as liberações rápidas e frequentes devem reduzir seu número a nada.
O próximo problema está testando .
Um dos critérios de construção para nós é a execução bem-sucedida dos testes. É habitual dividir os testes em pelo menos dois tipos: unidade e integração.
Os testes de unidade permitem verificar a exatidão dos módulos individuais do código-fonte do programa, que na prática se resume a verificar um ou mais métodos que possuem uma conexão lógica óbvia.
Os testes de integração geralmente verificam a operacionalidade de uma cascata inteira desses módulos, ou seja, o funcionamento de um recurso inteiro no lado do cliente. Por exemplo, se uma interface restante foi implementada em uma tarefa, verificaremos a operacionalidade da autorização, a desserialização da solicitação em si, a validade dos campos transmitidos, a integração com outros serviços e bancos de dados, bem como a própria lógica de negócios. À primeira vista, pode parecer que esses testes sejam muito auto-suficientes e possam cobrir todas as possíveis áreas problemáticas. Não há necessidade de entender como cada bloco individual funciona, e a interface chamada encapsula toda a lógica para obter uma resposta simples sobre a saída: se funciona ou não.
De fato, eles criam uma série de problemas adiados, eis alguns deles:
- A participação de um grande número de componentes testados afeta proporcionalmente o tempo de montagem e a execução desses testes.
- Encapsular a lógica do teste geralmente leva ao fato de que é difícil garantir a correção do resultado do teste. Muitas vezes, personalizamos o teste para o resultado e, ainda mais frequentemente, o resultado corresponde à expectativa devido a efeitos colaterais aleatórios.
- A relevância dos dados de teste é perdida.
- As integrações com sistemas de terceiros, especialmente em ambientes de teste, geralmente caem. Isso anula o tempo gasto na execução, pois nem sempre é óbvio: trata-se de uma queda ou avaria temporária causada por nossas alterações.
Para a maioria dos problemas já surgiu uma solução. Mas, como sempre, as soluções não vêm sem restrições adicionais ou novos problemas.
Escolher os testes certos para você e implementá-los corretamente é uma tarefa muito difícil. Além disso, é importante encontrar um equilíbrio entre a qualidade da cobertura e a velocidade de construção para otimizar seus lançamentos.
No nosso caso, optamos por um híbrido. Continuamos a elevar todos os componentes necessários para um teste completo, lavando simultaneamente todas as integrações possíveis. Para salvar os contratos da API, usamos o Pact e para verificar a integração com o banco de dados - Testcontainers .
Essa abordagem para escrever testes como resultado resultou em uma solução para o terceiro problema - muito tempo para o teste manual de uma tarefa . A estabilidade dos testes híbridos levou à idéia de atrair um engenheiro de controle de qualidade no estágio de especificação da tarefa de compilação de casos de teste - isso permitirá que eles sejam ignorados no estágio de teste manual. A integração com produtos úteis, como TestRail e Allure, tornou-se um tipo de ponte entre o desenvolvedor e o testador. Um contrato é criado, cuja execução é refletida passo a passo no relatório gerado durante a montagem de teste.
TestRail. Detalhes da compilação de teste TestRail. Casos de teste implementados pelo desenvolvedor Allure Relatório de criação de teste automático Allure Informações para cada teste em execução e detalhamento passo a passo da execução Resta conectar os relatórios à sua ferramenta de rastreamento de tarefas para obter um rastreamento transparente das tarefas. Uma história clara também reduzirá o tempo necessário para compilar e implementar testes para futuras tarefas relacionadas.
Jira. Casos de teste anexados automaticamente à tarefa Dessa forma, os engenheiros de controle de qualidade economizam tempo suficiente para se concentrar no teste de casos excepcionais e na integração com outros sistemas.
O último problema está relacionado a isso. Para testes manuais, todas as tarefas se fundem a partir das ramificações de recursos no desenvolvimento e lançamento de uma implantação na bancada de testes.
Em primeiro lugar, com essa abordagem, você não pode falar sobre testes puros do recurso, pois, paralelamente, as alterações relacionadas de outros desenvolvedores podem se desenvolver.
Em segundo lugar, ao liberar a ramificação de liberação, pode ocorrer que o controle de qualidade não tenha tempo para testar algumas das tarefas. Há uma opção: reverter as alterações afetadas por essas tarefas ou diminuir a velocidade da versão até o final do teste.
Você terá que fazer essa escolha o tempo todo, a menos que isole seu ambiente de teste . É importante que não haja alterações no componente em teste além daquelas feitas pela tarefa. No nosso caso, precisamos poder escolher um ou mais serviços cujas filiais tenham sido alteradas e gerenciar o roteamento dentro do cluster enviando apenas as solicitações necessárias do controle de qualidade para essas instâncias. A tarefa é complicada se mecanismos de balanceamento já estiverem sendo usados no cluster, que também precisam ser levados em consideração.
Tendo percebido essa oportunidade, começamos a realizar testes manuais diretamente em ramos de recursos separados: implantar o serviço desejado durante a duração do teste, integrando-se ao ambiente geral de forma isolada. Ao mesclar apenas tarefas prontas na ramificação de desenvolvimento e nos livrar dos bloqueios, começamos a determinar quais alterações devem ser incluídas no release e quais não.
Vale a pena notar que é improvável que essa solução se torne uma bala de prata para as equipes que fazem alterações quase nos mesmos arquivos. Nesse caso, o armazenamento de recursos é inversamente proporcional ao número de conflitos que surgem durante a fusão. Na prática, com lançamentos bastante frequentes, isso acontece muito raramente em uma equipe com cinquenta desenvolvedores.
Em vez de saída
Resumindo, podemos identificar as principais abordagens que podem ajudar na aceleração de lançamentos:
- Automação de operações de rotina.
- Transparência dos processos para todos os envolvidos.
- O equilíbrio necessário entre velocidade e qualidade ao escrever testes automatizados.
- Tempo reduzido para testes manuais.
- Isolamento do ambiente para teste.
É importante entender que na escolha de várias abordagens, princípios e estratégias sempre vale a pena começar a partir do contexto do seu problema. Existem muitas maneiras igualmente confiáveis e sofisticadas de agilizar a entrega do seu software ao cliente. Uma tentativa de responder a novas dificuldades emergentes levou às conclusões descritas acima. Para nós, eles foram o primeiro passo em direção a uma abordagem mais ousada para liberar lançamentos .