Na nossa série contínua DevOps for the Win, temos vindo a documentar a nossa jornada de transformação DevOps. No Capítulo 1, introduzimos o Triângulo DevOps: Ferramentas, Arquitetura e Pessoas no Desenvolvimento de Produtos, onde discutimos os elementos fundamentais que impulsionam o sucesso do DevOps. No Capítulo 2, explorámos Os Primeiros Passos na Nossa Transformação DevOps, partilhando como lançámos as bases para mudanças significativas.
Agora, no Capítulo 3, abordamos o próximo grande desafio que encontrámos: Conceber para a Testabilidade. Após 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 se tinha deslocado para o design de software — especificamente, a testabilidade. O trabalho estava a acumular-se na fase de testes, impedindo-nos de alcançar ciclos de lançamento mais rápidos. Este capítulo aprofunda como superámos este desafio e melhorámos a nossa capacidade de entregar software de alta qualidade de forma eficiente.
O Gargalo dos Testes
A principal razão para este atraso foi o facto de os testes serem predominantemente manuais, focados em testes de UI e fluxos de trabalho do utilizador. Uma vez que a UI era geralmente um dos últimos itens a ser concluído, havia pouca ou nenhuma automação implementada, e os testes manuais tornaram-se a única abordagem viável. Casos de teste de UI automatizados (por exemplo, testes Selenium) eram tipicamente 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 nos lançamentos de software.
- A necessidade de múltiplas implementações para permitir testes paralelos por mais de um membro da equipa de QA.
Isto realçou a importância de tornar o código mais testável para otimizar o fluxo de valor. No entanto, alcançar isto revelou-se mais difícil do que tínhamos previsto.
Priorização de Testes de API e Testes Unitários
Para enfrentar este desafio, focámo-nos em duas áreas principais:
- Testes Unitários - Garantir que os componentes individuais pudessem ser testados de forma isolada.
- Testes de API - Reduzir a dependência de testes baseados na interface do utilizador (UI) e de dependências de base de dados.
Testes de API: Antecipar os Testes para um Feedback Mais Rápido
Redefinimos a nossa estratégia de testes de API com estas melhorias chave:
- APIs Bem Definidas- As APIs foram 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 integração criava dependências que atrasavam os testes. Em vez disso, avançámos para uma integração baseada em API utilizando REST sobre HTTP e GraphQL. Isto minimizou o tempo de configuração da base de dados e melhorou a automatização dos testes.
Esta mudança reduziu significativamente os atrasos, permitindo uma automatização mais rápida e testes numa fase inicial, demonstrando que o foco em conceções API-first melhorou tanto a testabilidade como a eficiência.
Testes Unitários: Uma Mudança de Mentalidade
Inicialmente, os nossos esforços de testes unitários levaram a um rápido aumento da cobertura de testes, mas rapidamente identificámos problemas:
- Alguns testes eram superficiais, escritos apenas para cumprir metas de cobertura de código.
- Faltava-lhes uma validação significativa da funcionalidade, de casos-limite e de cenários de falha.
Para contrariar isto, salientámos:
- Formar os programadores sobre como escrever testes valiosos.
- Refatorização de código para melhorar a testabilidade.
Desafios na Escrita de Código Testável
O maior obstáculo para testes unitários eficazes foi 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.
- Fraca separação de responsabilidades.
- Abstração insuficiente.
Estas questões dificultaram o isolamento e o teste eficaz de unidades individuais. Para resolver isto, nós:
- Diretrizes Fornecidas: Partilhámos as melhores práticas sobre:
- Seleção de funções para testes unitários.
- Refatorização de código para melhorar a testabilidade.
- Conceber e usar mocks para facilitar os testes.
- Focados em Melhorias Incrementais: Os programadores foram incentivados a fazer pequenas e significativas alterações para melhorar a testabilidade ao longo do tempo.
Progresso Lento mas Constante
Apesar destes esforços, o progresso foi lento, especialmente para produtos legados. A arquitetura existente limitava o número de testes unitários que podíamos escrever. No entanto, estas ações não visavam apenas melhorar a base de código atual — eram um investimento no futuro:
- Melhorar as competências dos programadores na conceção de código testável garantiu que projetos futuros não sofreriam dos mesmos problemas.
- Melhorias incrementais evitaram interrupções enquanto aumentavam constantemente a cobertura da automatização de 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. Ajuda a aliviar os estrangulamentos na fase de desenvolvimento. No entanto, é importante gerir as expectativas — resultados imediatos podem não ser possíveis, especialmente ao lidar com grandes bases de código legadas. Refatorar esse código sem testes unitários e de regressão suficientes é um grande desafio.
Lições de The Pragmatic Programmer
Para cenários em que iniciar 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 acionáveis:
- Focar em Alterações Incrementais: Concentre-se em refatorar partes pequenas e geríveis da base de código.
- Desacoplar Componentes: Reduza as dependências para tornar as unidades individuais mais fáceis de testar.
- Dividir Funções Monolíticas: Divida funções grandes em unidades mais pequenas e focadas.
- Adotar um Design Modular: Tornar o código mais testável e fácil de manter, melhorando a modularidade.
A mensagem aqui é clara: uma vez que os constrangimentos iniciais tenham sido resolvidos, a testabilidade deve tornar-se uma área de foco principal. Isto irá aliviar as restrições na fase de testes e permitir uma entrega mais rápida de software de alta qualidade.
Perspetivas Futuras
A nossa jornada para melhorar a testabilidade ensinou-nos lições valiosas sobre o papel do design de software na viabilização de transformações DevOps suaves. Ao focarmo-nos na testabilidade, abordámos os gargalos, melhorámos as competências dos programadores e lançámos 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 tornam este investimento valioso.
À medida que continuamos a nossa jornada DevOps, medir o sucesso torna-se o próximo desafio principal. No Capítulo 4, iremos explorar como estabelecemos KPIs e Dashboards para acompanhar o nosso progresso e identificar áreas adicionais para melhoria.
Fique atento enquanto exploramos como a tomada de decisões baseada em dados nos ajudou a refinar os nossos processos DevOps e a otimizar o desempenho!