Entrega contínua superpotente com o Git

Agora que o Git resolveu o problema de merge, os fluxos de trabalho de branches ficaram muito mais atraentes.

Sarah Goff-Dupont Sarah Goff-Dupont

Todos já ouvimos o dizer “cuidado com um código escrito por apenas uma pessoa” e sabemos os benefícios de criar software em equipe: há diferentes maneiras de pensar, diferentes conhecimentos e experiências... e quando essas diferenças ajudam na resolução dos problemas, o resultado é um software melhor. O software fica mais sustentável, com mais qualidade e acaba atendendo melhor às necessidades do usuário.

Colaboração em equipe | Atlassian CI/CD

Mas também sabemos que desenvolver em equipe pode ser confuso!

Você fica tentando entender em quais partes do código todos estão trabalhando, tentando garantir que as alterações não entrem em conflito, tentando encontrar defeitos antes dos clientes e tentando manter todos que estão conectados ao projeto atualizados sobre o andamento das coisas. Cada um desses problemas é solucionado pelos branches do Git ou pela entrega contínua.

Espero convencê-lo de que, combinando os dois (talvez colocando algumas ferramentas incríveis na mistura só de brincadeira), você vai ter uma receita de sucesso superpoderosa.

O poder dos fluxos de trabalho baseados em branches 

Verdade seja dita, não é que o Git por si só seja tão perfeito para entrega contínua. É que os fluxos de trabalho de branches são ótimos para implementação contínua e o Git é ótimo para os fluxos de trabalho de branches. Além de ser o maior aliado da implementação contínua, um fluxo de trabalho de branch e merge permite solucionar bugs difíceis, experimentar novas tecnologias ou apenas começar a codificar um novo recurso do zero sem o risco de que as alterações impeçam o avanço das tarefas dos colegas de equipe.

Diagrama de fluxo de trabalho básico | Atlassian CI/CD

O Subversion e outros sistemas tradicionais de controle de versão também permitem usar branches. Mas vamos fazer uma pausa e conhecer o gêmeo malvado das branches: o merge.

Os sistemas tradicionais de controle de versão como o Subversion não são tão bons em acompanhar versões de arquivos que residem em diferentes branches. Quando chega a hora do merge, o Subversion tem que parar e pedir um monte de instruções. (Sabe aquele pequeno pop-up perguntando “Você quer essa linha ou aquela na versão do merge?”) O grande volume de interação humana necessária durante os merges leva as equipes a instituir congelamentos de código, para que quem estiver realizando o merge não seja interrompido pelas novas alterações chegando em um dos branches. E os congelamentos de código são caros: são períodos bem improdutivo.

O Git, por outro lado, é muito bom em acompanhar as alterações em diferentes versões de arquivos que residem em branches diferentes e sempre sabe como era o ancestral comum do arquivo. Ele tem um GPS integrado que permite navegar nos merges sem ter que parar e pedir instruções o tempo todo.

Com o Git, você está livre para explorar o poder dos branches de uma forma que seria impraticável com o Subversion. A sobrecarga envolvida nas ramificações e merges é tão trivial que os branches que duram apenas um dia ou dois não são só viáveis, mas também benéficos.

Ok, tudo bem. Mas, afinal, por que os branches são tão poderosos para entrega contínua?

As ramificações mantêm a principal limpa e lançável

Estabelecemos que as ramificações de curta duração são uma ótima maneira para os desenvolvedores colaborarem em um projeto ou recurso sem atrapalhar o trabalho dos outros. Mas o mais importante para a implementação contínua é que isolar todo o trabalho em andamento nas ramificações de desenvolvimento mantém a principal e todas as ramificações das versões estáveis em um estado limpo para que você possa lançar à vontade.

Ou seja, é necessário fazer a bateria completa de testes automatizados nos branches de desenvolvimento para que os desenvolvedores tenham certeza sobre a qualidade do código e possam tomar decisões confiantes sobre quando as alterações estão prontas para o merge. (Se você ainda não está fazendo os testes automatizados, consulte este post da RebelLabs com uma conversa divertida e receitas para escrever os primeiros testes de unidade.)

Também significa usar solicitações pull do Git como uma forma de análise de código para que toda a equipe fique confiante sobre a manutenção e a interoperabilidade do código com outras áreas da base de código. Sim, este é um trabalho mais direto do que os modelos de entrega tradicionais exigem. E sim, vale a pena.

O sucesso da entrega contínua depende de manter os branches de versão limpos.

Como exemplo, todas as equipes de desenvolvimento da Atlassian têm um acordo de que nenhum commit é feito direto na principal nem nas ramificações estáveis. Todos trabalham em ramificações. Na verdade, a gente gosta tanto das ramificações que acabou criando uma separada para cada item abordado do Jira.

Ou seja, é possível fazer quantos testes quiser e causar a maior bagunça nas ramificações! A principal continua sempre em um estado em que pode ser lançada e em que você pode criar outras ramificaçõoes sem herdar um monte de código errado. Essa é uma vitória para a CD e a produtividade geral do desenvolvedor (sem falar na motivação que ela traz).

Os branches ajudam a aceitar as contribuições de fora da equipe

A facilidade de usar branches do Git, ainda mais na bifurcação de repositórios inteiros, facilita o recebimento de contribuições de pessoas fora da equipe imediata: prestadores de serviço, desenvolvedores de empresas parceiras, desenvolvedores de outras unidades de negócios etc. Você não precisa perder o sono se preocupando com pessoas não familiarizadas com a base de código fazendo alterações em branches críticos e prejudicando a capacidade de lançar um novo código.

