Integração Contínua

Integração Contínua

O que é?

A Integração Contínua (CI – Continuous Integration) é um processo que exige que os programadores integrem, num repositório partilhado por toda a equipa, o código desenvolvido por si, pelo menos uma vez por dia. Num Universo utópico, as integrações deverão ocorrer várias vezes ao dia, essencialmente a cada ‘push’ de código para o repositório. Esta acção dispoletará uma ‘build’ automatizada.

Porquê?

As metodologias Agile exigem o código esteja sempre “preparado” para uma eventual entrega. Este processo de integração contínua do código de toda a equipa, na base de código em que se está a trabalhar, permite se detectar problemas bastante mais cedo.

Num processo Waterfall, cada entrega de código poderia demorar vários meses a acontecer. Como podes imaginar, nessa altura era recorrente que num dia de entrega se convocasse uma “sala de guerra”, onde deveria estar toda a equipa de desenvolvimento, a equipa de operações, a equipa de produto, a equipa de qualidade, a equipa de gestão. O processo de entrega era um verdadeiro caos, e poderia levar várias horas a decorrer (não contando com possíveis problemas que pudessem ocorrer, e que deveriam ser atacados de imediato pela equipa responsável).

De facto, nessa altura imaginar que a cada ‘push’ para o repositório poderia correr uma bateria de testes (unitários, integração, aceitação, …) e obter feedback se eventualmente as minhas alterações iriam partir alguma funcionalidade existente, era impensável. Uma verdadeira aventura !!

Como?

Existem imensas opções para quem quer começar a construir uma pipeline de desenvolvimento com integração contínua. O mais conhecido do momento talvez seja o Jenkins. Uma ferramenta open-source de CI escrita em JAVA. Para quem não quer adicionar mais uma ferramenta à, quase infindável, lista de ferramentas que se tem de gerir e administrar, existem várias opções SaaS. De momento, na log, utilizamos o SaaS Codeship da CloudBees.

Uma vez configurada a pipeline de integração contínua, sempre que ocorrer um “push” para um repositório Git, irá correr um processo automático onde será levantado um ambiente virtual com as características por nós definidas, dentro do qual irá ocorrer a “build” do projecto e posteriormente correrão os testes – unitários, integração, aceitação, mutação, …

Em qualquer um destes pontos, caso ocorra um qualquer problema, o processo de integração contínua falhará e teremos o relatório do problema. Importante referir que se o processo de integração falhar, não haverá qualquer possibilidade de se efectuar um deploy – automático ou manual – para qualquer que seja o ambiente.

Não é possível utilizar “eXtreme Programming” sem este processo !!

WordCamp Londres 2018

WordCamp é uma conferência que foca em tudo o que seja WordPress. WordCamps são eventos informais, organizados pelas comunidades de utilizadores de WordPress como tu. Qualquer um, desde utilizador casual a developers do core, participam, partilham ideias e conhecem-se uns aos outros.

Como se pode ver pela descrição presente no site da “central” de WordCamps realizados pelo Mundo fora, os WordCamps são momentos de partilha de conhecimentos e experiências, mas também uma oportunidade de expandir redes de contactos e formar parcerias.

log, empresa para a qual trabalho, irá patrocinar o WordCamp Londres e estará presente para apresentar os seus serviços de desenvolvimento em “Nearshore”. É com bastante entusiasmo que farei parte da comitiva. Será um prazer poder passar dois dias a falar com outros programadores de WordPress e, com bastante orgulho, promover a qualidade de código desenvolvido pela minha equipa.

Continuo a acreditar que é possível promover e disseminar a utilização das boas práticas de engenharia que o eXtreme Programming aconselha. Certamente que este será mais uma oportunidade para debater o tema.

Encontramo-nos em Londres?

Programação Pareada

Programação Pareada

O processo de programação pareada (em pares) é uma metodologia de trabalho defendida pelo eXtreme Programming (XP). Como o nome refere, cada unidade de programação é constituída por dois programadores num único computador. Poderemos incorrer no erro de pensar que é um desperdício de capacidade humana, uma vez que estão dois elementos a realizar o trabalho de um. No entanto esse pensamento não poderia estar mais afastado da realidade.

É expectável que uma equipa pareada produza a mesma quantidade de código que seria de esperar de dois programadores individuais, no entanto com a mais valia do código ter maior qualidade. Num artigo anterior escrevi sobre a importância e o papel da revisão de código no desenvolvimento de software. Processo que também consome tempo no presente mas que nos permite ganhar tempo no futuro através da maior maturidade e qualidade do código que seguirá para produção. Se se optar pela metodologia da programação em pares, poder-se-á presumir que a revisão de código já estará presente no código desenvolvido pela dupla de programadores.

