O texto abaixo é a uma tradução autorizada do artigo Integração Contínua escrito pelo Martin Fowler. Para acessar a versão original em inglês, clique aqui . Caso você tenha sugestões para tornar a tradução melhor, compartilhe através da seção de comentários no final da página.
The text bellow is an authorized translation of the article Continuous Integration written by Martin Fowler. To access the original english version, click here . If you have any suggestion to make the translation better, share it via comments section at the end of the page.
Construindo um componente usando Integração Continua
Para mim, a forma mais fácil de explicar o que Integração Contínua (IC) é e como trabalha é mostrando um pequeno exemplo de como isso funciona com o desenvolvimento de uma pequena parte do sistema. Vamos assumir que eu tenho que fazer alguma coisa para uma parte do sistema, não importa realmente que tarefa é – por enquanto vou supor que isso é pequeno e que pode ser feito um poucas horas ( vamos explorar tarefas maiores e outros problemas mais tarde ). Começo pegando uma cópia atual do código fonte e colocando na minha máquina de desenvolvimento. Faço isso utilizando um sistema de controle de código para ter uma cópia de trabalho a partir do código principal.
O parágrafo acima irá fazer sentido para pessoas que usam sistema de controle de código, mas não será para aqueles que não usam. Então deixe-me explicar rapidamente: um sistema de controle de código mantém todo o código fonte de um projeto em um repositório. O estado atual do sistema geralmente é chamado de mainline( ou linha principal). Em qualquer momento um desenvolvedor pode fazer uma cópia da versão principal em sua máquina, e isso é chamado de check out. A cópia em uma máquina de um desenvolvedor é chamada de cópia de trabalho (na maior parte de tempo você estará atualizando sua cópia de trabalho junto a principal).
Agora eu pego minha cópia de trabalho e faço o que preciso para completar minha tarefa. Isso irá consistir em alterar o código de produção e também adicionar ou alterar os testes automatizados. Integração Contínua assume um alto nível de testes que são automatizados no software: uma facilidade que eu chamo de self-testing code ( ou código auto-testável). Frequentemente estes usam uma versão da popular framework de teste xUnit.
Um vez que eu terminei (e isso geralmente acontece em vários lugares quando eu estou trabalhando), eu faço uma build (ou desenvolvimento de uma versão) automatizada na minha máquina de desenvolvimento. Assim, o código fonte em minha cópia de trabalho é pego, compilado e transformado em um executável – e então é executado os testes automáticos. Somente se todas as builds e testes não tiverem erros que tudo será considerado bom.
Com uma boa build, eu posso então pensar em colocar minhas alterações no repositório. A questão é, claro, que outras pessoas podem fazer (e geralmente fazem) mudanças na versão principal antes de eu ter a chance de fazer um commit ( ou atualizar o repositório principal com a minha versão do código). Sendo assim, primeiro eu atualizo minha cópia de trabalho com as mudanças feitas pelos outros e crio uma nova build. Se as mudanças deles chocarem com as minhas mudanças, será exibido erros tanto na compilação quanto nos testes. Neste caso é minha a responsabilidade de corrigir estes erros e repetir o processo até que minha cópia local esteja sincronizada com a versão principal.
Uma vez que tenho as alterações na minha própria build de uma cópia devidamente sincronizada eu posso finalmente fazer o commit na versão principal, que atualiza o repositório. Entretanto meu commit não termina meu trabalho. Nós fazemos uma build novamente, mas agora em uma maquina de integração baseada na versão principal do código. Somente com o sucesso desta build poderemos dizer que as minhas alterações foram feitas. Sempre existe uma chance de eu ter esquecido alguma coisa na minha máquina e o repositório não ser atualizado corretamente. Somente quando minha build de alterações for lançada com sucesso na máquina de integração é que o meu trabalho terminou. A build de integração pode ser executada manualmente por mim ou ser feita automaticamente usando o Cruise.
Se um choque ocorrer entre as versões de dois desenvolvedores, isso geralmente é pego quando o segundo desenvolvedor atualizar sua cópia local com as builds feitas pelos outros. Caso não, a build de integração deve falhar. De qualquer modo o erro é detectado rapidamente. Nesse momento a tarefa mais importante é consertar os problemas, e fazer sua build voltar a funcionar corretamente. Em um ambiente de Integração Contínua você nunca deve ter uma build de integração com falhas por muito tempo. Um bom time deve ter muitas builds corretas por dia. Más builds podem ocorrer de tempos em tempos, mas devem ser consertadas rapidamente.
Práticas da Integração Contínua
A história anterior é uma apresentação da Integração Contínua e de como ela funciona diariamente. Colocar isso tudo pra funcionar sem problemas envolve obviamente mais coisas do que foi dito. Eu vou focar agora em práticas chaves que efetivarão a IC .
Manter um único repositório de código.
Projetos de softwares envolvem vários arquivos que precisam serem “orquestrados” juntos para se construir um produto. Manter todos esse arquivos exige um grande esforço, particularmente quando existem várias pessoas envolvidas. Logo não é surpresa que através de anos equipes de desenvolvimento tem construído ferramentas para gerenciar tudo isso. Estas ferramentas – chamadas de ferramentas de gerenciamento de código fonte, gerenciamento de configuração, sistema de controle de versão, repositórios ou outros nomes – são uma parte integrante na maioria dos projetos de desenvolvimento. A triste e surpreendente questão é que essas ferramentas não fazem parte de todos os projetos. Isto é raro, mas eu participo de projetos que não usam algo como um sistema mas usam um combinação confusa de drivers locais e compartilhados.
Portanto, como um passo fundamental, tenha certeza de usar um sistema decente de controle de código fonte. Custo não é um problema por causa da boa qualidade das ferramentas open-source disponíveis. O repositório open-source atual que escolho é o Subversion ( o antigo CVS continua sendo usado largamente, e ainda é bem melhor que nada, mas o Subversion é uma escolha moderna). É interessante como eu converso com desenvolvedores e descubro que a maioria dos gerenciadores de código fonte comerciais são menos aceitos que o Subversion. A única ferramenta que eu tenho ouvido pessoas falarem que é digna de ser paga é o Perforce.
Uma vez que você tenha um sistema de gerenciamento de código fonte, tenha certeza de que isso esteja em um lugar conhecido por todos, para pegarem o código fonte. Ninguém deve sequer perguntar “Onde estão os arquivos?”. Tudo deve estar no repositório.
Embora muitos times usem repositórios, um erro comum que vejo é que eles não colocam tudo no repositório. Se as pessoas usam um repositório, eles vão por o código lá, mas tudo que você precisa para fazer uma build deve estar incluído la: scripts de teste, arquivos de configuração, esquema de banco de dados, scripts de instalação e bibliotecas de terceiros. Eu conheci projetos que colocavam seus compiladores no repositório (importante nos dias antigos de vários compiladores de C++). O regra básica é que você consiga por o projeto em uma máquina virgem, fazer um checkout (ou pegar uma cópia de todo o código) e conseguir rodar o sistema completamente. Somente poucas coisas devem ser feitas na máquina virgem – geralmente coisas que são estáveis, grandes e complicadas de instalar . Um sistema operacional, ambiente de desenvolvimento Java ou sistema básico de banco de dados são exemplos típicos.
Você precisa por tudo que a build precisa no sistema de controle de código, entretanto você pode por também outros materiais com que as pessoas geralmente trabalham. Configurações de IDE são boas para colocar lá porque este é um modo fácil para as pessoas compartilharem as mesmas configurações.
Outra característica dos sistemas de controle de versão é que eles permitem você criar múltiplos branches (ou linhas de desenvolvimento) , para manipular diferentes fluxos de desenvolvimento. Isso é uma característica útil, quer dizer essencial – mas frequentemente é usada de maneira abusiva o que coloca as pessoas em problemas. Reserve-se a usar os branches ao mínimo. Tenha uma versão principal: um único branch do projeto atualmente em desenvolvimento. Basicamente todos devem trabalhar fora desta linha principal a maior parte do tempo ( um número considerável de branches são correção de bugs de uma versão futura e experimentos temporários).
Geralmente você deve guardar no controle de código tudo que você precisa para executar tudo, mas nada do que você já executou. Algumas pessoas guardam os produtos das builds no controle de código, mas eu considero que isso é um sinal, uma indicação de um problema mais profundo, geralmente uma incapacidade para recriar as builds confiantemente.
Automatize a Build
Frequentemente pegar os fontes e transformá-los em um sistema rodando é um processo complicado envolvendo compilação, movimentação de arquivos, carregar schemas nas bases de dados e por ai vai. Entretanto, boa parte das tarefas nesta parte do desenvolvimento de software pode ser automatizada – e portanto deve ser automatizada. Pedir pessoas para digitarem comandos estranhos ou clicar em caixas de dialogo é uma perda de tempo e uma forma suscetível a reprodução de erros.
Ambientes automatizados de builds são características comuns de sistemas. O mundo Unix tem feito isso há décadas, a comunidade Java desenvolveu o Ant, a comunidade .NET desenvolveu o Nant e agora tem o MSBuild. Esteja certo de que você pode gerar e lançar o seu sistema rodando os scripts em um único comando. Um erro comum é não colocar tudo na build automatizada. A build deve incluir pegar o schema do banco de dados do repositório e colocá-lo no ambiente de execução. Eu vou elaborar minha regra anterior: qualquer um deve ser capaz de ir para uma máquina virgem, pegar as fontes do repositório, emitir um único comando e ter um sistema rodando na sua máquina.
Scripts de build podem ter varias formas e frequentemente são particulares a uma plataforma ou comunidade, mas eles não necessariamente ter que ser assim. Embora a maioria dos nossos projetos são em Java e usam o Ant, alguns tem usando Ruby ( o sistema Ruby Rake é um script build muito bom). Nós tivemos bons resultados automatizando um projeto anterior usando Microsoft COM com o Ant.
Dependendo do que você precisa, você pode querer que diferentes tipos de coisas estejam na build. Você lançar um sistema com ou sem código de teste, ou com diferentes conjuntos de testes. Alguns componentes podem ser construídos isoladamente. Um script de build deve permitir você usar métodos alternativos em casos diferentes.
Muitos de nós usamos IDEs, e a maioria das IDEs tem algum tipo de processo de gerenciamento de build dentro delas. Entretanto esses arquivos são sempre proprietários a IDE e frequentemente frágeis. Além disso eles necessitam da IDE para funcionar. Não existe problema se os usuários da IDE configurarem seus próprios arquivos de projetos e usarem para o desenvolvimento individual. Entretanto é essencial ter uma build principal que é usável em um servidor e executável a partir de outros scripts. Logo, em um projeto Java nós estamos livres para ter desenvolvedores fazendo a build em seus IDEs, mas a build principal deve usar o Ant para assegurar que isso possa rodar um servidor de desenvolvimento.
Faça sua Build ser auto-testável
Tradicionalmente uma build significa compilação, linkagem e todo o material adicional necessário para ter um programa executando. Um programa pode rodar, mas isso não significa que foi feito da maneira certa. Linguagens modernas estaticamente tipadas podem achar muitos bugs, mas alguma coisa pode acabar passando desapercebida. Um bom modo para pegar erros mais rápida e eficientemente é incluir testes automáticos no processo de build. Testes não são perfeitos (claro), mas eles podem pegar muitos bugs – o suficiente para ser útil.
Leitores regulares do meu trabalho irão notar que sou um grande fã de TDD e XP, entretanto eu quero ressaltar que nenhuma dessas abordagens são necessárias para ganhar os benefícios de um código auto-testável. Ambas as abordagens acertam que se escreva testes antes que você escreva o código, para então fazer os testes passarem – desta forma os testes exploram muito mais o design do sistema do que capturam erros. Isto é uma coisa boa, mas não é necessariamente do propósito da IC, onde nós temos requisitos mais fracos de código auto-testável (embora TDD é minha maneira preferida de produzir código auto-testável).
Para ter o código auto-testável você necessitará de uma suite de testes automatizados que possa checar uma grande parte da base do código para achar problemas. Os testes necessitam estar aptos para rodarem com um simples comando e serem checados automaticamente. O resultado da suite dos testes em execução deve indicar se estes falharam. Para uma build ser auto-testável a falha de um teste deve fazer com que a build falhe.
Através dos últimos anos em que o TDD tem crescido, ele popularizou a família de ferramentas open-source xUnit que são ideais para esse tipo de teste. As ferramentas xUnit tem sido muito valorosa para nós na ThoughtWorks e eu sempre sugiro as pessoas que as usem. Estas ferramentas, que teve Kent Beck como pioneiro, tornam fácil para você configurar um ambiente completo para testes automatizados.
Ferramentas xUnit são certamente o ponto inicial para tornar seu código auto-testável. Você deve também procurar por outras ferramentas que focam em testes mais voltados ao software em funcionamento. Atualmente, existem um grande número delas, incluindo FIT, Selenium, Sahi, Watir, FITnesse e muitas outras que eu não vou listar aqui.
Cada um lança suas modificações todos os dias.
Integração antes de tudo trata sobre comunicação. Integração permite os desenvolvedores dizerem uns aos outros sobre as alterações que eles fizeram. Frequentemente comunicação permite pessoas descobrirem rapidamente como as alterações aconteceram.
O único pré-requisito para um desenvolvedor lançar suas alterações na versão principal é que ele ele consiga executar perfeitamente o código. Isso claro, inclui passar pelos testes da build. Como com qualquer ciclo de lançamento de código, o desenvolvedor primeiro atualiza sua cópia de trabalho para coincidir com a versão principal, resolve qualquer conflito e então gera a build em sua máquina local. Se a build passar, então ele estará liberado para lançar suas alterações na versão principal.
Fazendo isso frequentemente, os desenvolvedores irão encontrar rapidamente se existe algum conflito entre as versões. O segredo para solucionar problemas rapidamente é encontrá-los rapidamente. Com desenvolvedores lançando suas alterações a cada hora, um conflito pode ser detectado dentro de poucas horas desde seu acontecimento, e em um ponto não muito avançado – o que o torna fácil de ser solucionado. Conflitos que não são detectados por semanas podem ser difíceis de serem resolvidos.
O fato de que você gerou uma build quando você atualizou sua cópia de trabalho significa que você detectou os problemas de compilação como também conflitos textuais. Desde que a build seja auto-testável, você também detecta conflitos no código em execução. Os últimos conflitos são conflitos particularmente incômodos de se encontrar se eles persistirem não detectados por um longo tempo em seu código. Desde que existam apenas algumas horas de diferença entre os commits, existem apenas alguns locais onde o problema pode estar se escondendo. Além disso, desde que muita coisa não tenha sido mudada, você pode usar um diff-debugging para lhe ajudar a encontrar o problema.
Minha regra geral é que cada desenvolvedor deve atualizar o repositório todo dia. Na prática, geralmente é útil os desenvolvedores lançarem suas atualizações com mais frequência que uma vez ao dia. Quanto mais frequentemente você fizer um commit, menos lugares você terá que procurar por erros de conflito, e mais rapidamente você os consertará.
Commits frequentes encorajam os desenvolvedores a quebrar seus trabalhos em pequenos pedaços de poucas horas cada. Isso ajuda a dividir o progresso e causa um senso de progresso. Frequentemente as pessoas começam sentir que não conseguem fazer alguma coisa significativa em poucas horas, mas nós temos encontrado que esta teoria e prática ajudam os desenvolvedores a aprender.
Cada commit deve atualizar o repositório principal em uma máquina de integração contínua
Usando commits diariamente, um time tem builds frequentemente testadas. Isso deveria significar que o repositório principal esta em um estado saudável. Na prática, entretanto, coisas continuam dando errado. Um razão é a disciplina: pessoas não estão atualizando suas versões e fazendo uma build antes de fazerem seus commits.
Outra é que existem diferenças de ambiente entre as máquina dos desenvolvedores. Como uma possível solução você deve assegurar que as builds ocorram em uma máquina de integração e somente se a build de integração passar com sucesso é que o commit deve ser considerado feito. Levando em consideração que o desenvolvedor que realiza o commit deve ser responsável pelo mesmo, ele precisa monitorar o repositório principal para que possa corrigir qualquer problema que possa ocorrer. A consequência desta responsabilidade é que ele não deve ir para casa até que a build do repositório principal tenha passado por qualquer commit ele tenha realizado durante o dia.
Existem duas formas principais que eu vejo para assegurarmos que isso ocorra: usando um processo manual de build ou um servidor de integração contínua. Descrever a abordagem da build manual é simples. Essencialmente ela é similar a build local que um desenvolvedor faz antes de lançar suas alterações no repositório. O desenvolvedor vai para a máquina de integração, checa a versão do repositório principal ( que é o último commit feito da casa) e faz a build de integração. Ele verifica todo este processo, e se a build for feita com sucesso ele finaliza seu commit (veja também a descrição de Jim Shore’s).
Um servidor de integração contínua age como um monitor para o repositório. Toda vez que um commit é feito no repositório o servidor automaticamente checa as fontes na máquina de integração, inicia a build, e notifica para quem fez o commit o resultado da build. Quem fez a build não a considera terminada enquanto ele não recebe uma notificação – geralmente por e-mail. Na ThoughWorks, nós somos grandes fãs de servidores de integração contínua – na verdade nós gerenciamos o desenvolvimento original do CruiseControl e do CruiseControl.NET, um servidor open-source de IC largamente usado. Desde então nós também construímos o servidor de IC comercial chamado Cruise. Nós usamos um servidor de IC quase que com todos os projetos que nós fazemos, e temos sido muito felizes com os resultados.
Nem todos preferem usar um servidor de IC. Jim Shore deu uma boa descrição do porquê ele prefere uma abordagem manual. Eu concordo com ele que IC é muito mais que somente a instalação de qualquer software. Todas as práticas citadas aqui são feitas efetivamente na IC. Mas igualmente muitos times que fazem IC acharão bom um servidor de IC para ser uma ferramenta útil.Muitas organizações fazem builds regulares em um tempo marcado, tipo toda noite. Isso não é a mesma coisa como uma build contínua e não é o suficiente para a IC. O ponto principal da IC é ajudar a encontrar os problemas o mais rápido possível. Builds noturnas significam que os erros ficam desapercebidos durante um dia inteiro antes que qualquer um os descubram. Uma vez que eles ficam muito tempo no sistema, eles demoram um longo tempo para serem encontrados e removidos.
O ponto chave de se ficar fazendo a build de integração é que se a build do repositório principal falhar, ela será corrigida no jeito certo. A idéia central de se estar trabalhando com a IC é que você sempre desenvolve em uma base conhecida e estável. Não é uma coisa ruim a build do repositório principal falhar, embora se isso acontecer o tempo todo é sinal que as pessoas não estão sendo cuidadosas o suficiente em atualizar e realizar a build localmente antes de realizarem o commit. Quando a build do repositório principal quebrar, entretanto, é importante que seja corrigida rapidamente. Para ajudar a evitar o quebramento do repositório principal, você pode considerar usar uma pending head.
Quanto as equipes que são iniciantes na IC, frequentemente esta é uma das coisas mais difíceis para se resolver. Uma equipe deve lutar para o quanto antes conseguir ter o hábito de trabalhar em builds da versão principal, particularmente se ela estiver trabalhando sobre uma base existente de código. Paciência e constância geralmente são boas dicas, portanto não desista.
Mantenha a Build rápida
O foco da IC é prover um feedback rápido. Nada é mais prejudicial as atividades da IC que uma build que toma muito tempo. Neste ponto eu tenho que admitir que existia um pouco de velho cara que considerava que uma build tinha que ser longa. A maior parte dos meus colegas consideram que uma build de uma hora é totalmente sem sentido. Eu me lembro de equipes sonhando que poderiam fazer isso tudo muito rápido e que ocasionalmente eles continuam rodando certos casos onde é muito difícil ter builds nesta velocidade.
Para a maior parte dos projetos, entretanto, a linha de build do XP de 10 minutos é, com razão, perfeita. A maioria dos nossos projetos modernos conseguem atingir isso. Isto é digno de se concentrar esforços para fazer com que aconteça, porque a cada minuto que você reduz o tempo da build é um minuto salvo para cada desenvolvedor a cada momento que ele realiza um commit. Uma vez que a IC demanda commits frequentes, ela economiza muito tempo.
Se você estiver começando com uma build de uma hora, começar a fazer a build cada vez mais rápido pode parecer intimidante. Isso pode até mesmo ser assustador começar a trabalhar em um projeto novo e pensar em manter as coisas rápidas. Para aplicações empresariais, ao menos, nos temos visto que o principal gargalo é testar – particularmente testes que envolvam serviços externos como uma base de dados.
Provavelmente o ponto mais importante é começar trabalhando na configuração de uma staged build (ou build por estágios, ou também build encadeada). A idéia por trás de uma staged build é que existe de fato multiplos builds feitas em sequência. O commit no repositório principal aciona a primeira build, que eu chamo de commit build. A commit build é a build que necessitou ser executada quando alguém atualizou o repositório principal. A commit build é a que tem que ser feita rapidamente, como consequência esta tomará alguns atalhos que diminuirão a capacidade de se achar bugs. O truque é balancear a necessidade de procurar bugs e a velocidade para que haja uma commit build estável o suficiente para que outras pessoas possam trabalhar.
Uma vez que a commit build é boa, então outras pessoas podem trabalhar no código com confiança. Entretanto existem testes novos e lentos que você pode começar a fazer. Máquinas adicionais podem rodar novas rotinas de testes na build que precisa de mais tempo.
Um exemplo simples disto é uma build de dois estágios. O primeiro estágio deve realizar a compilação e rodar os testes que são mais focados nos testes unitários com a base de dados vazia. Tais testes podem rodar muito rápido, dentro da linha de 10 minutos. Entretanto qualquer problema que envolva uma escala maior de interações, particularmente aqueles envolvendo a base de dados real, não serão encontrados. O segundo estágio roda em uma suite diferente de testes que acessam a base de dados real e envolve um comportamento de ponta-a-ponta. Essa suite pode tomar algumas horas para rodar.
Neste cenário as pessoas usam o primeiro estágio com a commit build e a usam para os seus ciclos principais da IC. O segundo estágio é formado por uma build secundária que roda quando possível, pegando os executáveis da ultima build válida para novos testes. Se a build secundária falhar, então isto não terá o mesmo “pare tudo”, mas o time precisa ter o objetivo de corrigir os problemas tão rápido quando possível, enquanto mantem funcionando a build de commit. Na verdade a build secundária não tem que ser sempre boa, tão logo os problemas sejam identificados e tratados em poucos dias. Como neste exemplo, as builds secundárias são frequentemente testes puros, desde que estes testes causem lentidão.
Se a build secundária detectar um problema, isso é um sinal que a commit build poderia fazer outro teste. Assegure-se o tanto quanto possível que qualquer falha na build secundária te leve a novos testes na commit build onde você deve pegar o problema, para que os problemas fiquem corrigidos na commit build. Desta forma os testes de commit são reforçados sempre que alguma coisa passar por eles. Existem casos onde não existem como construir um teste que rode rápido e que exponha o erro, então você pode decidir somente testar aquela condição na build secundária. Felizmente, na maior parte do tempo, você pode adicionar testes apropriados para a commit build.
Este exemplo é de uma build de dois estágios, mas o principio básico pode ser extendido para qualquer número de builds posteriores a principal. As builds posteriores a commit build pode também serem feitas em paralelo, então se você tem duas horas de testes secundários você pode melhorar o tempo e resposta do processo tendo duas máquinas que rodam metade dos testes cada. Usando builds secundárias paralelas desta forma você pode introduzir todos tipo de novos testes automáticos, incluindo teste de performance, dentro do processo regular de build (eu tenho rodado muitas técnicas interessantes como essa assim que visitei muitos projetos da ThoughtWorks nos últimos anos – e estou esperançoso para incentivar alguns dos desenvolvedores a escreve-las).
Teste em uma cópia do ambiente de produção
O ponto central dos testes é trazer a tona, dentro de condições controladas, qualquer problema que o sistema vai ter em produção. Um parte significante disto é que o ambiente onde eles irão rodar deve ser como o de produção. Se você testar em um ambiente diferente, toda diferença resultará no risco das situações sob teste não acontecerem em produção.
Uma solução para isso é que você configure seu ambiente de teste, dentro possível, para ser uma cópia exata do seu ambiente de produção. Use o mesmo software de banco de dados, com as mesmas versões, use a mesma versão do sistema operacional. Bote todas as bibliotecas que estão no ambiente de produção para o ambiente de teste, mesmo que o sistema atualmente não as use. Use o mesmo endereço IP e portas, rodando no mesmo hardware.
Bom, na verdade existem limites. Se você esta escrevendo um software desktop não é prático testar em uma cópia em todos os desktops possíveis com todas as partes terceirizadas do software que as diferentes pessoas estão rodando. Da mesma forma, alguns ambientes de produção podem ser caras demais para serem duplicadas (embora eu tenho encontrado frequentemente falsas economias não duplicando ambientes nem tão caros assim). Apesar destes limites, seu objetivo deve continuar sendo duplicar o ambiente de produção tanto quanto possível e entender que os riscos que você estará aceitando por cada diferença entre os ambientes de teste e produção.
Se você tem uma configuração boa e simples, sem ter que fazer muitas comunicações incomodas, você pode rodar sua commit build em um ambiente copiado. Frequentemente, entretanto, você necessitará fazer testes duplos, porque os sistemas responderão lenta ou intermitentemente. Como um resultado, é comum ter um ambiente muito artificial para os tests de commit, e usar uma cópia da produção para os testes secundários.
Eu tive notícias em um crescente interesse em usar virtualização para tornar fácil juntar ambientes de testes. Máquinas virtuais podem ser salvas com todos os elementos necessários juntos na virtualização. É relativamente simples instalar a última build e rodar os testes. Além disso, isto permite a você rodar multiplos testes em uma máquina, ou simular várias máquinas em uma rede com uma única máquina. Como o ônus da performance tem diminuido, esta opção faz cada vez mais sentido.
Torne fácil para qualquer um ter o último executável.
Uma das partes mais difíceis de desenvolvimento de software é ter certeza de que você esta construindo o software certo. Nós temos visto que é muito difícil especificar adiantadamente o que você quer e estar correto. Pessoas acham mais fácil ver algo que não é bom o bastante e dizer como isto precisa ser alterado. Os processos de desenvolvimento ágeis incentiva explicitamente e tomam vantagem desta parte do comportamento humano.
Para ajudar a fazer este trabalho, qualquer um envolvido com um projeto de software deve ser hábil para ter a última versão executável e ser capaz de roda-la: para demonstrações, testes exploratórios ou simplesmente ver o que mudou esta semana.
Faça isto ser bem simples: tenha certeza que existe um bom lugar conhecido onde as pessoas possam pegar o último executável. Pode ser útil pôr muitos executáveis em um lugar comum. Para a versão mais recente você deve por o último executável que passou nos testes de commit – como um executável, ele deve ser estável.
Se você estiver seguindo um processo com iterações bem definidas, geralmente é sábio por também junto o resultado final das builds da iteração. Demostrações, em particular, precisam do software cuja as características são familiares, então geralmente vale a pena por a última versão para alguma coisa necessária para quem estiver mostrando o software
Todos podem ver o que esta acontecendo.
Tudo sobre Integração Contínua esta relacionado a comunicação, então você precisa ter certeza que todos possam facilmente ver o estado do sistema e as mudanças que tem sido feitas nele.
Uma das coisas mais importantes para se comunicar é o estado da build da versão principal. Se você estiver usando o Cruise existe uma ferramenta embutida no site dele que irá mostrar a você se existe uma build em progresso e o qual foi o estado da última build da versão principal.
Muitos times gostam de fazer esta informação chamar mais a atenção colocando um display mostrando o sistema de build – com as populares luzes verde (quando a build funciona) e vermelha (quando ela falha). Um jeito particular é por algo como as lampadas de lava verde e vermelha – não somente para indicar o estado da build mas também para indicar por quanto tempo tem permanecido cada estado. Bolhas em uma lâmpada vermelha indica que a build tem quebrado por muito tempo. Cada time faz suas próprias escolhas sobre esses sensores da build – é bom ser informal com sua escolha (recentemente eu vi alguém experimentando por um coelho dançando).
Se você estiver usando um processo de IC manual, a visibilidade deste continua sendo essencial. O monitor da máquina de build pode mostrar o status da build da versão principal. Frequentemente você tem algo para ser posto na mesa de qualquer um que esteja fazendo atualmente a build (novamente alguma coisa boba como uma galinha de borracha é uma boa opção). Geralmente as pessoas gostam de fazer um som simples avisando boas builds, como um som de um sino.
As páginas dos servidores de IC pode trazer mais informações que estas, claro. Cruise oferece um indicador não somente de quem esta fazendo a build, mas quais mudanças ele fez. Cruise também provê um histórico de mudanças, permitindo membros da equipe ter uma boa informação das recentes atividades no projeto. Eu conheço equipes que gostam de usar isto para ter um senso do que as pessoas tem feito e manter uma idéia das mudanças feitas no sistema.
Outra vantagem de usar um site é para aqueles que não estão próximos fisicamente poderem ter uma idéia do status do projeto. Normalmente eu prefiro ter todos que estão trabalhando ativamente no projeto juntos, mas geralmente existem pessoas longes onde seja bom que tenham idéia de como as coisas estão. O site também é útil para as equipes para manter junto informações de builds de vários projetos – gerando um status simples e automático dos diferentes projetos.
Um bom painel de informações não são somente aqueles em uma tela de computador. Um dos meus painéis favoritos foi de um projeto que estava começando na IC. Ele tinha uma história longa de problemas em fazer builds estáveis. Nós colocamos um calendário na parede que mostrava um ano inteiro com um pequeno quadrado para cada dia. Todo dia o grupo de controle de qualidade (QA) colocaria um adesivo verde quando recebia uma build estável que passou nos testes, ou, colocaria um adesivo vermelho. Com o passar do tempo o calendário revelou que o estado do processo de build mostrava um crescimento estável até que os quadrados verdes eram tão comuns que o calendário desapareceu – com seu propósito cumprido.
Automatize a Implantação do Sistema
Para fazer a Integração Contínua você precisará de multiplos ambientes, um para rodar os testes de commit (ou commit build), um ou mais para rodar os testes secundários. Uma vez que você esteja movendo os executáveis entre estes ambientes várias vezes por dia, você vai querer fazer isso automaticamente.
Então é importante ter scripts que permitam você implantar a aplicação dentro de qualquer ambiente facilmente. Uma consequência natural disto é que você deve ter scripts que permitam você implantar o software dentro do ambiente de produção tão facilmente quanto nos outros ambientes. Você pode não implantar o software no ambiente de produção todos os dias (embora em tenho rodado projetos em que isso aconteça), mas implantações automáticas ajudam tanto a tornar o processo mais rápido quanto a reduzir erros. E isto é também uma opção barata desde que você use somente as mesmas configurações que você usa nos deploys dos ambientes de teste .
Se você implantar alguma função extra no ambiente de produção você deve checar se isto terá um rollback (ou reverter) automático. Coisas ruins podem acontecer de tempos em tempos, e se algo não der certo é bom ser capaz de desfazer rapidamente tudo para a última versão boa conhecida. Ser capaz de reverter automaticamente também reduz muita tensão durante a implantação, encorajando as pessoas a realizarem deploy ( entrega e implantação de uma versão) com mais frequência e assim ter novas funcionalidades para entregarem para os usuários rapidamente. ( A comunidade Ruby on Rails desenvolveu uma ferramenta chamada Capistrano que é um bom exemplo de uma ferramenta que faz esse tipo de coisa).
Em ambientes em cluster nós temos visto processos de implantação acontecerem onde um novo software é implantando em um nó por vez, substituindo gradualmente a aplicação durante o curso de poucas horas. Veja o artigo relacionado Evolutionary Database Design.
Um empecilho para muitas pessoas que fazem releases frequentes é a migração de banco de dados. Mudanças na base de dados são dolorosas porque você não pode simplesmente mudar os schemas do banco de dados, você tem também que assegurar que os dados migraram corretamente. Este artigo descreve técnicas usadas por meu colega Pramod Sadalage para fazer a refatoração automática e a migração dos bancos de dados. O artigo é uma tentativa rápida de captar a informação descrita em maiores detalhes pelo livro do Pramod e Scott Amblers sobre refatoração de banco de dados.
Uma variação particular e interessante disto que eu tenho visto surgir com a aplicação web pública é a idéia de implantar uma versão limitada para um grupo menor de usuários. O time então vê como essa versão limitada é usada antes de decidir se implantada o sistema para todos os usuários. Isto permite você testar novas funcionalidades de interfaces com o usuário antes de prosseguir para uma versão final. Implantação automatizada aliada a uma boa disciplina de IC é essencial para fazer isso funcionar.
Benefícios da Integração Contínua
Olhando isso tudo eu penso que o maior e mais abrangente benefício da Integração Contínua é a redução de riscos. Minha mente continua flutuando de volta para aquele projeto de software anterior que eu mencionei no meu primeiro paragrafo. La as pessoas estavam no final (ao menos acreditavam) de um longo projeto, sem ainda uma idéia real de quão longo isto deveria ser antes de terem terminado.
O problema com aquela forma de integração narrada é que é muito difícil saber o quão longa ela será, e o pior é que é muito difícil saber o quão longe você terá que ir neste processo. O resultado é que você esta pondo completamente a si mesmo em um ponto cego justamente em uma das partes mais tensas do projeto – mesmo que você esteja em um dos raros casos de ainda não estar atrasado. Integração Contínua reduz completamente este problema. Não existe uma integração longa, você elimina completamente o ponto cego. Em todo tempo você sabe onde você esta, o que funciona, o que não funciona e os bugs pendentes que você tem em seu sistema.
Bugs – estes são as coisas sórdidas que destroem a confiança, bagunçam os cronogramas e as reputações. Bugs no software implantando fazem os usuários ficarem com raiva de você. Eles entram em seu caminho, tornando difícil ter o resto do software funcionando corretamente.
Integração Contínua não nos livra dos bugs, mas os tornam dramaticamente mais fáceis de encontrar e remover. O grande responsável por este aspecto é o código auto-testável. Se você introduzir um problema e detecta-lo rapidamente, é mais fácil remover os problemas. Desde que você modificou somente uma parte pequena do sistema, você não tem que olhar muita coisa. Desde que aquele pedaço do sistema é o pedaço com que você trabalhou, ele ainda esta fresco em sua memória, novamente tornando fácil de encontrar o problema. Você também pode usar um diff debugging (ou um avaliador de diferenças) – comparando a versão corrente do sistema com uma versão anterior que não tinha o bug.
O bugs também são cumulativos. Quanto mais bugs você tiver, mais difíceis são se serem removidos. Em parte isto acontece porque você tem interações entre os bugs, onde as falhas são apresentadas como o resultado de múltiplos erros – tornando cada erro cada vez mais difícil de ser encontrado. E isto também é psicológico – pessoas tem menos energia para encontrar e eliminar os bugs quando existem vários deles – um fenômeno que os programadores pragmaticos chamam de síndrome da Janela Quebrada.
Para uma resposta a isso, projetos com IC tendem a ter dramaticamente menos bugs, tanto em produção quanto em desenvolvimento. Entretanto eu devo ressaltar que o grau deste benefício esta diretamente amarrado a quão boa sua suite de testes é. Você deve entender que não tão difícil construir uma suite de teste que faça uma diferença notável. Geralmente, entretanto, leva um tempo antes que o time consiga chegar ao mesmo nível dos bugs que eles tem que procurar. Avançar este nível envolve trabalhar constantemente e melhorar seus testes.
Se você trabalha com IC, este remove uma das maiores barreiras na implantação frequente. As frequentes implatações são valorosas porque elas permitem seus usuários terem as novas funcionalidades mais rapidamente, para darem feedback mais rapidamente sobre estas funcionalidades e geralmente se tornarem mais colaborativos no ciclo de desenvolvimento. Isto ajuda a quebrar as barreiras entre os desenvolvedores e os clientes – barreiras que eu acredito serem as maiores barreiras para desenvolvimento de software com sucesso.
Introduzindo a Integração Contínua
Então você imagina tentar a Integração Contínua – por onde você começa? Eu mostrei acima todo o conjunto de práticas para te mostrar todos os benefícios – mas você não precisa começar com tudo isso.
Não existe uma aqui receita fixa – muita coisa depende da natureza do seu ambiente e do seu time. Mas aqui estão algumas coisas que nós temos aprendido pra ter as coisas funcionando. Um dos primeiros passos é ter uma build automatizada. Ter todo que você precisa dentro do seu controle de código para que você possa então ter todo o sistema com um único comando. Para muitos projetos isto não é uma coisa pequena – isso é na verdade essencial para qualquer uma das outras coisas funcionarem. Inicialmente você pode fazer a build sob demanda, ou simplesmente fazer uma build noturna automatizada. Enquanto ainda não tem um Integração Contínua, ter uma build noturna automatizada é um bom passo.
Introduza alguns testes automatizados na sua build. Tente identificar as áreas onde geralmente as coisas dão erradas e faça testes automáticos que exponham estas falhas. Particularmente, em um projeto existente é difícil ter realmente uma boa suite de testes funcionando rapidamente – leva tempo para construir os testes. Você deve começar com o pensamento em algum lugar especifico.
Tente agilizar a commit build. Integração Contínua com uma build de poucas horas é melhor do que nada, mas diminuir para o número mágico de 10 minutos é bem melhor. Isto geralmente requer algumas cirurgias sérias em sua base de código, como quebrar as dependências das partes lentas do sistema.
Se você estiver começando em um projeto novo, comece com a Integração Contínua desde o início. Mantenha os olhos nos tempos da build e tome alguma ação logo quando quando começar a ficar mais lento que a regra dos 10 minutos. Agindo rapidamente você irá fazer as reestruturações necessárias antes que a base de código fique tão grande que isso se transforme em um problema maior.
Acima de tudo tenha alguma ajuda. Encontre alguém que tenha feito integração contínua antes que possa ajudar você. Como qualquer técnica nova, é difícil introduzi-la quando você não sabe como o resultado final deve parecer. Pode custar algum dinheiro ter um mentor, mas você pode também pagar por tempo e produtividade perdidos se você não sabe fazer isso. ( Aviso: sim, nós da ThoughtWorks fazemos algumas consultorias nesta área. Afinal de contas nós cometemos a maior parte dos erros que podem ser feitos).
Pensamentos Finais
Desde que Matt e eu escrevemos a versão original desde artigo, a Integração Contínua tem se tornado a principal técnica para o desenvolvimento de software. Dificilmente qualquer projeto da ThoughWorks acontece sem isso – e nós temos visto outros usando IC ao redor do mundo. Dificilmente eu ouço coisas negativas sobre esta abordagem – ao contrário de algumas das mais controversas práticas do Extreme Programming.
Se você não estiver usando Integração Contínua eu recomento fortemente você a tentar. Se você você estiver usando, talvez algumas idéias pode ajudar a você fazer isso mais efetivamente. Nós temos aprendido muito sobre Integração Contínua nos últimos anos, e eu espero que possamos continuar aprendendo e melhorando isso ainda mais.
Leitura Indicada
Um ensaio como este pode somente cobrir por alto algumas coisas. Para explorar a Integração Contínua em maiores detalhes, eu sugiro dar uma olhada no livro do Paul Duvall sobre este tema (que ganhou um prêmio Jolt – mais do que eu já tenha alcançado). Não existem muitas coisas sendo escritas sobre builds por estágios mas existe um ensaio escrito por Dave Farley na The ThoughtWorks Anthology que é útil.