In this article, we look at why selecting a branching strategy is an important step in defining a DevOps strategy that’s as unique as your team’s needs. We’ll look at a few approaches and see the ways they align to different approaches to delivery. Hopefully one will resonate with you as an approach to bringing your Salesforce DevOps to life.
Why do I need a branching strategy?
Version control is a fundamental building block of any DevOps architecture, Salesforce or otherwise. So it’s important to choose a branching model that fits your needs as a business, and as a team delivering change to that business. The right branching strategy sets your DevOps process up for success in a number of ways:
- It helps define how each change is handled by the team, and ensures a common approach.
- It helps reduce the complexity of change control and deployments.
- It can make your process more efficient and accurate thanks to better collaboration.
The benefits of version control
Version control has underpinned software development on other platforms for a long time. But until recently, Salesforce teams were behind the curve when it came to adopting version control. This was due to the platform’s unique ‘clicks not code’ approach to org configuration, which reduced the need for complex dev tooling.
As the Salesforce platform has continued to mature and offer more complex functionality, the value of version control has become increasingly apparent, and we’ve seen the transition to source-driven workflows become more common. Our latest State of Salesforce DevOps report, based on a survey of over 1,200 Salesforce teams, found that 85% of respondents were already using version control or plan to adopt it over the next year.
Fundamentally, version control provides a structure for tracking changes to files over time. Those files can be text documents, source code, or Salesforce metadata. That gives you a complete history of any change, to any file, at any time, by anyone, and a much greater level of insight than you get through Salesforce natively.
There are many flavors of version control out there, but Git is the de facto standard for Salesforce. Git provides teams with the ability to separate out multiple streams of development and avoid stepping on each other’s toes by making changes in isolated branches, instead of in shared sandboxes.
Git also provides a shared source of truth for all code changes. Each repository has a “main” or “master” branch, which holds the current ‘production ready’ versions of all files. By having everyone work from this single starting point, and tracking all changes across the codebase, it’s easy for teams to have a shared understanding of what’s approved and reduce the chances of code duplication.
Git gives you a structured way to review changes via pull requests and integrate them together before release, as well as dealing with any conflicts that may arise. Continuous integration, and as a result continuous delivery, is built on top of version control — it’s not possible without it. So version control is completely foundational to any DevOps process.
A branching strategy sets the rules for how you use and manage branches when developing features in version control, but there’s no one model that suits every team perfectly. Picking the right strategy for your team will depend on many things, like:
Team size: Larger teams are more likely to have a larger variety of roles that need to interact with the version control strategy, and this may have an impact on which model is right for you.
Team structure/Complexity: More complex models will take longer for newer and less technical members of the team to get their heads around.
Desired release cadence: There can be business reasons that dictate a particular release cycle. Branching strategy allows you to manage those requirements.
Current workflow: In many ways, switching to using version control is an opportunity to reshape your workflow. But it makes sense to start with a branching strategy that more closely resembles your existing workflow. How are your environments set up (e.g. how many Sandboxes)? How might these environments correspond to your branches?
Git as a backup
This strategy is often the first step teams take into version controlling their metadata. They set up one branch per environment, and start pushing metadata from their orgs into the branches, often with an automated CI job.
While this is a decent first step towards getting metadata into Git, the development life cycle is not really driven by version control. Version control isn’t acting as a source of truth here, just a record of the changes that have happened. To be clear, this is still useful — but to get most of the benefits of Git you need to ensure that once changes are developed, they go into version control to allow them to flow between environments smoothly.
For example, in this flow you can’t easily accommodate parallel development streams on multiple features. There are no pull requests in this model, which would allow you to easily integrate peer review into the workflow. And because you’re not regularly deploying from version control into your environments, you probably won’t have confidence that you could roll back a release by deploying an older revision from version control.
Version control is first and foremost designed to help teams create, track and deploy new features. It shouldn’t be seen as a full backup of your Salesforce environments for disaster recovery purposes — that’s what backup solutions are for in your complete DevOps solution.
Feature branch model
This is the simplest model that actually uses Git as part of your development process, rather than just a lightweight backup. Some teams will have good reasons for preferring more complex branching strategies, but the simplicity of the feature branch model makes it a good starting point. With a feature branch strategy, the latest version of your metadata is stored on the master branch, and it should ideally always be in a state that’s ready to release. This is your only long-lived, permanent branch. When someone wants to develop a feature or a fix, they create a new branch from master, and work on that branch. Once the feature is done, the branch is merged back into master.
Ideally, in this model, feature branches should be as short-lived as possible and deleted after use for general tidiness. Larger features should be sliced into smaller deliverables and implemented one at a time. This reduces the length of the feedback cycle, and reduces the chance of merge conflicts.
The master branch is usually deployed automatically to a staging environment whenever it is updated, using a CI process. This means that the latest version is always available for testing on the staging environment. Once you’re happy with the version on staging, a manual deployment from master to the production environment releases the changes to your end users. Optionally, before deployment to production, you could deploy to a UAT or QA environment for further testing.
Protected master branch
This strategy is similar to the feature branch model, but adds one long-lived branch for integration, before merging into master. In this model, the master branch isn’t updated until everything is tested and ready to go. Master is a more protected source of truth, as only work that has been fully tested in UAT makes it into the branch.
One drawback with this model is that it’s not possible to propagate just a subset of changes if some others aren’t ready yet. If some work has all been tested and approved in UAT, but other things aren’t good to go yet, you can’t easily cherry pick what to promote to master. You’d need to revert features that aren’t ready from the release branch. So a lot of the teams don’t like this model because of the inflexibility to pull changes.
Expanded branching model
This model adds a few extra long-lived branches which correspond to your integration and UAT/QA environments. Once a feature branch is complete, rather than merging straight into master, you merge into your integration branch. To promote any changes from there that you’re happy with, you would merge into the UAT branch, and then onwards into master.
Taking this approach allows more fine-grained control of which changes get through to each environment and allows for isolated reviews of changes on a per-request basis. The expanded branching strategy works well for teams that are more concerned about gating and quality control than releasing as frequently as possible.
Which is the right strategy for your team?
Your choice of branching strategy should align with the priorities of your team:
Git as a backup. Choose this strategy if your team is completely new to Git and needs a gentle learning curve. This is not a long-term strategy to adopt.
Feature branch model. This option is great for teams that rapidly iterate and want to prioritize agility — Gearset uses this strategy and it allows us to release multiple times a day. If your process requires more stage-gates and checks along the way, this may not work for your longer dev cycles.
Protected master branch. Teams that want to emphasize rigorous testing over fast delivery might choose this model. But this model creates a queue where items requiring longer test cycles can hold up other changes until promoted.
Expanded branch model. This choice is the best choice for teams that want smaller releases with multiple stage gates for testing. It allows for an asynchronous approach to releases, because pieces of work can be promoted through your environments individually. So your team can say things like, “JIRA 34533 is in UAT awaiting sign-off for release to Production.”
Where can I learn more?
This has been very much a whistlestop tour of a few branching strategies. As we’ve seen, there’s no one-size-fits-all – the key is to find the unique process for your team. If you’d like to get recommendations for your team, just get in touch via the live chat or take our free DevOps Assessment. The assessment is a great way to understand your current strengths and weaknesses, and identify next steps.