Cada um dos programadores terá um papel específico – o “condutor” e o “navegador”.

Isto é, um escreve código enquanto o outro lê e antecipa situações. O melhor entrosamento verifica-se quando o teclado passa de um para o outro, sem que nenhum dos elementos esteja agarrado ao seu papel pre-estabelecido. Como se poder constatar, programação em pares tem tanto de soft-skills como de technical skills. Não é fácil para um programador aceitar a opinião de um par antes de testar a sua opinião. Como todas as demais soft-skills, é possível desenvolvê-la mas que requer tempo e abertura da parte do indivíduo.

“Já que vou ter de colocar dois programadores juntos, o melhor é colocar um sénior junto com um júnior, assim ele vai poder aprender”.

É preciso muito cuidado com este pensamento. A relação de mentoria é diferente da relação entre pares. Se efectivamente se pretende que a produtividade não diminua, a equipa deverá ser constituída por programadores competentes e já numa fase em que são totalmente autónomos.

Existem empresas que optam até por definir os papeis como “programador” e “tester”, ou seja, o “programador” irá escrever código para os testes unitários desenvolvidos pelo “tester”. Estes papéis vão sendo trocados e chegam mesmo a trocar no próprio dia. Como se pode compreender, seria completamente impossível de o fazer se os dois elementos não estivessem no mesmo nível de autonomia e conhecimento.

Outra indicação que o eXtreme Programming nos dá, “mover as pessoas”. É importante que as equipas pareadas sejam trocadas regularmente. Com isso iremos prevenir o aparecimento de “ilhas de conhecimento”, que de futuro nos poderá encaminhar para uma dívida técnica caso os detentores do conhecimento decidam sair da empresa – fenómeno tão temido por qualquer gestor tecnológico.

Assim se percebe que aliando a programação pareada a uma rotação interna de programadores obtemos maior:

  • fluxo contínuo de desenvolvimento e entrega,
  • disseminação de conhecimento da base de código do projecto,
  • qualidade no código produzido,
  • diminuição de possíveis dívidas técnicas.

 

Revisão de Código

Revisão de Código

O processo de revisão de código é há muito defendido pela IBM, de facto desde 1974. Inicialmente designado por “inspecção de código”, um processo formal de análise estática para validar se o software vai ao encontro do requisitos. Este processo era lento mas mesmo assim a IBM conseguiu atingir significativas melhorias na qualidade do código entregue.

A empresa (IBM) quase duplicou o número de linhas de código entregues nos produtos de software do System/370 desde 1976, enquanto o número de defeitos por milhares de linhas de código foi reduzido em dois-terços – Michael Fagan

Graças a ferramentas colaborativas de controlo de versões, conseguiu-se agilizar este procedimento, tornando-o num processo de rotina diária. Na minha equipa, todos os dias, cada engenheiro lê, revê, avalia e comenta o código de algum colega. O objectivo é se aumentar a confiança no código que entrará em produção, reduzindo o número de bugs. O processo de revisão implica que o código poderá ser actualizado e novamente revisto. Esta troca de informação deverá ocorrer o número de vezes necessárias até que a equipa – quem produziu o código e quem reviu – esteja totalmente confiante com o resultado.

Não existem regras rígidas de como deverá funcionar este processo. Com a disseminação do procedimento, o bom senso imperou e começaram a surgir, de diferentes fontes, boas práticas aconselhadas.

