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

As I discussed at length in "Super-powered continuous delivery with Git", using prolific branching in your continuous delivery workflow is a Good Thing™. It helps keep your most important branches in a clean and releasable state, allows developers to try new things without stepping on their teammates' toes, and, if done right, makes project tracking easier.

We've used a branch-per-issue workflow for several years now, as have many of our customers. And we've baked support for it into Jira Software and the Atlassian developer tools so it's not just a best practice, it's also an easy practice. So let's dive deep into the branch-per-issue model, and how it fits with the three most common continuous delivery workflows: SaaS products, installed or mobile products, and Gitflow (which can work for either product type).

The basic branch-per-issue workflow

The name pretty much says it all: for each issue you work on (or each code change you make, which really ought to be tracked in Jira Software anyway), create a development branch. Then do all the implementation and testing work on that branch. When that's complete, submit a pull request, merge up, and release whenever you're ready.

Basic workflow screenshot | Atlassian CI/CD

Here's how it breaks down step-by-step using Atlassian tools.

Step 0: set up your tool integrations

Work with your friendly neighborhood Atlassian admin to integrate JIRA Software, Bitbucket, and Bamboo. Note that you can mix n' match Cloud and Server options. For example, several teams at Atlassian use JIRA Software Server, Bamboo Server, and Bitbucket Cloud. I also recommend SourceTree very highly for those who prefer working with Git through a GUI instead of through the command line. It's free, so I can't think of a single reason not to at least try it. Once you've installed it, connect SourceTree with your repo(s).

You'll also need to do the other obvious stuff like create issues to track your work, set up a repo or two, and configure your builds and deployment jobs.

Step 1: create your branch

Open up the issue in Jira Software, assign it to yourself, and set it to in progress so your team knows what's up. On the right-hand side is the development panel – click the create branch link in side it. If you have more than one repository manager connected, you'll see a screen that asks you to choose which one will be managing your branch. Else, you'll go straight to the next screen where you'll configure the branch.

Create branch workflows screenshot | Atlassian CI/CD

Notice how Bitbucket has picked up the issue key (MKT-15886 in this case), and used it as part of the branch name. This makes all kinds of magical things happen, like sending commit, build, pull request, and deployment information back to the Development panel. Issue keys trigger all sorts of nifty linking and automation throughout your continuous delivery workflow, so be sure to include it in your branch names whether you're working through a UI or 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.

Once everything is set to your liking, click on create branch, and Bitbucket does the rest.

Step 2: code, test, repeat

