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
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
masterbranch represent what's already deployed to production?
- Do we create feature branches off of
- 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
- 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
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.
All Git repositories start with a
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.
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.
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
- It makes the whole Git as source of truth mantra more attainable
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
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...
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
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.
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.
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 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
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.
Eventually, we merge
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
release branches represent the work-in-progress that we want to deploy before the next sprint begins.
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
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
- 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
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
Also, this pattern assumes that you merge the entire
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.
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
2) Feature branch is merged to
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
This means that, unlike in the previous strategies, the feature branch is not deleted after it is merged into a branch before
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.
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:
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.
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!