Mostrar mensagens com a etiqueta Continuous Integration. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta Continuous Integration. Mostrar todas as mensagens

sexta-feira, 8 de junho de 2012

Continuous Delivery – Road to Perfection

Segundo o Martin Fowler a Integração Contínua (CI) é uma prática de desenvolvimento de software onde os membros de uma equipa integram seu trabalho de forma  frequente, geralmente cada elemento da equipa faz essa integração pelo menos uma vez por dia. Aqui cada integração é verificada por uma compilação automatizada (incluindo um conjunto de teste) para detectar erros de integração tão rapidamente quanto possível. Esta abordagem leva a que o tempo que uma equipa demora a encontrar problemas de integração seja reduzidos significativamente levando a um desenvolvimento de software mais coeso.

Segundo o mesmo Martin Fowler existe um conjunto de princípios básicos que se devem seguir.

Practices of Continuous Integration

  • Maintain a Single Source Repository.
  • Automate the Build
  • Make Your Build Self-Testing
  • Everyone Commits To the Mainline Every Day
  • Every Commit Should Build the Mainline on an Integration Machine
  • Keep the Build Fast
  • Test in a Clone of the Production Environment
  • Make it Easy for Anyone to Get the Latest Executable
  • Everyone can see what's happening
  • Automate Deployment

Assim o objectivo é criar um Sistema de CI de modo a que os erros cometidos durante o desenvolvimento sejam o mais rapidamente detectados . Assim olhando para as práticas definidas pelo Martin Fowler vamos tentar cumprir todas elas. Comecemos a analisar as mesmas e a verificar como as podemos cumprir.

Maintain a Single Source Repository

Deve existir um branch de desenvolvimento único por projeto em que toda a equipa deverá trabalhar.  Outros branchs apenas para correções de bugs em versões de produção antigas. Para o desenvolvimento de novas funcionalidades usar um mecanismo que permita ligar e desligar essas mesmas funcionalidades, para que estas não sejam visíveis nas versões de produção. O Martin Fowler criou um post no seu blog onde dá um conjunto de dicas bastante úteis sobre como gerir a problemáticas dos branchs. Este post pode ser consultado aqui.

Estando a falar de repositórios deixo algumas notas sobre o que deve ser feito antes de efetuar um commit para o repositório, e que cuidados se devem ter em conta:

  • Deve atualizar a sua cópia local para obter todas as novas alterações feitas por terceiros desde a última atualização do  repositório.
  • Se existirem conflitos, estes devem ser resolvidos localmente. Não se deve simplesmente sobrepor as alterações de terceiros.
  • Deve compilar o código antes de efetuar um commit. Se não compilar, deve ser corrigido e só depois colocado no repositório.
  • Se o código compilar sem problemas, deve executar um conjunto básico de teste. Pelo menos executar os testes rápidos,  todos eles e não apenas os que você se acabou de escrever ou modificar.
  • Se um dos testes falhar, deve corrigir o código que deu origem ao erro, ou por outro lado, corrigir o teste se este já não for o mais correto. Repita o processo até que nenhum dos testes falhe.
  • Por fim deve-se efetuar o commit do código.

Automate the Build

Este ponto pode ser visto de dois lados, do ponto de vista do desenvolvimento e do ponto de vista do CI. Para um novo programador que se junte a uma equipa o período de tempo entre um checkout e o ter a aplicação em estado de criação de novos desenvolvimento deve ser mínimo (Entre 10-15m, a partir daí já começa a ser tempo perdido em demasia). Claro que destas estimativa se exclui-se deste tempo as instalações mais básicas como SO, IDE de desenvolvimento e ambientes Java, .Net, Ruby. Em suma, o processos de compilação deve permitir que qualquer pessoa seja capaz de trazer uma máquina virgem, retirar as fontes do repositório, correr um único comando, e ter o sistema pronto a correr na sua máquina.

Por outro lado devemos automatizar o processo de compilação numa máquina dedicada ao CI. Para se chegar a este objectivo existem alguns utilitários como o Ant, o Maven ou mesmo o MSBuild. Todas elas têm os seus pontos fortes e fracos. O MsBuild é muito bom para automatizar a compilação do código dos projetos .Net. È só escolher a mais adequada e começar a automatizar todo o processo de build.

Claro que nem sempre tudo corre bem e as vezes existem falhas nas builds. Então o que devemos fazer quando estas falham?