This part is probably familiar: clone the repo locally if you haven't already, check out your new branch, and start coding. By the time you've made your first push to the new branch, Bamboo has already detected it in your repo and configured continuous integration for it using the plan branches feature (assuming you've got automatic branch management enabled). Essentially, Bamboo listens for new branches in your repo, and applies whatever builds you've configured for master to them.

I also recommend enabling automatic merging through Bamboo. At the start of each build, Bamboo can check out any two branches, merge them, then run the build against the merged code. So for this stage of the continuous delivery workflow, you merge changes from master down to your feature branch. This way, your branch won't drift far from master, and you'll get early feedback as to whether your changes play nicely with the changes on master.

Branch updater screenshot | Atlassian CI/CD

If automatic merging isn't your thing, definitely merge master into your branch (or rebase) and fire off a build just to make sure there aren't any nasty surprises – or fix 'em if they pop up. Once implementation is complete, and all your tests are passing, you're ready for the next step.

Step 3: merge up

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

You can create pull requests from the command line or using SourceTree, but there are some benefits of going through the Bitbucket UI. First, you get the chance to diff your branch against the target branch. Eye-balling the diff often reveals one or two things you'll want to touch up right away. Then from the branch comparison screen, just hit the create pull request button, choose your reviewers, and off you go.

Bitbucket workflows pull request screenshot | Atlassian CI/CD

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.

Pro tip: For master and whatever branch you release from, you'll definitely want to build right away after each push. Configure an automatic build trigger for them in Bamboo with an aggressive polling schedule or a push notification from Bitbucket.

Etapa 4: lance, mas lance direito!

(Any other Devo fans out there? "When some new code comes along, you must ship it." No? Ok... guess I won't quit my day job.)

Bamboo workflows deploy build screenshot | Atlassian CI/CD

Once the build is green on your release branch, you're potentially ready to ship. I say "potentially" because whether or not you actually ship right away is up to your team. (Does it meet all the acceptance criteria? Have the artifacts been sufficiently pummeled by load tests?)

You can certainly use Bamboo to automatically deploy the build to staging or straight to production if you're into full-on continuous deployment. But that's not appropriate for every team and every product – as we're about to discuss.

In these next few sections, I'll walk through steps 1 through 4 again and call out how they differ (if at all) from what's described in the basic branch-per-issue workflow.

Continuous delivery workflow for SaaS products

In terms of the branches you work with and how code moves between them, the SaaS workflow is identical to the basic workflow.

SaaS workflow screenshot | Atlassian CI/CD

Step 1: create your branch

Apenas um ponto a observar aqui é que as equipes de SaaS geralmente criam seus branches de recursos com base no mestre.

Step 2: code, test, repeat

SaaS teams often want to get as close to continuous deployment as possible – and have the luxury of working on a product well-suited for it. In order for that to be viable, you'll need to automate deploys to a test or staging environment as part of this step, rather than waiting until after the pull request step.

Fortunately, lightweight environments are increasingly easy to spin up and tear down on a moments' notice thanks to technologies like Puppet, Chef, Docker, and Vagrant. (I'd love to do a deep-dive here, but that's another article for another day...) And Bamboo supports automated deployments from any branch. So whether you're working with temporary or persistent environments, you can configure it to deploy each and every successful build of your branch to an environment where it can go through a gauntlet of automated UI and/or load tests.

Let's assume that you've already created a deployment project in Bambo associated with this build plan. Pull up (or create) Bamboo's configs for the environment you want to deploy to, and create a trigger that will automatically deploy each successful brand build there.

Test environment screenshot | 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.

Step 3: merge up

The number of pre-production environments your team uses will influence the exact point at which you move to this step. Typically, developers will run in-process tests on their branch with each build, and if those pass, deploy to a test environment for UI, load, and/or exploratory testing. Once everything is ship-shape on test, they create the pull request and merge up to whatever branch you release from (again, typically master).

Step 4: ship it

At this point, you've come full circle: you've merged back up to master and verified tests are passing there. This is also a point where we see lots of variation in different teams' approach.

Some teams trigger an automatic deploy after every successful build of master (in which case, feature flags are essential), some teams wait until a critical mass of changes are on master before tagging a release and triggering a deploy. Similarly, some teams deploy straight to production, others promote the build to a staging environment for one last round of sanity-check tests before taking it live.

There's no magical "best" way to get code from master to customers. As long as you're automating as much possible, you're on the right track.

Continuous delivery workflow for installed products

The primary difference from the basic branch-per-issue workflow is the existence of long-lived branches to house each version you're currently supporting. For enterprise B2B products like Atlassian's, you're probably looking at half a dozen of these branches (or more). For mobile apps, it might be just 2 or 3 (or fewer).

Multiple version workflow screenshot | Atlassian CI/CD

Step 1: create your 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?

In the case of the latter, you'll branch off of master. If it's the former, you'll base your branch off the branch for the earliest version the change is destined for (i.e., the first version in which the bug appeared).

Step 2: code, test, repeat

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.

Updating a version with bugfixes is a far bigger pain with installed products vs. with SaaS products – for your team, and for your customers. In other words, the stakes are higher when it comes to discovering bugs.

So in this workflow, deploying out to a test environment for UI, load, and/or exploratory testing should be considered "really not optional". For that matter, I'd say exploratory testing isn't optional either, considering the sakes. But I digress...

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

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

If you're working on something for an upcoming release, you do a pull request and merge your branch up to master just like in the basic workflow. But if you based your branch off a stable version branch, you'll merge back down to that branch first and make sure all your tests pass there. Then back-port to older versions that need the same update, testing each one along the way. Finally, you'll merge to master so all future versions carry the same change.

Multiple version workflow screenshot | 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.

Alternatively, you can take advantage of Bamboo's automatic merging (described above) to move the changes between stable version branches. In this case, however, use the Gatekeeper option.

Gatekeeper screenshot | Atlassian CI/CD

For example, let's say you've merged a bugfix to the branch for v1.2. Go to the plan branch configs for that branch and set it up to automatically merge down to the branch for v1.1, and so on.

Step 3.5: create a stable version branch

Naturally, if you're working on new stuff for the next version, you'll cut a new stable version branch when you've got a critical mass of features ready. (Ready = implemented, tested, blessed, etc.) This is typically cut from master, and, like master, is configured to build and test automatically each time changes are pushed to it.

If (ok: when) you discover that more changes are needed before shipping the version out, cut feature branches off the stable version branch. Once the changes are ready, merge down to the stable version branch and test there. Assuming that goes well, cascade your change down to master, like in the diagram above.

Whether your team uses pull requests for the cascading merges is up to you. It's a good safety measure, but pull requests and the automated merging features offered by Bitbucket and Bamboo don't mix. So weigh the benefits of automation against the benefits of additional code reviews.

Step 4: ship it

Once your in-process tests are passing on the stable version branch, it's time to deploy. Deploy to where is up to you and your team – most teams take their release to a staging environment first, but others are confident enough in the tests run up to this point that they ship straight to production. 

Entrega contínua à maneira Gitflow

Instead of a single master branch, this approach uses two branches to track the history of the project. While the master branch contains tags and/or commits that record the project's official release history, a shared integration branch (usually called "develop") gives your team a place to ferret out bugs and incompatible changes.

Gitflow screenshot | Atlassian CI/CD

Step 1: create your branch

Here again, the difference from the basic workflow is simply where you branch from. For new development work, your feature branch will be based on develop (make sure you choose a clean commit to branch from!). For bugfixes to a version you've already shipped, it'll be based on a stable version branch – not pictured above, but you get the idea. For more details on Gitflow's variations and their branching structures, check out our tutorial. Bitbucket supports all variations, as well as branch permissions that give you the option to control access master or version branches.

Regardless of where you branch from, use Bamboo's branch updater feature (mentioned above) to pull changes from the parent branch into your feature branch with each build. You'll discover integration issues right away and be able to fix them on your feature branch instead of finding them only after you've merged to develop – at which point, you've already polluted it.

With the Gitflow model, it's possible to release from master, or from stable version branches. The rule of thumb is to make your release the primary branch for your Bamboo builds – this will come into play when it's time to deploy – and enable plan branches so all branches are tested thoroughly.

Step 2: code, test, repeat

The testing step gets interesting with Gitflow. Use plan branches in Bamboo to put your feature branch under test (as in all continuous delivery workflows), but here's the difference: when implementation is complete and all your tests are passing, merge to develop instead of master.

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 confirmação 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árias confirmações 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.

Trigger type screenshot | Atlassian CI/CD

Pro tip: Another advantage of repository-triggered builds for develop is that it uses Bamboo's CPU efficiently, as I mentioned in CI-friendly Git Repos. For teams doing continuous deliver on a large scale, it really makes a difference.

As with the basic workflow, be sure to merge develop down into your feature branch (or rebase) and run tests one last time before going to develop.

Step 3: merge up

Creating a pull request when merging your feature branch to develop is standard practice. Doing peer code reviews at this stage is far easier than delaying it until you're ready to ship, in which case you'd have to review all the changes since the last release at once. No, gracias.

Inevitably, you'll merge your feature branch to develop, only to be met with test failures there. Instead of making changes directly to develop, checkout your branch again and do the work there. (Most teams at Atlassian have "handshake" agreements never to make commits directly on master – only merge commits.)

Step 4: ship it

Designating your release branch as the primary branch for your build plan in Bamboo sets you up for fairly straightforward deployment projects. Whatever your primary branch for the build plan is, that will automatically be the primary branch for your deployment jobs, though you can set up the deployment project to deploy builds from feature branches as well.

Similar to the SaaS workflow, you can automatically create tags on master based on each successful build of develop and deploy from those tags right away. Or, you can wait until several features have been successfully been added to develop, and create the tag by hand. It simply depends on whether you're moving toward continuous deployment, or sticking with continuous delivery. Gitflow accommodates both.

Whew! Four workflows, five diagrams, and about 3,200 words later, here we are. (If you're still reading this, congratulations!)

Hopefully this has given you a foundational understanding of how JIRA Software, Bitbucket, and Bamboo work together to support the branch-per-issue model in continuous delivery. If not, tweet me up and let me know how I can improve this article – it's an important one.

If nothing else, we've seen the value of making a branch for each issue you work on. You won't step on your teammates' toes, and your most important branches stay clean and releasable at all times. So say it with me:

Ramificar a coisa toda é bom demais.

Happy branching!