Fluxos de trabalho de entrega contínua com o modelo de branch por edição

Branching produtivo mais entrega contínua? Pode apostar seu SaaS.

Sarah Goff-Dupont Sarah Goff-Dupont

Como discuti bastante em "Entrega contínua superpotente com o Git", é ótimo usar o branch produtivo no fluxo de trabalho de entrega contínua. Essa ação ajuda a manter os branches mais importantes em um estado limpo e lançável, permite que os desenvolvedores experimentem coisas novas sem atrapalhar o trabalho dos colegas de equipe e, quando feito corretamente, facilita o acompanhamento do projeto.

As equipes da Atlassian usam um fluxo de trabalho de branch por item há vários anos, assim como muitos dos clientes. E preparamos o suporte no Jira Software e nas ferramentas do desenvolvedor da Atlassian, então esse modelo não é apenas uma prática recomendada, é também uma prática fácil. Vamos conhecer melhor o modelo de branch por item e como ele se encaixa nos três fluxos de trabalho de entrega contínua mais comuns: produtos de SaaS, produtos instalados ou móveis e Gitflow (que pode funcionar para qualquer tipo de produto).

O fluxo de trabalho básico de branch por item

O nome já diz tudo: para cada item em que você trabalha (ou para cada alteração de código que você faz, o que deve ser acompanhado no Jira Software de qualquer maneira), crie um branch de desenvolvimento. Depois, faça todo o trabalho de implementação e teste nesse branch. Quando essa etapa for concluída, envie uma solicitação pull, faça o merge e lance quando estiver pronto.

Captura de tela do fluxo de trabalho básico | Atlassian CI/CD

Veja o passo a passo disso usando as ferramentas da Atlassian.

Etapa 0: configurar as integrações de ferramentas

Trabalhe com o simpático administrador da Atlassian próximo de você para integrar o JIRA Software, o Bitbucket e o Bamboo. Observe que você pode combinar opções de Cloud e de Server. Por exemplo, várias equipes da Atlassian usam o JIRA Software Server, o Bamboo Server e o Bitbucket Cloud. Também recomendo bastante o SourceTree para quem prefere trabalhar com o Git usando uma GUI em vez da linha de comando. Ele é grátis, então não há motivo para não experimentar. Depois de instalá-lo, conecte o SourceTree aos repositórios.

Você também vai precisar fazer outras coisas óbvias, como criar itens para acompanhar o trabalho, definir alguns repositórios e configurar os builds e os trabalhos de implementação.

Etapa 1: criar o branch

Abra o item no Jira Software, atribua-o a você e defina-o como em andamento para que a equipe saiba o que está acontecendo. No lado direito está o painel de desenvolvimento. Clique no link criar branch ao lado dele. Se houver mais de um gerenciador de repositórios conectado, vai aparecer uma tela para que você escolha qual deles vai gerenciar o branch. Caso contrário, você vai direto para a próxima tela para configurar o branch.

Captura de tela da criação de fluxos de trabalho de branch | Atlassian CI/CD

Observe como o Bitbucket pegou a chave do item (MKT-15886 neste caso) e a usou como parte do nome do branch. Assim, toda a mágica acontece, como enviar informações de commit, build, solicitação pull e implementação de volta ao painel Desenvolvimento. As chaves de item acionam todos os tipos de links e de automação interessantes no fluxo de trabalho de entrega contínua inteiro, então, não deixe de incluí-las nos nomes dos branches, seja trabalhando em uma IU ou no cmd.

Observe também as listas suspensas que permitem selecionar um prefixo para o branch com base em sua variável (correção de bug, recurso, versão etc.) e o branch pai ou marca com base na qual criar seu novo branch Presumindo que o branch selecionado esteja sendo compilado e testado pelo Bamboo, você verá um indicador de se o branch está limpo no momento. Definitivamente fique atento a isso! Tudo o que você não precisa é um branch novinho que já está com algum tipo de defeito.

Depois que tudo estiver definido conforme você gostaria, clique em criar branch e o Bitbucket vai fazer o resto.

Etapa 2: codificar, testar, repetir

