Friday, February 12, 2021

Scrum Branching Strategy


I've been using git for a few years now and I don't see how I could ever go back to TFVC or *shudder* Visual Source Safe. Git makes managing branches and work significantly easier than other source control technologies I've used in the past, but there seems to be some concern or confusion regarding a good branching strategy to use along with Scrum.

I was pointed to this article by a colleague and while I agreed with some of it, some of it just seemed flat out wrong to me. We've worked really hard through several iterations to land on our branching strategy in my current role and it works really well. I figured I'd share it and our reasons for going this route.

Before I start, it's important to point out that we use Azure DevOps for our CI/CD pipelines so we use their Pull Request tool to merge between some branches. If you're using a different tool then you may follow slightly different processes that make sense for that tool. I've done my best to lay out the way we do it using git terminology, but there are definitely parts that are specific to Azure DevOps.

The Basic Premise

We started with the idea that you should always have a branch that matches what is live right now. We decided that would be our main branch. We also have the classic 4 environments (dev, QA, staging, production), but we determined that we don't necessarily need to have corresponding branches for those environments at all times. That's because at any given time those environments could change. Though there are rules for promoting to QA and staging, dev is free to be updated at any time and is frequently changing (as we develop). We also recognized that in the event of a hotfix we wanted to be able to test the hotfix in the QA environment before promoting the change directly to the production environment (once the hotfix is approved).

The Branches

  • main - this branch matches our production environment nearly all the time
  • staging - this branch is created only when necessary
  • release/<release number> - this branch is our iteration branch and contains all changes that are expected to be available as a result of our iteration
  • <task branches> - these branches are transient, abundant, and created as necessary to complete individual pieces of the iteration (i.e. stories or bugs), but could also be created as sub-task branches to work on tasks within stories or bugs

The Strategy

Once we made the decision to change to this branching strategy, we deleted all branches except main because they were no longer useful. So assume these steps start with only the main branch in existence and that the contents of the main branch exactly represent what is in the production environment.
  1. Create a branch from main called release/<release number> where <release number> represents the next version number
    • If 1.0 is in production you'd create a branch called release/1.1
  2. When a new story is started in the sprint, create a branch for it, named something meaningful to the story (I'll refer to this as the story branch the rest of the way)
    • If the story is about accepting online orders you might name the branch online-orders
  3. Every task in the story could have a separate task branch named whatever you want (I'll refer to these as task branches the rest of the way)
    • If the story to accept online orders has separate pieces to accept credit card payments and invoices you might create a branch called accept-credit-cards when you start working on that task and another developer might create a branch called accept-invoices when they start working on the other task
  4. As the work is completed on each task, the task branch is merged into the story branch
    • Depending on team dynamics, you could use a pull request to do this, but on our team we just review our own changes and make sure there are no merge conflicts
    • It doesn't matter what merge type you use (fast-forward, rebase, etc.) because these commits will be squashed in the next step
  5. When a story is complete, the story branch can be deployed to the dev environment to validate everything is working together as expected
  6. When the story is ready to be tested (this varies by team and project, but on our team a story is ready to be tested when the developers feel confident that it could safely go live right at that moment; in other words, we don't "throw it over the wall" and wait to see what the tester finds), we merge the story branch into the release/<release number> branch and deploy that branch to the QA environment
    1. We do this using a pull request in Azure DevOps
    2. The important part here is to do a squash commit and provide a useful commit message for the work that was done on the story branch (e.g. "Made changes to allow online orders to be placed via credit card and invoice")
  7. If the tester finds something, create a new branch based on the release/<release number> branch, fix the bug, and repeat step 6
  8. At the end of the sprint, only completed stories are in the release/<release number> branch (that's important)
  9. Create a staging branch from main
  10. Perform a squash commit from release/<release number> into staging and make the commit message the release number
    1. We do this via pull request in Azure DevOps and it allows us to use the release number as the title of the pull request and then we fill in the individual commit message from the story branches as the description of the pull request; that way we have the detailed information from each story as well as the release number in the commit log
    2. This is also the time to associate work items with the commit if your team does that
  11. Once the staging branch has been updated, the release/<release number> branch can safely be deleted and the staging branch can be deployed to the staging environment
  12. When the next sprint starts, create a new release/<release number> branch based on the next release (so if our previous release was 1.1 this branch would be named release/1.2) from the staging branch (that's important)
  13. When it's time to deploy the code to production, rebase the changes from the staging branch onto the main branch
    1. Rebasing one branch onto another applies each commit from the source onto the destination, but there's only one commit to staging that gets rebased onto master at this point
    2. Since we create the new release/<release number> branch from the staging branch we already have the same exact commit (with the same SHA1) in the release/<release number> branch that is now in main
  14. Delete the staging branch and repeat this process starting at step 2 for every sprint until the project is complete

Conclusion

Obviously every team is different and what works for us may not work for you. But this does work very well for us. The commit log on main is clean and concise, but contains all of the important information for which stories are in which release. I'll try to do a separate write-up on our hotfix approach tomorrow (or next week or something).

Tuesday, February 2, 2021

Flexbox Row/Column

I've been working more towards using Flexbox and away from Bootstrap's column layout as much as possible. I love the column layout, but it still feels janky to me sometimes and Flexbox seems a lot smoother. I was recently updating a page that uses columns to display 3 items on a row and each item  (with margins and padding) takes up 33% of the row. When there are 4 items, there are 2 rows with 3 items on the first row and 1 item on the second row, but the single item on the second row still only takes up 33% of the second row.

I was able to accomplish this using the following markup and CSS.

<div class="d-flex flex-wrap">
  <div class="item">
    ...single item contents...
  </div>
  <div class="item">
    ...single item contents...
  </div>
  <div class="item">
    ...single item contents...
  </div>
  <div class="item">
    ...single item contents...
  </div>
</div>
.item {
  flex: 0 0 33%;
}

At one point I had flex: 1 0 33% and that was really close, except the one item on the second line took up the entire line. This works beautifully and I'm happy because I get to use flexbox.