Primeiro é necessário identificar o que falhou. Aqui existem normalmente vários candidatos possíveis desde erros no código, a testes que não passaram, erros que não passaram ou até mesmo uma simples falta de espaço na máquina de CI, assim:

  • A primeira coisa a fazer é identificar qual é o problema.
  • Se o problema for derivado a um erro de compilação, deve-se imediatamente alertar a equipe de desenvolvimento (para que estes não atualizem a sua cópia local com o código partido), e posteriormente descobrir quem é o responsável. Verifique que este erro é corrigido o mais rapidamente possível.
  • Se o problema for derivado a testes que falharam verificar se o teste ainda se encontra adequado à situação a verificar. Deve-se adequar o mesmo caso esta situação já  não se verifique. Se por outro lado o problema estiver no código que está a ser validado por um determinado teste verificar quem é o responsável por essa parte do código e com ela verificar o problema.

Uma build partida deve ser corrigida de forma rápida, porque geralmente está a impedir o trabalho de terceiros

Make Your Build Self-Testing

Tradicionalmente uma build significa compilação, empacotamento,  e todo o material adicional necessário para obter um programa para executar. Um programa pode ser compilar corretamente, até arrancar, mas isso não significa que ele faz o que correto.

Aqui podem ser colocados testes unitários, verificações de comprimento das regras de arquitetura definidas (um utilitário bom para este aspecto é por exemplo o Macker embora seja uma aplicação já descontinuada). Um outro exemplo de uma aplicação que nos permite efetuar verificações de arquitetura no código desenvolvido é o Sonar que pode facilmente ser integrado com servidores de continuous integration ou mesmo com um IDE de desenvolvimento. Podemos/devemos colocar mesmo testes funcionais e de integração.

Mais Informação sobre como forçar regras de arquitetura no código desenvolvido pode ser encontrada aqui.

Comecemos por uma abordagem sobre análise estática do código e por alguns utilitários conhecidos que permitem identificar os erros mais comuns na construção de software. O FindBugs e o PMD são dois bons exemplos para identificar este tipo de erros. Embora tenham uma parte bastante semelhante estas também se complementam muito bem.

O FindBugs é um programa open Source criado por Bill Pugh e David Hovemeyer que procura bugs em código Java. Ele usa análise estática para identificar centenas de diferentes tipos de potenciais de erros que opera sobre o bytecode do Java. O software é distribuído como uma aplicação gráfica, embora também existam plug-ins disponíveis para o Eclipse, Netbeans, Hudson, Jenkins. Por outro lado por ser usado a partir do Ant.

O PMD verifica programas feitos em código Java e procura por potenciais problemas como: Opções try / catch / finally vazias, código não utilizado, assim como variáveis ​​locais não utilizadas. O PMD também têm a capacidade de detectar código duplicado.

As ferramentas do tipo xUnit são certamente um ponto de partida para tornar o código testável e estes testes são normalmente rápidos para se colocar numa build. São os exemplos mais conhecidos o Junit e Nunit.

Claro que além de testes unitários podemos colocar na build testes funcionais ou de integração e existem um conjunto interessante de ferramentas que permitem cumprir este objectivo. Assim são exemplo dessas ferramentas que se focam nos testes, e que podem e que podem com mais ou menos esforço ser usadas, o FIT, Selenium, Sahi, Watir, Fitnesse  e ainda o TeStudio.

Everyone Commits To the Mainline Every Day

A utilização de commits frequentes obriga os programadores a dividirem o seu trabalho em pequenas porções. Esta opção ajuda a que se tenha uma melhor percepção do progresso. Geralmente ficamos com a impressão que não conseguimos fazer algo significativo no espaço de algumas horas. Mas uma boa organização, prática e metodologia ajuda-nos a conseguir cumprir estes objectivos.

Faz bastante sentido efetuar fusões frequentes porque estas são mais fáceis de integrar do que grandes, os ocasionais. Quanto menos vezes nós efetuarmos um commit  a integração torna-se mais doloroso. Os problemas irão aparecer muito mais tarde, e irá ser gasto muito tempo na integração, porque vamos cair em “merges” complicados o que tornará sempre mais difícil descobrir o que quebrou a build.  Porque estaríamos literalmente à procura de uma agulha num palheiro.

Every Commit Should Build the Mainline on an Integration Machine

Quando assim acontece ficamos logo a saber que foi que provocou um erro na build. Com a associação a um sistema de notificações que partiu a build fica a saber dessa situação uns minutos depois enquanto a memória ainda se encontra fresca e o problema é mais fácil de corrigir.