Esta parte já é familiar: clone o repositório no local se ainda não clonou, consulte a nova ramificação e comece a codificar. Quando você fez o primeiro push para a nova ramificação, o Bamboo já o detectou no repositório e configurou a integração contínua para ele usando o recurso de ramificações do plano (supondo que o gerenciamento automático de ramificações esteja habilitado). O Bamboo identifica as novas ramificações no repositório e aplica a eles os builds que você configurou na principal.

Também recomendo habilitar o merge automático usando o Bamboo. No início de cada build, o Bamboo consegue consultar duas ramificações quaisquer, fazer o merge delas e, então, executar o build no código após o merge. Neste estágio do fluxo de trabalho de entrega contínua, você faz merge das alterações da principal na ramificação de recurso. Assim, a ramificação não se desvia muito da principal e você recebe um feedback antecipado sobre a adaptação das alterações que fez em relação às alterações na principal.

Captura de tela do atualizador de branch | Atlassian CI/CD

Se você prefere não usar o merge automático, faça merge da principal na ramificação (ou faça rebase) e acione um build só para que não haja surpresas desagradáveis ou, caso haja, para fazer a correção. Quando a implementação for concluída e todos os testes forem aprovados, você vai estar pronto para a próxima etapa.

Etapa 3: fazer o merge

Sendo cidadãos de respeito de nossas equipes, nunca cobramos com antecedência e mesclamos à principal (ou qualquer outra ramificação crítica) sem fazer uma solicitação pull – a única forma de revisão de código conhecida no universo que não é uma droga.

Você pode criar solicitações pull usando a linha de comando ou o SourceTree, mas o uso da IU do Bitbucket oferece alguns benefícios. Primeiro, você consegue comparar o branch com o branch de destino. A análise da comparação muitas vezes revela algumas coisas que você já vai querer melhorar. Depois, na tela de comparação de branch, basta clicar no botão criar solicitação pull, escolher os revisores e pronto.

Captura de tela da solicitação pull dos fluxos de trabalho do Bitbucket | CI/CD da Atlassian

O Bitbucket é (para ser sincera) bastante tirano no que se refere a solicitações pull. Além das coisas normais, como comentários em linha e diffs lado a lado, você também pode definir regras. Algumas equipes de desenvolvimento na Atlassian definem uma regra que determina que solicitações pull somente podem ser mescladas depois de serem aprovadas por pelo menos duas pessoas. Outras equipes designam um tipo de gatekeeper: permissões no branch de destino são definidas de modo que apenas o gatekeeper possa realizar merges. E todas as equipes devem ativar a regra que impede a solicitação pull de ser mesclada se houver qualquer build com falha com relação aquele branch.

Dica profissional: para a ramificação principal e qualquer outra ramificação em que você faz lançamentos, é muito importante fazer o build logo após cada push. Configure um acionador de build automático para eles no Bamboo com um agendamento de sondagem agressivo ou uma notificação push do Bitbucket.

Etapa 4: lance, mas lance direito!

(Há mais algum fã do Devo aí? "Quando um código novo aparece, você precisa lançá-lo". Não? Ok... então acho que eu não vou largar o trabalho).

Captura de tela do build de implementação de fluxos de trabalho do Bamboo | Atlassian CI/CD

Quando o build está verde no branch da versão, é provável que esteja tudo pronto para o lançamento. Eu digo "é provável" porque se você vai lançar de imediato ou não é uma decisão da equipe. (Ele atende a todos os critérios de aceitação? Os artefatos passaram por testes de carga suficientes?)

Você pode usar o Bamboo para fazer a implementação automática do build no ambiente de staging ou direto na produção no caso da implementação contínua completa. Mas essa ação não é apropriada para todas as equipes e todos os produtos, como vamos discutir agora.

Nestas próximas seções, vou percorrer as etapas de 1 a 4 de novo e apontar as diferenças (caso haja) em relação ao que está descrito no fluxo de trabalho básico de branch por item.

Fluxo de trabalho de entrega contínua de produtos de SaaS

Em relação aos branches com os quais você trabalha e como o código se movimenta entre eles, o fluxo de trabalho de SaaS é idêntico ao fluxo de trabalho básico.

Captura de tela do fluxo de trabalho de SaaS | Atlassian CI/CD

Etapa 1: criar o branch

Apenas um ponto a observar aqui é que as equipes de SaaS geralmente criam suas ramificações de recursos com base na principal.

Etapa 2: codificar, testar, repetir

