Desenvolver softwares envolve mais do que o conhecimento de programação propriamente dito: é preciso que cada desenvolvedor saiba como integrar suas contribuições ao “conjunto da obra”, trabalhando com responsabilidade e espírito de equipe para que, ao invés de um grande emaranhado de linhas de códigos, o resultado seja um belo, coeso e bem estruturado sistema.
Como evitar bugs e gargalos nesse processo, em que dezenas de novas versões do software são criadas simultaneamente? Como conduzir esse processo com harmonia, permitindo a realização de testes constantes que não atrapalhem o andamento do trabalho?
A Continuous Integration (CI, “Integração Contínua”) é uma resposta a essas demandas. Em linhas gerais, trata-se de uma prática de trabalho em que os desenvolvedores integram suas modificações ao branch principal (mainline), diversas vezes ao dia, para que sejam verificados e os eventuais erros sejam detectados e corrigidos com agilidade. A Integração Contínua traz imensos ganhos em produtividade e qualidade, mas implantá-la pode ser desafiador, já que representa um conjunto complexo de ferramentas, processos, competências e cultura.
O que é Integração Contínua (Continuous Integration – CI)?
O termo Continuous Integration teria sido proposto pela primeira vez em 1991 pelo engenheiro de software Grady Booch, que defendia a prática de integrar frequentemente à mainline os códigos dos desenvolvedores (embora não necessariamente várias vezes ao dia, como se faz na prática moderna da CI). Com o passar do tempo, a integração frequente se tornou uma das práticas típicas, dando base a processos mais dinâmicos.
Para resumir ao máximo, podemos entender a CI como uma prática de desenvolvimento em que os programadores integram continuamente suas alterações ao repositório principal compartilhado. Naturalmente, é preciso tomar uma série de cuidados e providências para que todo esse processo ocorra de maneira harmoniosa e eficiente.
Nesse processo, é necessário um sistema de controle de versão. Esse recurso permite que os desenvolvedores façam testes de unidade locais em seus códigos antes da integração e recebam feedback de outros membros da equipe.
Sistemas de CI criam e executam testes nas novas versões de código, revelando erros e facilitando uma correção ágil.
Há uma série de princípios que dão base à CI e que revelam as vantagens da prática. Eis os principais:
- Simplificação
- Automação
- Diagnóstico precoce de bugs
- Compartilhamento
- Otimização
Simplificar o processo como um todo ajuda a reduzir as possibilidades de erros. Automatizar testes e processos agiliza a entrega e evita enganos. O diagnóstico precoce de bugs é uma das principais motivações para a implementação da CI. O compartilhamento de informações entre as equipes tende a evitar redundância e descompasso no trabalho. Por fim, a otimização é uma consequência natural da prática correta da CI.
Em termos práticos, a CI facilita a identificação e resolução de bugs no processo de desenvolvimento, ao incentivar atualizações menores e testes frequentes. Em termos mais “psicológicos”, tende a aumentar o senso de cooperação entre os membros das equipes, já que facilita o feedback e a comunicação ao longo do desenvolvimento.
Em relação aos testes, ajuda a diminuir tanto os custos quanto o tempo necessário para realizá-los, aumentando a disponibilidade da equipe para focar mais na qualidade do produto como um todo.
Contar com a compreensão e a concordância das equipes é essencial para que a CI seja implementada com sucesso. E, para facilitar o processo de implementação, é recomendável fazê-lo gradualmente, ao invés de adotar a CI da noite para o dia.
Continuous Delivery (Entrega Contínua)
A Entrega Contínua é, por assim dizer, um “passo além” da CI: é um processo de desenvolvimento de softwares em que as alterações de código são, de forma automatizada, preparadas para serem liberadas para produção.
Implementada de forma correta, entrega aos desenvolvedores um artefato de compilação pronto para implantação (deploy). Isso é possível graças à automatização de testes que vão além dos testes de unidade – como de Interface do Usuário (user interface), carga, confiabilidade de APIs e entre outros.
Testes diferentes podem demandar “ambientes” diferentes e massas de dados apropriadas. Outro recurso necessário para a Entrega Contínua é a automação do processo de lançamento. Daí vem a necessidade de um sistema desenhado estrategicamente.
Com a automatização promovida pela Entrega Contínua, o deploy é naturalmente simplificado e já não é preciso levar dias para prepará-lo. Outra vantagem é que os lançamentos de atualizações se tornam mais frequentes, o que acelera o ciclo de feedback por parte dos usuários (desenvolvimento -> lançamento -> feedback -> atualizações).
Finalmente, como as atualizações são menores, há menos pressão na hora de tomar decisões a respeito do software. Assim, encoraja-se uma iteração mais rápida.
Para se implantar a Entrega Contínua é preciso ter uma base de CI firme e bem estabelecida. Além disso, os testes disponíveis para os seus desenvolvedores devem ser capazes de cobrir todos os pontos críticos – de modo que o software esteja “perfeito” quando chegar ao final do processo.
É importante também que o produto use feature flags, de modo que funcionalidades incompletas não afetem os usuários na produção.
Continuous Deployment (Implantação Contínua)
A Continuous Deployment é uma prática similar à Continuous Delivery, mas com um importante passo extra: o deploy (ou seja, o lançamento da nova versão para os usuários) é automatizado, sem a necessidade de uma aprovação manual. Naturalmente, porém, ele só ocorre quando a versão passa por todos os testes necessários.
Com isso, o “dia de lançamento” é eliminado e o intervalo entre a produção do software e o feedback do público diminui consideravelmente.
Como deixa de ser necessário interromper o desenvolvimento para preparar o deploy, o processo ganha agilidade. As atualizações, por serem menores, passam a ser menos “arriscadas” e fáceis de corrigir caso algum erro escape dos testes.
Por conta dos lançamentos constantes, a própria imagem da organização é beneficiada – afinal, o usuário sabe que só uma equipe competente e bem alinhada é capaz de lançar atualizações frequentes e com boa qualidade.
Para se chegar a esse ponto com segurança, a cultura de testes da empresa deve estar afiada. Além disso, os processos de documentação ligados ao software e suas atualizações devem ser ágeis também, a ponto de conseguir acompanhar os lançamentos.
A recomendação do uso de feature flags feita na seção anterior permanece válida também para a implantação de Continuous Deployment.
Colocando em prática
Listamos uma série de benefícios trazidos pelas práticas de Continuous Integration, Continuous Delivery e Continuous Deployment. Contudo, há um obstáculo muito comum para sua implementação: o pensamento “por que mexer em um time que está ganhando?” que muitas organizações adotam.
Nessas horas, cabe a pergunta: sua organização está, de fato, “ganhando”? Está bem do jeito que está? Ou na verdade está em um processo de (como diria o programador Ian Buchanan) “frustração contínua”? Eis o diagnóstico desse caso:
- Os membros da equipe fazem seus commits na master preocupados, já imaginando que logo virá uma bronca por causa de algum bug que escapou
- A master é instável
- Conforme os códigos se acumulam, os bugs ficam mais e mais escondidos, difíceis de detectar e (pior ainda) resolver
- Os responsáveis pelos testes têm dificuldade de se inteirar do estágio de desenvolvimento do software e aprovar uma build
- É preciso “congelar” o código para poder fazer o lançamento… enquanto o resto da equipe continua trabalhando, apenas parando de fazer commits, que acabam se acumulando
No fim das contas, o grande problema está na acumulação de modificações que acabam complicando os commits e os testes e atrapalhando todo o processo. Implementar a Continuous Delivery, porém, exige uma estratégia, já que envolve modificar a cultura da organização e o fluxo de trabalho.
O conceito de continuidade envolve duas questões centrais: receber um feedback a cada commit e consertar a build onde houver problema. Para isso é preciso evitar que as alterações de código se acumulem e permitir que sejam testadas em ambientes seguros, de acordo com a fase de desenvolvimento.
Já no começo é interessante criar ambientes de desenvolvimento, isolados, em que o programador tenha liberdade para codificar, errar, realizar alguns testes (como unitários e de aceitação) e corrigir os erros sem afetar a mainline. É relativamente fácil fazer isso por meio de máquinas virtuais.
Para manter a agilidade do processo, entra em cena o ambiente de testes – que pode ser criado em um servidor de CI. A ideia é ter um “clone” do repositório central onde são realizados testes automaticamente a cada commit. Logo que se identifica um erro, os membros da equipe responsáveis por aquela parte do desenvolvimento são notificados e podem tomar as devidas providências.
O terceiro ambiente a ser criado é o de staging. Deve ser montado para ser o mais próximo possível do “mundo real” (ou seja, do ambiente de produção, onde o software de fato será usado). Além de novos testes, no ambiente de staging é possível demonstrar features para clientes, antes do deploy.
O quarto e último ambiente é o de produção. Uma vez nas mãos dos usuários, o software passa pelos testes “definitivos”, levando a feedbacks valiosos feitos pelos usuários no dia a dia.
Aqui chegamos a outro ponto fundamental: a automatização de testes. Realizar todos (ou mesmo a maioria) dos processos manualmente tende a criar um gargalo no fluxo de trabalho, levando aos problemas já mencionados anteriormente na “frustração contínua”.
Da mesma forma que acontece com os outros processos, porém, a automatização deve ser feita de maneira gradual, de modo a facilitar o trabalho dos desenvolvedores. Os membros responsáveis pelos testes podem ser excelentes “guias” nesse processo, apontando o que deve ou não ser automatizado, e em que ordem de prioridade.
Ao longo de toda a implementação, é importante contar com o entendimento e o apoio dos profissionais da sua equipe – afinal, eles estarão vivenciando as mudanças na prática, diariamente. Investir em treinamento, escolher ferramentas confiáveis e reforçar os benefícios das novas práticas certamente irá ajudar na consolidação da mudança.
Conforme o sistema e a cultura da CI e das CDs são implementados, cria-se um círculo virtuoso: os commits, devidamente testados em seus ambientes, crescem em qualidade e são entregues mais rápida e frequentemente ao usuário final, que dá seu feedback e leva a uma melhora contínua no desenvolvimento.