Na nossa série DevOps for the Win, temos estado a documentar a nossa viagem de transformação DevOps. No Capítulo 1, apresentámos o Triângulo DevOps: Ferramentas, Arquitetura e Pessoas no Desenvolvimento de Produtosonde discutimos os elementos fundamentais que impulsionam o sucesso do DevOps. No Capítulo 2, explorámos Os primeiros passos da nossa transformação DevOpspartilhando como lançámos as bases para mudanças significativas.
Agora, no Capítulo 3, abordamos o próximo grande desafio que encontramos: projetar para testabilidade. Depois de melhorar a estrutura da nossa equipa, refinar a nossa definição de concluído e implementar a análise automatizada de código, percebemos que o principal gargalo no nosso pipeline havia mudado para o projeto de software — especificamente, a testabilidade. O trabalho estava a acumular-se na fase de testes, us alcançar ciclos de lançamento mais rápidos. Este capítulo aprofunda como superámos esse desafio e melhorámos a nossa capacidade de entregar software de alta qualidade com eficiência.
O estrangulamento dos testes
A principal razão para este atraso foi o facto de os testes serem predominantemente manuais, centrando-se nos testes da IU e nos fluxos de trabalho dos utilizadores. Uma vez que a IU era normalmente um dos últimos itens a ser concluído, havia pouca ou nenhuma automação, e os testes manuais tornaram-se a única abordagem viável. Os casos de teste automatizados da IU (por exemplo, testes Selenium) eram normalmente escritos após a conclusão dos testes manuais, principalmente para fins de regressão.
Esta dependência de testes manuais resultou em
- Atrasos significativos nas versões de software.
- A necessidade de múltiplas implementações para permitir testes paralelos por mais de um membro QA .
Isto realçou a importância de tornar o código mais testável para otimizar o fluxo de valor. No entanto, conseguir isso acabou por ser mais difícil do que tínhamos previsto.
Priorização de testes de API e testes de unidade
Para responder a este desafio, concentrámo-nos em duas áreas principais:
- Testes unitários - Garantir que os componentes individuais possam ser testados isoladamente.
- Testes de API - Reduzir a dependência de testes baseados na interface do utilizador e dependências de bases de dados.
Teste de API: Mudar para a esquerda para obter um feedback mais rápido
Redefinimos a nossa estratégia de teste de API com estas melhorias fundamentais:
- APIs bem definidas APIs APIs concebidas para serem simples, bem documentadas e disponíveis numa fase inicial do desenvolvimento.
- Evitar a base de dados como ponto de integração - A dependência de bases de dados para a integração criou dependências que atrasaram os testes. Em vez disso, mudamos para a integração baseada em API usando REST sobre HTTP e GraphQL. Isso minimizou o tempo de configuração do banco de dados e melhorou a automação de testes.
Esta mudança reduziu significativamente os atrasos, permitindo uma automatização mais rápida e a realização de testes na fase inicial, demonstrando que a concentração em concepções que dão prioridade à API melhorou tanto a capacidade de teste como a eficiência.
Testes unitários: Uma mudança de mentalidade
Inicialmente, os nossos esforços de testes unitários conduziram a um rápido aumento da cobertura dos testes, mas rapidamente identificámos problemas:
- Alguns testes eram superficiais, escritos apenas para cumprir objectivos de cobertura de código.
- Faltava-lhes uma validação significativa da funcionalidade, dos casos extremos e dos cenários de falha.
Para contrariar esta situação, sublinhámos:
- Educar os programadores sobre como escrever testes valiosos.
- Refactoring do código para melhorar a testabilidade.
Desafios na escrita de código testável
O maior obstáculo a um teste unitário eficaz era a falta de testabilidade na própria base de código. Os principais problemas incluíam:
- Funções grandes e monolíticas.
- Componentes fortemente acoplados.
- Má separação das preocupações.
- Abstração insuficiente.
Estes problemas dificultaram o isolamento e o teste eficaz de unidades individuais. Para resolver este problema, nós:
- Diretrizes fornecidas: Partilhámos as melhores práticas sobre:
- Seleção de funções para testes unitários.
- Refactoring do código para melhorar a testabilidade.
- Conceber e utilizar simulações para facilitar os testes.
- Focado em melhorias incrementais: Os programadores foram encorajados a efetuar pequenas alterações significativas para melhorar a testabilidade ao longo do tempo.
Progresso lento mas constante
Apesar destes esforços, o progresso foi lento, especialmente para os produtos antigos. A arquitetura existente limitava o número de testes unitários que podíamos escrever. No entanto, estas acções não se destinavam apenas a melhorar a base de código atual - eram um investimento no futuro:
- A melhoria das competências dos programadores na conceção de código testável garantiu que os projectos futuros não sofreriam os mesmos problemas.
- As melhorias incrementais evitaram interrupções, ao mesmo tempo que aumentaram de forma constante a cobertura da automatização dos testes.
- Uma mudança de mentalidade ajudou as equipas a ver a testabilidade não como um fardo, mas como uma necessidade para o sucesso sustentável do DevOps.
Para equipas e organizações que embarcam numa transformação DevOps, a testabilidade é uma área crítica de foco. Ela ajuda a aliviar os gargalos na fase de desenvolvimento. No entanto, é importante gerir as expectativas - resultados imediatos podem não ser possíveis, especialmente quando se lida com grandes bases de código legadas. A refacção desse código sem testes unitários e de regressão suficientes é um grande desafio.
Lições de O Programador Pragmático
Para cenários em que começar com uma nova base de código não é viável, The Pragmatic Programmer: Your Journey to Mastery, de Andy Hunt e David Thomas, oferece orientações práticas:
- Alterações Incrementais: Concentre-se na refacção de partes pequenas e gerenciáveis da base de código.
- Desacoplar componentes: Reduzir as dependências para tornar as unidades individuais mais fáceis de testar.
- Dividir funções monolíticas: Dividir as grandes funções em unidades mais pequenas e mais específicas.
- Adotar uma conceção modular: Tornar o código mais fácil de testar e manter, melhorando a modularidade.
A mensagem aqui é clara: uma vez resolvidos os estrangulamentos iniciais, a capacidade de teste deve tornar-se uma área de foco principal. Isto irá aliviar os constrangimentos na fase de teste e permitir uma entrega mais rápida de software de alta qualidade.
Olhando para o futuro
A nossa jornada para melhorar a testabilidade us ensinou lições us sobre o papel do design de software em possibilitar transformações DevOps tranquilas. Ao nos concentrarmos na testabilidade, resolvemos gargalos, aprimoramos as habilidades dos programadores e estabelecemos as bases para o sucesso futuro. Embora os resultados imediatos possam ser lentos, os benefícios a longo prazo em termos de qualidade, eficiência e crescimento da equipa fazem com que esse investimento valha a pena.
À medida que continuamos a nossa jornada DevOps, medir o sucesso torna-se o próximo desafio chave. No Capítulo 4, vamos explorar como estabelecemos KPIs e Dashboards para acompanhar o nosso progresso e identificar outras áreas de melhoria.
Fique ligado enquanto mergulhamos em como a tomada de decisões baseada em dados us ajudou us os nossos processos de DevOps e otimizar o desempenho!