A empresa SmartBear Software desenvolveu um pequeno white-paper com 11 boas práticas para um eficaz processo de revisão de código:

  1. Rever menos de 200-400 linhas de código (LOC) de cada vez.
    • Mais de 400 linhas de código exigirão maior tempo de atenção, além de ser desmoralizador para o revisor saber de ante-mão que irá passar o dia inteiro a rever “aquele” código.
  2. Apontar para um rate de revisão numa ordem inferior das 300-500 LOC/hora.
    • É preferível que se analisem menos linhas de código mas que se procura situações como bugs, possíveis falhas de segurança, possíveis falhas de optimização e até possíveis falhas no desenho ou arquitectura do código.
  3. Ter tempo suficiente para rever adequadamente, e com calma, mas nunca mais de 60-90 minutos.
    • Por ser uma tarefa que exige atenção ao detalhe, a capacidade de concentração irá decair drasticamente quanto mais tempo levar a tarefa a concluir. Por experiência própria, após 60 minutos de revisão eficaz de código, ou se faz uma pausa (ir beber um café, levantar da cadeira e fazer alguns alongamentos, ler um artigo, etc) ou se começa a ser complacente com algum código e deixa-se de se analisar situações sensíveis como questões de segurança, optimização e escalabilidade.
  4. Autores deverão anotar o código-fonte, antes do processo de revisão começar.
    • É importante que o autor do código encaminhe os seus colegas para quais os ficheiros que devem ser revistos, evitando que código já revisto seja novamente validado.
  5. Estabelecer objectivos quantificáveis para o processo de revisão de código e capturar métricas para se poder melhorar o processo.
    • É importante que a equipa de gestão tenha forma de quantificar se o processo de revisão de código é eficaz, como por exemplo, contabilizar o número de bugs reportados pelo cliente.
  6. Lista de validação melhoram substancialmente os resultados tanto para os autores como para os revisores.
    • O que rever? Sem uma lista, cada engenheiro poderá procurar por algo em particular e deixar ao esquecimento outros pontos importantes.
  7. Validar que os defeitos foram de facto corrigidos!
    • Não basta um revisor indicar onde estão as falhas ou sugerir melhorias. E não se trata de uma questão de confiança nos colegas. É importante verificar que, de facto, as alterações também foram bem implementadas.
  8. Managers deverão cultivar uma boa cultura de revisão de código, em que encontrar defeitos é visto como algo positivo.
    • É necessário evitar a cultura de “porque não escreveste bem à primeira?”. Interessa sim que não se encontrem bugs em produção. No desenvolvimento e no processo de revisão é onde se devem encontrá-los. É importante existir espaço para que um engenheiro possa errar. Só assim poderá aprender algo novo.
  9. Atenção ao efeito “Big Brother”.
    • Similar ao ponto 8, mas na perspectiva do engenheiro. É importante se ter noção de que as sugestões ou bugs reportados nas revisões de código são quantificáveis. Estes dados deverão servir aos managers para analisarem se o processo está a funcionar, se um engenheiro está com alguma dificuldade em particular, mas nunca deverão ser utilizados para avaliações de performance.
  10. O factor Ego: Fazer sempre alguma revisão do próprio código, mesmo que não tenham tempo para fazê-lo até ao final.
    • Saber que o nosso código vai ser revisto por terceiros alerta-nos para sermos mais cautelosos no que produzimos.
  11. As revisões de código feitas de forma “leve” são eficientes, práticas e eficazes em encontrar erros.
    • Não é necessário se entrar no procedimento descrito pela IBM há 30 atrás, onde 5-10 pessoas se fecham em reuniões periódicas com impressões de código e rabiscam a cada linha de código. Utilizando ferramentas como o Git, é possível se participar no processo de revisão de código, escrever comentários associados a linhas específicas, debater soluções através de mensagens assíncronas com o autor, etc.

Aconselho a lerem o documento para terem uma visão mais aprofundada de cada ponto.

Não é possível refutar que a revisão de código irá melhorar a qualidade do código produzido, diminuindo o número de bugs e melhorando o desenho. Existem algumas mais valias adicionais como a partilha de conhecimento entre colegas e a mentoria de elementos mais juniores da equipa.

É um facto que este processo, mesmo que feito de uma forma mais leve, consumirá tempo ao projecto. Ao processo de revisão, temos de adicionar o tempo de resposta do autor e novamente o tempo de nova revisão. Numa tentativa de agilizar este consumo de tempo, aconselho que, após duas tentativas de revisão sem sucesso, o autor e o revisor se sentem lado a lado e debatam o pedaço de código que está a causar o problema.

Mas é importante recordar uma frase que se encontra no site da IBM –  Slow down to go faster (abranda para ires mais rápido).

Gostaria de terminar com uma nota, que tenho como demasiado importante. O processo de revisão de código exige que toda a equipa de desenvolvimento demonstre uma elevada inteligência emocional. Regra geral os programadores têm um ego forte e sentem-se “pais” do código por si escritos. Por vezes não é fácil aceitar comentários menos construtivos. Comentário totalmente destrutivos, provocantes ou jocosos “farão mais mal que bem”, podendo mesmo destruir o espírito de uma equipa. É importante acompanhar esses elementos, tentar que modifiquem as suas atitudes ou, em último recurso, retirá-los da equipa.

Desenvolvimento Orientado por Testes

TDD

Desenvolvimento Orientado por Testes

A metodologia “eXtreme Programming” promove o desenvolvimento orientado por testes (TDD). O quer isto dizer?

Contrariamente ao que a técnica do TDD aconselha, não é pouco-comum encontrar um programador que “ataca de cabeça” o desenvolvimento de uma qualquer funcionalidade.