As equipes de SaaS costumam querer se aproximar ao máximo possível da implementação contínua e têm a vantagem de trabalhar com um produto adequado. Para que essa opção seja viável, você vai precisar automatizar as implementações em um ambiente de teste ou de staging nesta etapa, em vez de esperar pela etapa da solicitação pull.

Por sorte, os ambientes leves são cada vez mais fáceis de ativar e desativar de imediato graças a tecnologias como Puppet, Chef, Docker e Vagrant. (Eu adoraria me aprofundar aqui, mas esse é um artigo para outro dia...) E o Bamboo dá suporte a implementações automatizadas de qualquer branch. Portanto, se você está trabalhando com ambientes temporários ou persistentes, pode configurá-lo para implementar cada build bem-sucedido do branch em um ambiente onde ele possa passar por uma batelada de testes automatizados de IU e/ou de carga.

Vamos supor que você já tenha criado um projeto de implementação no Bambo associado a esse plano de build. Puxe (ou crie) as configurações do Bamboo no ambiente em que você quer implementar e crie um acionador para fazer a implementação automática de cada build de branch bem-sucedido.

Captura de tela do ambiente de teste | Atlassian CI/CD

Mesmo que sua equipe não sonhe com implementação contínua e prefira tomar uma decisão humana sobre quando lançar, implementar builds de branch com sucesso em um ambiente é uma boa ideia. Isso dá a você e aos seus colegas de equipe a oportunidade de realizar alguns testes exploratórios antes da merge.

Etapa 3: fazer o merge

O número de ambientes de pré-produção que a equipe usa vai influenciar o ponto exato em que você passa para esta etapa. Em geral, os desenvolvedores executam os testes durante o processo na ramificação a cada build, e quando os testes são aprovados, eles implementam em um ambiente de teste para fazer testes de IU, carga e/ou exploratórios. Depois que todos os testes são aprovados, eles criam a solicitação pull e fazem merge para a ramificação de lançamento (que costuma ser a ramificação principal).

Etapa 4: lançar

Neste ponto, você fez o circuito completo: fez merge de volta na ramificação principal e verificou se os testes foram aprovados lá. Este também é um ponto em que vemos bastante variação na abordagem de diferentes equipes.

Algumas equipes acionam uma implementação automática após cada build bem-sucedido da ramificação principal (nesse caso, as marcações de recursos são essenciais) e algumas equipes esperam até que haja um grande volume de alterações na ramificação principal para marcar um lançamento e acionar uma implementação. Da mesma forma, algumas equipes implementam direto na produção, outras promovem o build para um ambiente de staging para uma última rodada de testes de verificação de integridade antes de lançá-la na produção.

Não há uma "melhor" maneira mágica de enviar o código da ramificação principal para os clientes. Desde que você esteja automatizando ao máximo possível, saiba que está no caminho certo.

Fluxo de trabalho de entrega contínua de produtos instalados

A principal diferença do fluxo de trabalho básico de branch por item é que existem branches de longa duração para abrigar cada versão com suporte no momento. Para produtos B2B corporativos como os da Atlassian, estamos falando de cerca de meia dúzia desses branches (ou mais). Para aplicativos móveis, pode ser apenas 2 ou 3 (ou menos).

Captura de tela do fluxo de trabalho de várias versões | Atlassian CI/CD

Etapa 1: criar o branch

De onde você cria o branch dependerá de que tipo de alteração você está fazendo. É uma correção de bug para a versão que acabou de enviar na semana passada? Ou uma nova funcionalidade para a próxima versão?

No caso dos aplicativos móveis, você vai criar ramificações a partir da ramificação principal. No caso dos produtos B2B, você vai basear a ramificação na ramificação da versão mais antiga de destino da alteração (ou seja, a primeira versão em que o bug apareceu).

Etapa 2: codificar, testar, repetir

Como acontece com produtos de SaaS, é uma boa ideia compilar com sucesso do seu branch para um ambiente de teste depois que todos os testes em processo estiverem ocorrendo. Porém, os motivos pelos quais isso é uma boa ideia são um pouco diferentes.

Atualizar uma versão com correções de bugs é um problema muito maior com produtos instalados do que com produtos de SaaS, tanto para a equipe quanto para os clientes. Em outras palavras, a responsabilidade é muito maior quando se trata de descobrir bugs.