Este é provavelmente um dos item mais complicado de atingir, quando os recursos físicos do sistema são limitados. Existem opções de em vez de  o fazer a cada commit o fazer apenas a cada X horas, mas claros estes factos dependem sempre do número de elementos envolvidos, da complexidade do sistema de build montado e da capacidade da(s) máquinas associada(s) ao servidor de integração contínua.

Keep the Build Fast

Um dos principais pontos da Integração Contínua é fornecer um feedback rápido, e nada é mais inoportuno para uma atividade de CI do que uma build que leva muito tempo a completar. Uma build de CI deve além de compilar o código, efetuar a atualização dos ambientes de desenvolvimento e testes de modo a que se esteja a trabalhar sobre as versões mais recentes do software. Nestes casos o processo de build e mais principalmente o processo de deploy deve ser  o mais rápido possível para não ter o sistema indisponível durante um longo período de tempo.

Uma build de CI deve conter a compilação e deploy dos dados nos ambientes de teste (potencialmente nos ambientes de desenvolvimento, se conseguirmos ter o tempo de downtime extremamente curto). Em teoria uma build de continuous integration não deve ultrapassar os dez minutos.

Test in a Clone of the Production Environment

Um dos objectivos dos testes é identificar e eliminar, sob condições controladas, qualquer problema de que o sistema possa vir a ter em produção. Se for usado um ambiente diferente, cada diferença resulta num risco de algo que acontece em testes não vai acontecer em versões de produção. Como resultado queremos ter uma versão tão parecida quanto possível à versão de produção pois só assim são identificados os problemas reais e que realmente importam.

Para facilitar a criação de testes devem ser criadas user stories que simulem situações importantes para a utilização. Neste ponto será interessante experimentar as sugestões que o James Whittaker apresenta no livro Exploratory  Testing para criar guias automatizados. Estes tours como são chamados no livro ajudam a criar um bom conjunto de situações de teste que, obviamente, depois têm de ser automatizados, o que pode não ser fácil.

Make it Easy for Anyone to Get the Latest Executable

Uma das partes mais difíceis do desenvolvimento de software é conseguir ter certeza de que estamos a criar o software certo. Não é fácil, aliás é bastante difícil, especificar os objectivos com  antecedência e fazer a coisa correta: Normalmente as pessoas acham mais fácil de ver algo, mesmo que não está certo, e dizer como é que pode ser mudado ou melhorado. Um dos objectivos dos processos de desenvolvimento ágeis explicitamente tenta tirar proveito desta vertente do comportamento humano.

Quero instalar a última build onde a obtenho? Acho que basta para isso ter uma qualquer pasta partilhada na rede, de modo que acedendo à mesma seja fácil de obter

Everyone can see what's happening

CI gira à volta de uma boa comunicação, assim é esperado que o sistema garanta que todos possam facilmente ver o estado do mesmo, e que alterações foram feitas a ele. A utilização de emails de notificação quando uma build corre bem ou mal com os resultados dos testes é sempre uma necessidade (Principalmente quando esta falha para que possa ser reposta o mais rapidamente possível).

Automate Deployment

Para levar as práticas de CI a bom porto são sempre necessários vários ambientes, um para executar testes de confirmação, um ou mais para executar testes secundários. Como normalmente se está a movimentar artefactos entre a máquina de CI e os ambientes de teste com uma grande frequência, este processo deve ser feito de forma automática.

Um boa consequência de se ter o deploy automático, é que estes são depois facilmente adaptados para serem usados em ambientes de produção. Quando se automatizar os deploys em ambientes de produção será sempre bom fazer o mesmo com os processo de backup e rollback, porque infelizmente as coisas de vez em quando acabam por correr mal (O Murphy anda sempre à espreita. Sorriso).

Outra preocupação, geralmente, é a atualização das bases de dados associados ao software desenvolvido, quando são feitas releases frequentes. A utilização de utilitários como o mybatis-migrations ajuda a melhorar em muito este aspecto. O Martin Fowler e o Pramod Sadalage escreveram também um conjunto de dicas/sugestões que podem também ser bastante úteis e que pode ser encontrado aqui.

O que ganhamos com tudo isto?

Em geral, o maior benefício maior e mais abrangente de Integração contínua é a redução do risco, que advém da seu rápido feedback. O CI não faz com que os bugs desaparecem de vez, mas torna muito mais fácil a sua detecção e remoção. Estes quando chegam ao produto final levam a que o utilizador fique de algum modo zangado com o fornecedor do software, o que deteriora a imagem do mesmo.

terça-feira, 25 de outubro de 2011

Continuous Integration (Benefícios para os Testes)

 

Introdução ao Continuous Integration


