Tecnologia do DevOps: integração contínua

Os sistemas de software são complexos. Portanto, uma alteração aparentemente simples e independente em um único arquivo pode acarretar efeitos colaterais inesperados em todo o sistema. Quando há muitos desenvolvedores trabalhando em sistemas relacionados, fica difícil coordenar as atualizações de código, podendo ocorrer incompatibilidade entre as alterações de diferentes desenvolvedores.

A prática de integração contínua (CI, na sigla em inglês) foi criada para resolver esses problemas. Ela segue o princípio (em inglês) de que, se algo consome muito tempo e energia, deve ser feito com mais frequência, para que seja menos penoso. Com a criação de rápidos ciclos de feedback e a garantia de que os desenvolvedores trabalhem em lotes pequenos, a CI permite que as equipes produzam software de alta qualidade, visando reduzir o custo de desenvolvimento e manutenção contínuos de software e aumentar a produtividade das equipes.

Como implementar CI

Quando a organização pratica a CI, os desenvolvedores integram todo o trabalho na versão principal da base do código (conhecida como tronco, principal ou linha principal) regularmente. Esta pesquisa (PDF) da DevOps Research and Assessment (DORA) mostra que as equipes se saem melhor quando os desenvolvedores mesclam o trabalho no tronco pelo menos uma vez por dia. Um conjunto de testes automatizados é executado antes e após a mesclagem para validar que as alterações não apresentem bugs de regressão. Se esses testes automatizados falharem, a equipe interromperá o que estiver fazendo para corrigir o problema imediatamente.

A CI garante que o software esteja sempre em estado funcional e que os branches do desenvolvedor não divirjam significativamente do tronco. Os benefícios da CI são significativos: a pesquisa (PDF) mostra que ela proporciona uma maior frequência de desenvolvimento, sistemas mais estáveis e software de qualidade melhor.

Os principais elementos da implementação bem-sucedida da integração contínua são:

  • cada confirmação deve acionar uma versão do software;
  • cada confirmação deve acionar uma série de testes automatizados que fornecem feedback em poucos minutos.

Para implementar esses elementos, você precisa dos seguintes itens:

  • Um processo de criação automatizada. A primeira etapa da CI é providenciar um script automatizado para criar pacotes que possam ser implantados em qualquer ambiente. Os pacotes criados pela versão da CI devem ser confiáveis e usados por todos os processos downstream. Essas versões devem ser numeradas e repetíveis. É preciso executar o processo de criação com êxito pelo menos uma vez por dia.
  • Um conjunto de testes automatizados. Se você não tem nenhum, comece escrevendo alguns testes de aceitação (PDF, em inglês) e unidade que abranjam a funcionalidade valiosa do seu sistema. Certifique-se de que os testes sejam confiáveis. Desse modo, quando eles falharem, você saberá que há um problema real. Quando eles terminarem com êxito, você estará seguro de que não há problemas sérios no sistema. Em seguida, garanta que os testes abranjam toda a nova funcionalidade. A execução deles deve ser rápida, para que os desenvolvedores recebam feedback o quanto antes. A execução dos testes deve ter êxito pelos menos uma vez por dia. Por fim, se você tiver testes de desempenho e aceitação, os desenvolvedores deverão receber feedback deles diariamente.
  • Um sistema de CI que execute a versão e os testes automatizados em cada entrada. O sistema também deve deixar o status visível para a equipe. Isso pode ser feito de forma divertida, por exemplo, usando buzinas ou sinais luminosos para indicar quando a versão está com falha. Não use notificações por e-mail. Muitas pessoas ignoram as notificações por e-mail ou criam um filtro que oculta as notificações. Usar notificações em um sistema de bate-papo é um modo melhor e mais conhecida de conseguir isso.

A integração contínua, conforme definição de Kent Beck (em inglês) e a comunidade de programação extrema em que o termo surgiu, também inclui duas outras práticas, que também preveem um maior desempenho no fornecimento de software:

A CI requer testes automatizados de unidade. Eles devem ser abrangentes a ponto de proporcionar segurança de que o software funcione conforme esperado. Além disso, precisam ser executados em alguns minutos ou menos. Se a execução dos testes automatizados de unidade demorar mais que isso, os desenvolvedores não vão querer executá-los com frequência. Se os testes forem executados com pouca frequência, pode surgir uma falha de teste proveniente de muitas alterações distintas, o que é difícil de depurar. Testes executados com pouca frequência são difíceis de manter.