Portanto, neste fluxo de trabalho, a implementação em um ambiente de teste para testes de UI, de carga e/ou exploratórios não deve mesmo ser considerada "opcional". Nesse caso, eu também diria que os testes exploratórios não são opcionais, considerando as vantagens. E digo mais...

Etapa 3: mesclar para cima (e/ou para baixo)

Então. É aqui que as coisas ficam interessantes.

Quando está trabalhando em algo para uma próxima versão, você faz uma solicitação pull e o merge da ramificação na principal, como no fluxo de trabalho básico. Mas se você baseou a ramificação em uma ramificação da versão estável, primeiro você vai fazer o merge de novo nessa ramificação e verificar se todos os testes foram aprovados lá. Depois, retorne para as versões mais antigas que precisam da mesma atualização, testando cada uma ao longo do processo. Por fim, você vai fazer o merge na ramificação principal para que todas as versões futuras tenham a mesma alteração.

Captura de tela do fluxo de trabalho de várias versões | Atlassian CI/CD

Suas ferramentas da Atlassian podem ajudar de algumas maneiras. Primeiro, você pode fazer o Bitbucket distribuir suas merges em cascatas automaticamente pelos branches de versão estáveis. Tenha cada branch configurado para build automático sempre que ele receber novo código.

Como alternativa, você pode aproveitar o merge automático do Bamboo (descrito acima) para mover as alterações entre os branches das versões estáveis. Mas, nesse caso, use a opção Gatekeeper.

Captura de tela do Gatekeeper | Atlassian CI/CD

Por exemplo, digamos que você fez merge de uma atualização de segurança no branch da v1.2. Acesse as configurações de branch do plano desse branch e configure-o para fazer o merge automático no branch da v1.1 e assim por diante.

Etapa 3.5: criar um branch da versão estável

É claro que se estiver trabalhando em algo novo para a próxima versão, você vai separar uma nova ramificação da versão estável quando tiver um grande volume de recursos prontos. (Pronto = implementado, testado, abençoado etc.) Essa ramificação costuma ser separada da principal e, como a principal, é configurada para fazer build e testes automáticos sempre que receber alterações.

Se (ok, quando) você descobrir que mais alterações são necessárias antes de lançar a versão, separe as ramificaçãoes de recurso da ramificação da versão estável. Quando as alterações estiverem prontas, faça merge na ramificação da versão estável e teste lá. Se tudo ocorrer bem, transmita a alteração em cascata para a ramificação principal, como no diagrama acima.

Você decide se a equipe vai usar solicitações pull para os merges em cascata. Essa é uma boa medida de segurança, mas as solicitações pull e os recursos de merge automatizados oferecidos pelo Bitbucket e pelo Bamboo não se misturam. Então, pondere os benefícios da automação em relação aos benefícios das análises de código adicionais.

Etapa 4: lançar

Quando os testes durante o processo são aprovados no branch da versão estável, é hora de implementar. Você e a equipe decidem onde implementar. A maioria das equipes coloca a versão em um ambiente de staging primeiro, mas outras têm confiança suficiente nos testes executados até o momento que lançam direto na produção.

Entrega contínua à maneira Gitflow

Em vez de uma só ramificação principal, essa abordagem usa duas ramificações para acompanhar o histórico do projeto. Embora a ramificação principal contenha tags e/ou commits que registram o histórico de versão oficial do projeto, uma ramificação de integração compartilhada (chamado de "develop") oferece à equipe um lugar para detectar bugs e alterações incompatíveis.

Captura de tela do Gitflow | Atlassian CI/CD

Etapa 1: criar o branch

Aqui, de novo, a diferença em relação ao fluxo de trabalho básico é apenas de onde a ramificação é criada. Para um novo trabalho de desenvolvimento, a ramificação de recurso vai ser baseada no develop (escolha sempre um commit limpo para criar a ramificação!). Nas correções de bugs de uma versão já lançada, vai ser usado uma ramificação da versão estável, que não está na imagem acima, mas você pode ter uma ideia. Para obter mais informações sobre as variações do Gitflow e as estruturas da ramificação, consulte este tutorial. O Bitbucket dá suporte a todas as variações e permissões de ramificação que oferecem a opção de controlar as ramificações principais ou de versões.

