Design Patterns for Salesforce Git Branching Strategies

Git branching strategies for Salesforce CI/CD are critical to a successful CI/CD implementation and a requirement to practice Continuous Integration in Salesforce.  

In this article, I will discuss some common branching patterns and how they fit (or not) in the world of Salesforce development and DevOps.


Sponsored by Salto

CPQ Deployments, CI/CD, Impact Analysis, and more

30-day free trial here

Git-based Salesforce deployments

Git branching strategies are only needed if you are doing git-based deployments. What does that mean?

A Git-based deployment is a deployment where the metadata to be deployed is stored in a Git branch. Rather than taking metadata from one org and directly uploading it to another org (this is how Change Sets work), you make those changes in a Git branch and use sfdx (via a CI server) to deploy the entire contents of the branch to another org. This works because what's in the Git branch is an sfdx project.

Components of a Git-based deployment

To make Git-based deployments possible, you must have the following:

  • an sfdx project+ your changes are committed to a Git branch
  • A Continuous Integration (CI) server is connected to your Git repository
  • The CI server executes sfdx commands against the sfdx project stored in Git
  • These commands deploy the metadata to another Salesforce org

What are the benefits of Git-based deployments?

The main benefit is your Git branch represents the desired future state of production. This, in turn, allows for the following:

  • Collaboration and visibility with GitHub (or BitBucket, GitLab, etc.)
  • Easy to see the difference between the current state and desired state
  • Enables Continuous Integration
  • Provides a detailed audit or log of changes made to the org

Git-based deployments are great, but they come with a lot of challenges, as we'll see next.

What is a Salesforce Git Branching Strategy?

One of the main problems with Git-based deployments is they open a big can of worms and make your process (initially) more complicated.

Once you start doing Git-based deployments, you must answer these and many other questions:

  • Does the main or master branch represent what's already deployed to production?
  • Do we create feature branches off of main?
  • Do we use feature branches at all?
  • Do we have one git branch per Salesforce sandbox?
  • How to we deploy a git branch to another Salesforce org?
  • How does the sandbox strategy affect the git branching strategy?
  • How often do we merge into main?
  • How and when do we deploy to production?

How you answer those questions becomes your Git branching strategy. So in that sense, a Git branching strategy is your defined pattern for managing Git branches, when to merge them, what they represent, etc.

A Git branching strategy is your defined pattern for managing Git branches, when to merge them, what they represent, and more.

Sandboxes vs. Git branches

Before we look at the patterns for managing Git branches, I think it's useful to talk about sandboxes and how they are, in some ways, a way of branching.

A Git branch starts off as a clone of another branch. For example, we might create a branch called staging that starts exactly the same as main. Over time, we add commits to the staging branch, and it starts diverging from main.

In a way, the staging branch now represents the desired state of the main branch when they are eventually merged.

Do you see any similarities with sandboxes?

Similarities between sandboxes and Git branches

The lifecycle of a Git branch sounds a bit similar to a Salesforce sandbox, for example:

  • Sandboxes are always cloned from another org (production or another sandbox).
  • We develop new features in that sandbox that we eventually want to move to production
  • The sandbox, like a Git branch, diverges from its source org over time as we make new changes.

There are obviously some major differences.

Differences between sandboxes and Git branches

Some differences include the following:

  • Sandboxes take way longer to be created compared to a Git branch (less than a second)
  • Sandboxes are never merged into another org, rather, their metadata (or a subset) is deployed to a target org.

Should sandboxes be used as branches?

Now that we understand the parallels between Git branches and sandboxes, it's worth asking whether sandboxes should be used as a way of branching or whether we need Git branches in the first place.

Sandboxes have (more or less) the same purpose as Git branches in traditional software development. They allow for isolated development of new features, isolated testing, release testing, etc.

The concept of a sandbox doesn't really exist in traditional software development, because in that case you use Git branches. The closest thing to a sandbox in traditional software development is a staging environment. For example, I may have HappySoup.io as production and staging.happysoup.io as a staging environment, where I give can give users the opportunity to test new features before they are released to production.

What are we talking about now?

I know I went on a tangent here with this discussion on git-based deployments and sandbox vs. branches. However, this was necessary to establish two points:

1) Git branching strategies are only needed if you are doing Git-based deployments; they are meant to answer many questions that arise from deploying in this way.

