Why merging is the hardest part of integrating Git with Salesforce

Diving into what it takes to smoothly merge work across Salesforce orgs

Last Update:
February 2, 2023

Table of Contents

This blog post is #2 in our series of Git+Salesforce blog posts. If you haven’t read our previous release, check-out why Salesforce DevOps is more than just hooking up a git repo to a Salesforce org .

Overwriting a colleague’s work is the last thing you want to happen when deploying on Salesforce. Yet the reality so far is that with Change Sets alone and even with most Salesforce DevOps tools, you’re still very much exposed to that risk. While integrating Git with Salesforce is a step in the right direction, it takes more than that to truly avoid such pitfalls. Merging is key, and it’s a tough problem. So let’s dive into concrete examples of what merging can look like in Salesforce environments, and highlight what it really takes to solve this.

Apex code: when it’s obvious there’s a conflict

Git merging comes into action whenever one file is changed from multiple sources (or git branches). We’ll talk Salesforce metadata later on, and for now let’s start with a simple scenario: Apex code, which Charlie and Elliot are separately working on.

The code below is from Trailhead’s dreamhouse app. SlackOpportunityPublisher.cls sends a heads-up about modified opportunities on a Slack channel. On their sandbox, Charlie is adding a feature to that class (adding the new stage of the modified opportunity):

Elliot is also working on that class. They’re editing it directly on UAT to clarify something ambiguous in the Slack notification (it only tells about the first opportunity modified, while there may be more):

Because Elliot has directly changed UAT, Charlie is now lagging behind. They, and their sandbox, are not aware of that modification. Whenever Charlie would deploy their code to UAT, Elliot’s change might get silently overwritten. That is, unless some kind of Merge Conflict Detection would be in place in both Charlie’s and Elliot’s tooling.

Now it’s easy to think of conflicting changes on the same line as being the one obvious thing to catch (spoiler: it’s not that simple, without the notion of a ‘common ancestor’). But before we address that, let’s consider a slightly different scenario.

Merging distinct changes in one same Apex file

Changing a single line is one thing, but day-to-day scenarios would usually involve changes across different files or across multiple lines of the same file. Considering this from a Salesforce deployment perspective:

  • multiple devs each changing their own set of files is not much of a problem. They’ll each instruct Salesforce to deploy their own changes, and nobody would step on other devs’ toes.
  • Multiple devs working on the same file is where things get tricky. From a deployment perspective, questions arise: what’s the common origin? Should I trust a specific change more than another? Which changes should I cherry-pick?

Change Sets fall short on any of those questions. They’d blindly deploy the whole file without any such consideration. We can illustrate this by going back to SlackOpportunityPublisher.cls:

  • On their sandbox, Elliot adds adds a message in the Slack notification, to explicit how many more (if any) opportunities were modified:
  • Then Charlie adds extra context to those Slack notifications, to make it clear which application is suddenly reaching out:

In a traditional Salesforce deployment, the sequence of events would go like:

  • Elliot deploys to UAT, their change is live
  • Charlie deploys to UAT (no conflict detection is in place), their entire file gets ‘promoted’, hereby erasing Elliot’s change

This is why merging is so essential to teams working together. And why we believe detecting merge conflicts is a must for any DevOps tooling to strive in the Salesforce ecosystem. But now think of it from a tooling perspective:

  • After Elliot has deployed, how does the tool know Charlie doesn’t actually intend to undo Elliot’s change?
  • More generally, for any difference between Charlie’s sandbox and the UAT org, what are the changes that Charlie explicitly intended, and are changes coming from other sources?

Not only does answering these questions require precise Git versioning (see our previous post on the topic), it also means diligently tracking the relationship between each orgs. In git terminology this is known as tracking the ‘common ancestor’ (think of branches forking and re-uniting):

  • With no history and relationship tracking, the tool blindly considers Charlie’s final deployment as the authoritative source to be deployed => Elliot’s work gets overwritten.
  • With merge conflict detection, the tool can notice that Charlie’s work is based on an older snapshot than what is now live on UAT (after Elliot changed it) => the tools give a chance to Charlie and their team to adjust and make things right ✌️