Seja qual for a origem do branch, use o recurso de atualização de branch do Bamboo (mencionado acima) para extrair as alterações do branch pai para o branch de recurso em cada build. Você logo vai descobrir problemas de integração e vai poder corrigi-los no branch de recurso em vez de encontrá-los só depois do merge no develop, que nesse ponto, já vai estar poluído.

Com o modelo do Gitflow, é possível lançar da ramificação principal ou das ramificações das versões estáveis. A regra geral é fazer com que a versão seja a ramificação principal para os builds do Bamboo (ela vai entrar em cena quando chegar a hora de implementar) e habilitar as ramificações de plano para que todas as ramificações sejam testadas por completo.

Etapa 2: codificar, testar, repetir

A etapa de teste fica interessante com o Gitflow. Use as ramificações de plano no Bamboo para colocar a ramificação de recurso em teste (como em todos os fluxos de trabalho de entrega contínua), mas aqui está a diferença: quando a implementação estiver concluída e todos os testes forem aprovados, faça o merge no develop, não na principal.

Desenvolver é o tipo de caldeirão de culturas em que todas as alterações da sua equipe podem crescer juntas, e você vai querer feedback de cada commit de modo a tornar a depuração de falhas de teste mais fácil (menos alterações entre cada build para examinar). A melhor maneira de garantir isso é configurar o desenvolvimento para acionar builds com base em notificações push do Bitbucket. Sondar periodicamente o repositório acabará capturando alterações de vários commits em uma única compilação, pois o desenvolvimento recebe alterações com muita frequência, que é mais adequado para branches cujas alterações são mais espaçadas.

Captura de tela do tipo de acionador | Atlassian CI/CD

Dica profissional: outra vantagem dos builds acionados pelo repositório para o desenvolvimento é que a CPU do Bamboo é usada com eficiência, como mencionei em Repositórios do Git ideais para a integração contínua. Para equipes que fazem entrega contínua em grande escala, faz toda a diferença.

Como no fluxo de trabalho básico, faça o merge do develop no branch de recurso (ou faça rebase) e execute os testes uma última vez antes de voltar ao develop.

Etapa 3: fazer o merge

Criar uma solicitação pull ao fazer merge do branch de recurso no develop é a prática padrão. Fazer análises de código por colegas nesta fase é muito mais fácil do que atrasar o código até que você esteja pronto para lançar e, nesse caso, você precisaria analisar todas as alterações desde a última versão de uma só vez. Não, obrigado.

Seria inevitável fazer merge da ramificação de recurso no develop e encontrar as falhas de teste lá. Em vez de fazer alterações diretas no develop, consulte a ramificação de novo e faça o trabalho lá. (A maioria das equipes da Atlassian tem um acordo informal de nunca fazer commits direto na principal, apenas commits de merge).

Etapa 4: lançar

A designação do branch de versão como o branch principal do plano de build no Bamboo permite que você execute projetos de implementação bem simples. Seja qual for o branch principal do plano de build, ele vai ser automaticamente o branch principal dos trabalhos de implementação, embora você possa configurar o projeto de implementação para também implementar builds de branches de recursos.

Como no fluxo de trabalho de SaaS, você pode fazer a criação automática de tags na ramificação principal com base em cada build bem-sucedido do develop e implementar de imediato usando essas tags. Ou você pode esperar até que vários recursos sejam adicionados com sucesso no desenvolvimento e fazer a criação manual da tag. Depende se você está no processo de implementação contínua ou continuando com a entrega contínua. O Gitflow atende às duas opções.

Ufa! Quatro fluxos de trabalho, cinco diagramas e cerca de 3.200 palavras depois, cá estamos. (Se você ainda está lendo esse artigo, parabéns!)

Espero que você tenha adquirido um entendimento básico de como o JIRA Software, o Bitbucket e o Bamboo trabalham juntos para dar suporte ao modelo de branch por item na entrega contínua. Caso contrário, pode me enviar um tweet dizendo como posso melhorar este artigo, pois ele é importante.

No mínimo, vimos o valor de criar um branch para cada item de trabalho. Você não vai atrapalhar os colegas de equipe e os branches mais importantes vão continuar sempre limpos e lançáveis. Então diga comigo:

Ramificar a coisa toda é bom demais.

Tenha um ótimo branch!