Aqui, mais uma vez, os testes automatizados rigorosos nas ramificações ou repositórios bifurcados são a chave para o sucesso da colaboração. É interessante analisar os resultados de build e de teste antes de aprovar qualquer merge na linha de código da ramificação principal.

Dica profissional: gerenciadores de repositórios como o Bitbucket permitem que você use hooks do Git para impor padrões de qualidade. Por exemplo, você pode definir uma regra em que todos os builds do branch precisam ser aprovados para que uma solicitação pull seja aceita e submetida ao merge.

Branches bem feitos = clareza no acompanhamento do projeto

Tendência do momento: criar um branch de desenvolvimento para cada história, atualização de segurança ou tarefa (ou seja, cada item do JIRA) que você implementa. Adotamos esse modelo de branch por item na Atlassian há alguns anos e não nos arrependemos! Ele também se popularizou entre os clientes da Atlassian.

A criação de uma ramificação para cada item facilita a escolha manual de quais alterações são lançadas para produção ou agrupadas em uma versão. Como você não está acumulando alterações na principal, você pode escolher o que entra nela e quando. Você pode lançar um MVP excelente e outro bom, em vez de esperar até que todos os bons estejam prontos. Ou lançar uma só correção de bug dentro da estrutura de uma versão regular. Mesmo que a correção seja urgente, você não vai precisar dar conta de concluir outras alterações que ainda não estejam prontas para serem lançadas só para poder lançar essa alteração específica.

E essa facilidade de lançar uma só alteração de código é a essência da entrega contínua.

Com essa abordagem, além de manter o código não comprovado fora da principal, quando você inclui a chave do item do Jira relevante e o nome ou as iniciais do desenvolvedor no nome da ramificação, fica muito claro qual é o estado de desenvolvimento de cada item em andamento.

O Bitbucket faz commit da captura de tela do repositório do Git | Atlassian CI/CD

Observe a convenção de nomenclatura usada na figura acima: é a chave exclusiva do item do JIRA que está sendo implementado, além de uma pequena descrição legível sobre o contexto do item. Então, como gerente de versão ou outra parte interessada, você pode olhar para o repositório mostrado acima e saber com rapidez que a história do usuário acompanhada por AMKT-13952 está pronta para ser lançada porque o merge na ramificação principal já ocorreu. Essa é uma ótima capacidade de acompanhamento sem todo aquele esforço manual.

Como funciona o Git somado ao fluxo de trabalho de entrega contínua?

Que bom que você perguntou! Vou falar um pouco disso em linhas gerais, já que outros artigos neste site se aprofundam mais em cada fase.

  • Crie um branch para o item no qual você vai trabalhar. Inclua a chave do item do JIRA no nome do branch para esclarecer o objetivo do branch. E se você estiver usando outras ferramentas da Atlassian, como o Bitbucket ou o Bamboo, elas vão usar essa chave do item para criar links entre o item, o branch, todos os commits, os builds, as solicitações pull e as implementações relacionadas a esse item. Em outras palavras, as chaves de item são mágicas.
  • Faça as alterações no branch. Você está sozinho no próprio mundo aqui, então faça o quiser. Experimente coisas novas. Destrua coisas. Não importa, porque você também vai seguir este próximo passo:
  • Coloque o branch na integração contínua. (Aliás, o Bamboo faz essa tarefa para você automaticamente.) Cabe a você e à equipe decidir se querem executar testes especializados, como testes de carga ou testes baseados em IU de ponta a ponta aqui e se querem fazer o acionamento automático de uma execução de teste sempre que as alterações são enviadas ao branch. As equipes da Atlassian costumam executar testes no nível da unidade e da integração nos branches de desenvolvimento e deixam o desenvolvedor escolher com que frequência eles são executados, para economizar recursos de build e garantir que a fila não fique lotada sem necessidade.
  • Atualize a ramificação com as alterações mais recentes da ramificação principal com frequência. Para fazer a atualização, você pode usar a opção rebase ou o commit da ramificação. Você decide. (Mas não faça rebase em uma ramificação compartilhada com outros desenvolvedores, eles não vão ficar felizes.) De qualquer forma, você vai descobrir conflitos de integração antes de fazer o merge, o que também ajuda a manter a ramificação principal limpa.
  • Crie uma solicitação pull quando estiver pronto para fazer o merge. A implementação foi concluída, você enviou pull das alterações dos colegas de equipe, resolveu todos os conflitos resultantes e todos os testes no branch foram aprovados.
  • Faça merge e implemente à vontade. Algumas equipes preferem fazer o lançamento automático de cada alteração logo após o merge na principal e todos os testes são aprovados nela, ou seja, o modelo de implementação contínua. Outras equipes preferem decidir elas mesmas quais alterações são lançadas e quando. A escolha é sua e da equipe.

Automação da integração contínua do Git | Atlassian CI/CD

Então é isso. Os fluxos de trabalho de branch simplificam a entrega contínua e o Git elimina a dificuldade dos branches. Continue lendo para se aprofundar e saber como configurar o repositório do Git para ficar compatível com a implementação contínua e como colocar todas essas ideias em prática usando as ferramentas da Atlassian. Vejo você na próxima página!