As an example, here is what Blue Canvas invites the user to do in such a case:

There are actually multiple bits of information in that screenshot:

  • A merge is required, i.e. Blue Canvas detected that Charlie’s work lacks some recent deployments to UAT.
  • The merge can actually be done with just the click of a button. Blue Canvas can figure out that there’s no inline conflict and that all code changes in that file can be brought together. 

Specifics aside, what’s important here is that developers gain power and control 💪 Clear awareness that the end result is not going to look exactly like what they have worked on (because the changes occurred on the destination org too), clear control into the next steps (instead of blind deployments and risks of erasing other work). And it’s not just about Apex code, Salesforce admins deserve that same power and control when they work on Salesforce Metadata. Let’s see an example of that.

Merging Salesforce Metadata

With Salesforce’s low-code approach, it might seem harder to grasp what a merge conflict can look like in real life. But actually the same concept applies, and suffice to look at the underlying data representation.

Code below is from the Nebula Logger app (a generic logger for Salesforce), which exposes a field called ‘Environment Type’ (Picklist Field). In their sandbox, Charlie adds a new possible value (‘UAT’) to that field. Under the hood, the change actually live in an XML file:

On their end, Elliot is also working on adding another Environment Type , called ‘Pre-production’:

With the underlying data structure revealed, we can now explicitly see how this is going to lead to a merge conflict as well. Which new picklist value is the right one? Or should they both  be added? Should they be the same (with one agreed-upon name)? All open questions that the team has to figure out. And for that, it at least needs to be aware of that underlying conflict.

Ultimately the reasoning is now the same as for Apex examples above: diligent source tracking and awareness of common ancestors is what it takes to merge Salesforce sandboxes into one common org. Now you’d probably love to have that fully automated, but let’s think about that twice actually.

Salesforce & Git merging: why full automation is a bait

Looking back at the examples, it’s tempting to hope for magical solutions that would automatically detect and resolve any potential harm. At Blue Canvas we know that there is no such silver bullet. Think of the key takeaways from each of the previous examples:

  • Conflict detection is already a big win! It provides huge value in that it makes the developer and their team aware of an existing risk (e.g. of losing valuable work, or shipping unintended changes). Without that, everybody’s blind.
  • Conflict resolution depends on so many other factors: what are the changes based on? Are these changes related in some way? Is it actually safe to group them? Tooling can assist in answering some of these questions, but the ownership of what code goes live in production has to stay in the hands of a developer and their team. Business logic is at stake!
  • This gets even more true with low-code solutions, where it’s actually not explicit to the end-user how changes (e.g. a field update) are tracked under the hood, and what a merge would ultimately look like.

More fundamentally, if just like us you are a firm believer in Git, you can simply refer to git-merge’s documentation itself:

After seeing a conflict, you can do two things. [1] Decide not to merge. [2] Resolve the conflicts. Git will mark the conflicts in the working tree. Edit the files into shape and git add them to the index

This means the ball is in your court to solve any conflict(s). Git is there to detect them and eventually handle any obvious scenario (e.g. changes in different files). But anytime there’s no de-facto solution, the developer’s in charge.

How we’re trying to help at Blue Canvas

Our team’s mission is to offer awesome git-fueled Salesforce Deployers, and we are very much aware that this means going after the merge conflict problem. Our community of users and customers have also clearly shown their strong interest in getting help with this. This is why we are writing this series of Salesforce+Git blog posts, so we can all first share a common definition of the problem we are solving. And at the same time, our engineers are baking awesome features to help with this!

As illustrated by some of the screenshots in this post, we are currently rolling out our first version of Merge Conflict Detection. It’s available in public beta already and is gradually being rolled out to all existing accounts. We believe this functionality will be another game changer and we are eager to receive more feedback. And check-in for our next blog post, with a comprehensive walkthrough of the feature.

More like this