2) Git branching in Salesforce is a little awkward because sandboxes already provide a (poor man's) mechanism for branching, so when thinking of Git branches, it's impossible not to think about how they relate to your  sandbox strategy.

With that out of the way, let's finally explore some Git branching patterns.

Salesforce Git Branching Patterns

I took a lot of inspiration from Martin Fowler's article Patterns for Managing Source Code Branches to write this section. You could even say this is an adaptation of said article, albeit simplified and with a focus on Salesforce development (very different from the type of software Martin had in mind).

Any similarities with that article are on purpose.

First, I'm going to explore some general patterns around Git branches, and then we'll look at specific Git branching strategies and we'll see how those patterns are used in those strategies.  

Mainline

All Git repositories start with a main or master branch. Conceptually this branch is known as the mainline or trunk.  For the remainder of this article, I will refer to this special branch as either main or the mainline.

In both traditional software development and Salesforce development, this branch usually represents the production environment/org.

That said, the branch can represent two things:

1) What can be released to production users

2) What is already in production and available to users

The difference depends on whether the team is using Continuous Delivery vs. Continuous Deployment.

Salesforce Continuous Delivery vs. Continuous Deployment—what’s the difference?
Continuous Delivery and Continuous Deployment in Salesforce. Are they the same?

For most Salesforce teams, this branch represents what's already in production and is considered the source of truth (or so they think...).

main is also where the other initial set of branches (not all branches) originate from, as we'll see shortly.  

Healthy Branch

This is not a branch per set but a property of a branch, typically the mainline. A healthy branch is one that is considered healthy (duh!) in the sense that it represents a deployable code base.

What does that mean?

It means that you can take that branch and deploy it to production at any time. You are so confident that the branch represents what should be in production that you are not afraid to deploy it entirely on a Monday at 11 am without telling anyone.

This is the gold standard many teams strive for. Making sure that main is a healthy branch has many benefits:

  • If teams create branches off of main , they can do so with the confidence that they are getting a healthy copy of the code base.
  • Reduces the number of merge conflicts when you eventually merge any branch back into main
  • It makes the whole Git as source of truth mantra more attainable

Feature Branching

A feature branch is a branch that exists for a specific new feature. For example, if you are going to increase the code coverage of an existing apex class, you could create a feature branch called UnitOfWork-CodeCoverage.

All your work would be done on that branch. That means that you would do the work in VSCode while on that feature branch.

Of course, you need to deploy the new test methods to a sandbox, likely your personal dev sandbox, and this is when the lines between sandboxes and Git branches become blurry. Because Git doesn't care in which sandbox the new code is developed, it only cares about what branch the changes are committed to.

In any case, the feature branch is meant to represent that: a feature.

Where the feature branch originates from (i.e. is it cloned from main or somewhere else) depends on the concrete branching strategy, as we'll see later.

At some point, the feature branch is merged back into main, either directly or through a release branch. Speaking of which...

Release branches

Like Healthy Branch, a release branch doesn't necessarily have to be a branch named release (though this is also widely used, as we'll see later...) but is more of a property of a branch.

A release branch is one that holds multiple changes or new features in preparation for merging those into the mainline.

For example, developers can merge their feature branches into a release branch, and eventually, that release branch is merged into main.

Release branches are used to represent a cut-off, i.e., from a certain point forward, no more changes are added to this release. Any new features are not merged into this branch, and the branch is used for testing and stabilization before merging it into main and deploying to production.

Environment Branch

This is a branch that represents a specific development environment. I think this is pretty rare in traditional software development, but it's actually quite popular in Salesforce development.

In this pattern, a branch represents a specific Salesforce org in the pipeline.

I will discuss a branching strategy that uses this pattern in more detail later.

HotFix Branch

A HotFix branch is a branch that is almost always created off of main and is used to fix a critical production issue.

The branch is created from main because we assume main is a Healthy Branch, and we want to make sure that we fix the issue by taking into account what already exists in production.

Many Salesforce teams use a dedicated sandbox as a HotFix sandbox, typically one that is refreshed every day to ensure that it's as closed to production as possible.

This shows again that the differences (in their purpose) between sandboxes and Git branches are not often super obvious.


I think these are the main patterns worth discussing at this stage. For more patterns, I recommend reading Martin Fowler's article Patterns for Managing Source Code Branches.

Now that we understand the patterns, we can talk about Git branching strategies.

Salesforce Git Branching Strategies

Aside from the Environment Branch pattern, most Git branching strategies used by Salesforce teams are a version of common branching strategies used in traditional software development, so it makes sense that we look at them using their industry-accepted name.

GitFlow

GitFlow is a popular branching strategy for many Salesforce teams and in fact, is the one I use in my personal CI/CD pipeline.

In this strategy, we create a develop or development branch off of main at the beginning of the sprint.

Then, developers create feature branches off of develop; this is why I said earlier that where a feature branch is created from depends on the strategy.

Feature branches are merged into develop, which triggers a deployment to either your QA or Integration sandbox.

