課題ごとのブランチモデルでの継続的デリバリーワークフロー

大量のブランチングと継続的デリバリーによってもたらされるものは? 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.

また、ドロップダウンリストではブランチの種類 (バグ修正、機能、リリースなど) に基づいたプレフィックスや、新しいブランチの作成元となる親ブランチまたはタグを選択できます。選択するブランチが Bamboo で構築およびテストされると仮定して、現時点でブランチがクリーンであるかを示すインジケーターが表示されます。こちらは必ず確認してください。最後に必要なものは、すでに何らかの形で壊れている新しいブランチです。

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

チームの一員として、プルリクエストを実行せずに master (またはその他の重要なブランチ) に突撃してマージしてはいけません。プルリクエストはこの世界で失敗することのない唯一のコードレビューの形態です。

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

Bitbucket は正直なところ、プルリクエストに関してはかなり優秀です。横並び差分やインラインコメントのような通常の機能の他に、ルールの設定もできます。アトラシアンのある開発チームでは、プルリクエストは少なくとも 2 人が承認した後でないとマージできないというルールを設定しています。他のチームはある種のゲートキーパーを指名し、ゲートキーパーのみがマージを実行できるようなターゲットブランチの権限を設定しています。また、そのブランチに対して失敗したビルドがある場合はプルリクエストをマージしないというルールをすべてのチームが適用する必要があります。

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.

ステップ 4: 最適な方法でリリースする

(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

ここで注意すべき点は、SaaS チームは一般的に master からフィーチャーブランチを作成することのみです。

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

チームに継続的デプロイを行う意思がなく、人間がリリース時期を決定することを望んでいるとしても、成功したブランチビルドを環境にデプロイするのはよい考えです。このことにより、自分やチームメイトはマージする前に探索的テストを実施する機会が与えられます。

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

どこからブランチを作成するかは、どのような種類の変更を行うかによります。先週リリースしたばかりの製品のバグ修正でしょうか? または、次のリリース用の新しい機能でしょうか?

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

SaaS 製品については、すべてのインプロセステストを実行した段階で成功したビルドをブランチからテスト環境にデプロイすることをお勧めします。ただし、お勧めする理由は多少異なります。

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...

ステップ 3: マージする (マージアップ/マージダウン)

面白くなるのはここからです。

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

アトラシアンツールはいくつかの場面で役に立ちます。はじめに、Bitbucket で安定したバージョンのブランチを通じて マージを自動的にカスケードダウンできます。各ブランチが新しいコードを受信するたびに自動的にビルドするように構成されていることを確認します。

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. 

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.

開発はすべてのチームの変更が一緒に煮込まれる鍋のようなものです。テストの失敗のデバッグを簡単に (調べるべき各ビルド間の変更を少なく) するため、すべてのコミットに対するフィードバックが求められます。これを確実に実行する最善の方法は、Bitbucket からのプッシュ通知に基づいてビルドをトリガーするように開発を構成することです。リポジトリを定期的にポーリングすると、開発者が頻繁に変更を受信するため、1 つのビルドで複数のコミットからの変更をキャプチャする場合があります。この方法は変更の間隔が開いているブランチにより適しています。

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:

ワークフローからブランチングを行うのは「Good Thing™」です。

Happy branching!