Parafraseando Martin Fowler, Continuous Integration (CI) é uma prática, onde os todos os elementos de uma equipa tornam os seus desenvolvimentos parte de um conjunto de forma a combinar estes desenvolvimentos com o trabalho do resto da equipa. Tradicionalmente este tipo de integrações são feitas tendo como base um sistema de controle de versões. São exemplos disso o svn e o cvs.


Benefícios


Quando usado de forma eficiente permite colocar as falhas num curto período de tempo. Isto porque por tradição um sistema de CI além de efetuar a build do software também lhe aplica um conjunto de testes. E aqui ficamos a saber por exemplo se a build da hora transacta passou sem problemas e a atual revela erros (ou porque falhou ou porque um dos testes falhou), existiu um  lapso cometido na última hora. Daqui até se descobrir que contribuições foram feitas para o repositório e por que é um “saltinho de pardal”. Como o processo é tão rápido o problema ainda está fresco na cabeça do elemento que cometeu o erro.


Com este tipo de práticas, tornarmos o código independente de um IDE, como evitamos a necessidade de configurar ficheiros antes de efetuar uma build. Basicamente passamos do ponto onde temos o software num repositório para o ponto onde temos a aplicação completamente a funcionar com a utilização apenas de um comando, ou de um clique, caso se use alguns exemplos de implementações de CI como as referidas abaixo.


Principais Práticas


A grande maioria do software é construída por vários módulos, algumas das vezes construídos em cima de tecnologias completamente distintas que são posteriormente agregados para formarem um produto sólido.


Um dos princípios advogados pelo CI é a agregação num único repositório das fontes necessárias à construção do produto. A gestão de vários pontes onde guardar software acaba por trazer vários problemas com a gestão correta dos mesmos.


Outro ponto fundamental do CI é que o mecanismo responsável pela compilação e preparação do software seja também responsável pela realização de um primeiro conjunto de testes de modo a detectar logo à partida qualquer erro grave. Testes estes que devem sempre quanto possível correr em ambientes que são réplicas exatas dos ambientes de produção, ou não caso de não ser possível do mais próximo possível.


As builds criadas devem ser o mais frequentes possíveis para restringir ao máximo no tempo o erros detetados. Estas builds devem estar num local acessível a toda a comunidade de desenvolvimento e a toda a equipa de controlo de qualidade (testes). Esta situação permite que cada elemento da equipa de desenvolvimento tenha rapidamente o código produzido por diferentes colegas. Do mesmo modo a equipa de testes pode começar a trabalhar rapidamente sobre o código produzido o que permite a existência de feedback mais rápido.


Principais Implementações


Existem vários aplicativos que permitem ou facilitam a automação dos processos de compilação. Cada um deles com as suas vantagens e desvantagens, ou mais virados para uma determinada área desenvolvimento especifica. Podem consultar o seguinte artigo na wikipedia que ajuda a perceber quais as funcionalidades de cada um.


http://en.wikipedia.org/wiki/Comparison_of_continuous_integration_software


Os baseados em Ant geralmente são boas opções, pois permitem descrever o que se quer construir numa forma bastante simples, e possui já suporte nativo para a grande maioria dos casos mais comuns na construção de software. Um exemplo bastante interessante de uma aplicação deste género é o caso do CruiseControl.


Principais Referências


Qualquer pesquisa no Google apresenta inúmeros resultados sobre o tema Continuous Integration mas gostava de destacar aqui duas referências que me parece, no meu ponto de vista, que se sobrepõe às restantes. Elas são:



A ligação entre os Testes e o CI


Como foi referido acima é boa prática possuir uma construção automatizada do software que se desenvolve. Esta boa prática permite manter uma unicidade entre as versões que são instaladas em ambientes de produção, desenvolvimento e testes. Esta unicidade ajuda a evitar erros de builds com lapsos quando feitas para ambientes de produção que não existem quando estas são construídas para testes ou mesmo nas atualizações do próprio ambiente de desenvolvimento.


Por outro lado permite que sejam usados testes unitários no processo da própria build. Esta situação permite, quando usando um conjunto de testes unitários adequado, que erros em pontos crucias da aplicação sejam detectados muito rapidamente. O que facilita a sua correção pois a sua codificação ainda está relativamente fresca na cabeça da equipa de desenvolvimento.


Existem inúmeros aplicativos que permitem a criação de testes unitários como são o caso do nUnit, jUnit entre outros ferramentas do género.


Muito mais poderia ser dito à volta de tema, mas acho que já serve para começar a perceber as vantagens do uso de CI no desenvolvimento diário de software.


Bom, até à Próxima.