A criação de conjuntos de testes automatizados de unidade passíveis de manutenção é um trabalho complexo. Um meio de resolver esse problema é praticar o desenvolvimento orientado a teste (TDD, na sigla em inglês), em que os desenvolvedores escrevem testes automatizados que falham a princípio, antes de implementarem o código que faz o teste ter êxito. Há várias vantagens no TDD, uma delas é que os desenvolvedores escrevem código modular fácil de testar, o que reduz o custo de manutenção dos conjuntos de teste automatizados resultantes. Muitas organizações não têm conjuntos de testes automatizados de unidade passíveis de manutenção e, mesmo assim, não praticam TDD.

Objeções à CI

Conforme descrito anteriormente, às vezes a CI é considerada uma prática controversa. Ela exige que os desenvolvedores desmembrem grandes recursos e outras alterações em etapas incrementais menores que possam ser integradas ao tronco com frequência. Isso é uma mudança para os desenvolvedores não acostumados a trabalhar assim. Além disso, quando as equipes passam a usar etapas pequenas, a conclusão de recursos grandes pode demorar mais. Porém, em geral, não é uma boa prática acelerar para que os desenvolvedores declarem um grande recurso como concluído em um branch. O melhor é poder revisar, integrar, testar e implantar as alterações o mais rápido possível. Esse processo gera desenvolvimento e fornecimento de software mais rápido e mais estável (PDF, em inglês) quando as alterações são pequenas e independentes e as ramificações em que residem são de curta duração. Trabalhar em lotes pequenos também garante que os desenvolvedores recebam feedback regularmente sobre o impacto do trabalho deles no sistema como um todo, proveniente de outros desenvolvedores, testers e clientes, bem como de testes automatizados de desempenho e segurança. Com isso, fica mais fácil e rápido detectar, selecionar e corrigir quaisquer problemas.

Mesmo com essas objeções, ajudar as equipes de desenvolvimento de software a implementar integração contínua deve ser a principal prioridade em qualquer organização que queira ingressar na jornada de fornecimento contínuo.

Dificuldades comuns

Algumas dificuldades comuns que impedem a ampla adoção da CI:

  • Não incluir tudo no repositório de código. Tudo o que é preciso para criar e configurar o aplicativo e o sistema deverá estar no seu repositório. Embora possa parecer fora do escopo da CI, isso é uma base importante.
  • Não automatizar o processo de criação. Etapas manuais criam oportunidades de erros e não são documentadas.
  • Não acionar testes rápidos em cada alteração. Testes completos são necessários, mas os testes rápidos (geralmente de unidade) também são importantes para permitir feedback rápido.
  • Não corrigir versões com falha imediatamente. Uma meta essencial da CI é ter uma versão estável que sirva como base de desenvolvimento para todos. Se não for possível corrigir a versão em poucos minutos, a alteração que gerou a falha da versão deverá ser identificada e revertida.
  • Adotar testes de execução muito demorada. A execução dos testes não deve demorar mais que alguns minutos, com um limite superior de aproximadamente 10 minutos, de acordo com a pesquisa do DORA (PDF, em inglês). Se sua versão demora mais que isso, aprimore a eficácia dos testes, adicione mais recursos de computação para executá-los em paralelo ou divida os testes de longa duração em um build separado usando o padrão de pipeline de implantação (em inglês).
  • Não mesclar no tronco com a devida frequência. Muitas organizações têm testes e versões automatizados, mas não aplicam uma mesclagem diária no tronco. Isso acarreta branches de longa duração, que são muito mais difíceis de integrar, e longos ciclos de feedback para os desenvolvedores.

Formas de medir a CI

Os conceitos de CI abordados anteriormente descrevem os meios de medir a eficácia da CI nos sistemas e no ambiente de desenvolvimento, conforme mostrado na tabela a seguir. Reunir essas métricas permite otimizar os processos e as ferramentas utilizadas neles. Com isso, você consegue melhores práticas de CI e ciclos de feedback menores para os desenvolvedores.

Fator a testar O que medir
Confirmações de código acionam uma versão do software A porcentagem de confirmações de código que gera uma versão de software sem intervenção manual.
Confirmações de código acionam uma série de testes automatizados A porcentagem de confirmações de código que gera um conjunto de testes automatizados executados sem intervenção manual.
Versões e testes automatizados executados com êxito todos os dias A porcentagem de versões automatizadas e a porcentagem de testes automatizados que são executados com êxito todos os dias.
Versões atuais estão disponíveis para testers para testagem exploratória A disponibilidade das versões para os testers ou o inverso, isto é, a indisponibilidade das versões para os testers.
Os desenvolvedores recebem feedback dos testes de aceitação e desempenho todos os dias A disponibilidade de feedback proveniente de testes de aceitação e desempenho aos desenvolvedores, ou seja, a porcentagem de testes que fornecem feedback disponível aos desenvolvedores no período de um dia.
Versões com falha são corrigidas imediatamente Tempo decorrido entre a falha da versão e a respectiva correção, seja com uma entrada que corrige o problema ou com a reversão da alteração que causa falha.

A seguir