At the end of the sprint, we create a release branch off of develop. We then follow the Release Branch pattern and only use this branch for testing and stabilization of the deployment.  

Source: https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow

Eventually, we merge release into main, which should trigger a production deployment.

I think this works well for many Salesforce teams because it's quite common for Salesforce teams to follow an agile-based development process, where you deploy to production at the end of the sprint. So in this model the develop and release branches represent the work-in-progress that we want to deploy before the next sprint begins.

GitHub Flow

Another popular strategy is GitHub Flow, which uses the Feature Branching pattern.

In this strategy, feature branches are created directly off of main, and they are merged directly into main. We don't use the develop or release branches.

Source: https://gaboesquivel.com/blog/2018/recommendations-to-enhance-your-github-flow/

This is a strategy Salesforce teams can use if they consider sandboxes to be an existing type of branching. So rather than creating additional branches, everyone merges directly into main from their own dev sandbox.

A potential workflow could look something like this:

  • Create a feature branch from main
  • Connect that branch to your dev sandbox in vscode
  • Do some development, and commit the changes
  • Push the feature branch to your remote repository. The creation of this branch triggers a CI job that deploys to a QA sandbox
  • Someone does testing in QA
  • When QA passes, you merge the branch into main, which triggers a production deployment.

I used a similar workflow in a previous job, and I think it's great for small teams that can release to production multiple times a day.

Org-based branching strategy

As far as I can tell, this branching strategy doesn't have an industry-standard defined name. This is where every main sandbox in the pipeline has its own longed-lived branch.

So if you have a QA, Integration, and Preprod sandboxes, you'd have a qa, integration and preprod Git branches.

Remember I said the lines between Git branches and sandboxes are blurry? Yeah.

This branching strategy is surprisingly popular among Salesforce teams, and to be honest, I'm not sure why.

Some claim that this makes it easy to tell where a feature currently "lives" in the pipeline, i.e., "is this feature in QA or UAT?" But I think you can answer the same question with the other strategies we saw above.

One benefit I see from this strategy is that it's easier to compare the metadata of multiple sandboxes. For example, if I want to see the differences between my QA and Integration sandbox, I just need to create a pull request between the qa and integration branches.

Also, this pattern assumes that you merge the entire staging or preprod branch into main, but what if something shouldn't be deployed? What if there is a user story that the business concluded shouldn't be released yet?

That's where the Release Branch pattern becomes handy because you can create a release branch and cherry-pick only the commits/user stories that you really want to promote to the next sandbox.

That said, this branching strategy does not mention the use of a release branch, so it's unclear to me how this scenario (a feature needs to be pulled out) is supposed to be handled.


Correction
Someone on Discord pointed out that my argument above about how branches are merged into each other in this strategy is incorrect.

The actual flow of this strategy is as follows:

1) Feature branch is created from main
2) Feature branch is merged to qa, integration and any other longed-lived branches for testing, etc.
3) When you are ready to deploy to production, the feature branch is then merged into main

This means that, unlike in the previous strategies, the feature branch is not deleted after it is merged into a branch before main.

Also, I think this branching strategy is popular, most likely because it's what Copado does. They have a really nice explainer that describes this strategy in detail, and I highly recommend you watch it:

That said, using this strategy means that all your branches are long-lived, even feature branches. For this reason, I do not recommend using this strategy unless you are using a DevOps vendor that manages this complexity for you.

Managing so many long-lived branches manually would introduce too much overhead to any Salesforce team.


Continuous Integration

While not a Git branching strategy, Continuous Integration (CI.) relies on the effective use of Git branches. At the same time, many of the strategies described above assume a Heathy Branch, and the only way to achieve that is by using CI.

So this topic would be incomplete without mentioning CI. CI is a big topic, so I recommend you read my article on that:

Salesforce Continuous Integration—Concepts, components, and challenges
Continuous Integration in Salesforce is not straight forward. Let me show you the way…

And this concludes my review of popular Git branching strategies for Salesforce.

Which branching strategy should you use?

As with most things in software development, it depends.

I recommend you start with the simplest Git branching strategy, possibly GitFlow, to avoid overhead and complexity.

That said, the branching strategy, as we've seen throughout this article, is greatly influenced by your sandbox strategy. It's also influenced by your team size and by how knowledgeable your team is when it comes to Git and Salesforce deployments.

Conclusion

Hopefully, this article was useful in understanding the common patterns of Git branches and their strategies.

You should have the knowledge to choose one for your team.

If you enjoy this article, consider subscribing so you are notified when I release more DevOps and CI/CD content!

Subscribe to become a Salesforce API and CI/CD expert
fullstackdev@pro.com
Subscribe