Síndrome do cobertor curto

“Não vejo o mal nisso” poderás estar a pensar. Deixa-me então colocar a seguinte questão – já te aconteceu implementar/actualizar/corrigir uma funcionalidade e por obras de magia negra, outra funcionalidade “completamente ao lado” partiu? A isto se poderia chamar de síndrome do cobertor curto. Para taparmos de um lado, vamos destapar noutro.

test-driven development diagramO XP aconselha o desenvolvimento em ciclos curtos e em constante repetição. O programador deverá criar um teste e executá-lo. Este deverá falhar pois ainda não temos código real para ser testado. É só então que o código para a funcionalidade é criado e volta-se a executar o teste. Quando o teste passar o programador deverá, sem qualquer demora, revisitar o código escrito e refactorizá-lo. Este é o ciclo curto de desenvolvimento do XP.

Existem vários tipos de testes – unitários, integração, funcionais e aceitação. Todos eles, como iremos ver, são de especial importância no ciclo de vida do software.

Testes Unitários

Este tipo de teste, tal como o nome indica, deverá testar uma unidade de código. Tipicamente testará cada método público, em particular e sem qualquer tipo de contacto com o resto do sistema. Idealmente o que se testará é a correcta entrada e retorno de dados. Se eventualmente numa iteração futura, um programador alterar o comportamento desse método para aceitar ou retornar outro tipo de dados, o teste deverá automaticamente falhar.

É bastante comum se encontrar programadores a desenvolver na técnica – “testes orientados ao código”. É de primordial importância o desenvolvimento primeiro dos testes e somente depois o algoritmo. Fazer o processo inverso fará com que o código seja muito mais difícil de testar.

É interessante salientar que este tipo de testes, quando bem estruturados, também poderão servir como documentação do sistema. Quantos testes unitários se deve escrever por sistema? Bom, de acordo com o Robert “Uncle Bob” Martin, dever-se-á abranger 100% de todo o código !!!

Testes de Integração

Após a realização de testes unitários, onde se testaram unidades isoladas do sistema, é a altura de se misturar as peças. Existem várias aproximações a este tipo de teste – “big-bang”, “top-down”, “bottom-up”, “mixed” e “risky-hardest”. Deixarei a explicação destas abordagens para futuros artigos. No entanto, e se com os testes unitários se defende a cobertura de 100% do código, com estes testes a situação merece uma maior reflexão. O J.B.Rainsberger explicou como se poderá estar a criar um pequeno Monstro ao se utilizar testes de integração, pois facilmente se poderão chegar a milhares de testes. Aconselho a verem a apresentação (ou lerem o artigo) do Rainsberger.

Testes Funcionais

Os testes funcionais fazem parte do processo de Controlo de Qualidade (QA). Como o nome permite adivinhar, num teste funcional ir-se-á testar uma funcionalidade, de acordo com a especificação e requerimentos do sistema. O funcionamento interno não é tido em consideração, daí a designação de teste “caixa-negra”. Também estes testes têm várias abordagens.

Testes de Aceitação

Poderemos considerar a fase de testes de aceitação como o último momento para a libertação de um sistema de software. Estes são escritos a partir de “user stories” definidas pelo cliente. Um exemplo de um teste de aceitação num sistema de backend poderia ser:

  • o utilizador acede à página de login
  • o utilizador insere dados de autenticação (user: admin, pass: password)
  • o utilizador é reencaminhado para a página X

Basicamente ir-se-á testar possíveis cenários, mimicando utilizadores reais. Como estamos numa fase de pré-lançamento, o cliente e a equipa de QA deverão estar em uníssono sobre o que é pretendido testar.

Testes de Mutação

Este tipo de testes consiste em pequenas alterações no código base (mutações) que deverão ser detectadas e debeladas pela bateria de testes que estão a correr em cada commit. Como se pode depreender, esta tipologia de testes é primeiramente para testar a bateria de testes existentes e para auxiliar no desenho de novos testes.

O exemplo que se encontra na Wikipedia, transposto para PHP:

A mutação poderá ser somente transformar a condição lógica && para a condição ||.

Para a linguagem PHP podemos utilizar o framework Infection e para JavaScript poderemos recorrer ao framework Stryker.

 

Todos estes testes deverão ser automatizados e, de acordo com outro princípio do XP, deverão correr em contínua integração do código. Não te preocupes com este conceito, um artigo sobre o mesmo já está na calha.

Ficaste convencido que programar com recurso a testes é vantajoso? Partilha a tua opinião.