Merge conflicts and code clobbering are one of the biggest challenges for Salesforce teams. Even if you’re using Git, you’re probably not properly catching merge conflicts.
Merge conflicts and code clobbering are one of the biggest challenges for Salesforce teams. Whether you’re using change sets, Ant scripts, Salesforce DX, or a commercial tool, there just aren’t many good ways to deal with “code clobbering”. Even if you’re using Git, you’re probably not properly catching merge conflicts (for reasons that we’ll explain in detail below).
One of the biggest promises of Salesforce DX is that using source driven development will help Salesforce teams work through merge conflicts and implement merging the way most software teams do.
Code clobbering is a simple, but devastatingly common occurrence for many Salesforce teams.
Let’s say Joe is working on an Apex class: classOne.cls. The class has 100 methods. Joe is only working on methodB(). Susie is also working on classOne.cls. But she is working on methodC() and methodD(). Code clobbering is when Joe pushes changes to classOne.cls that wipe out or “clobber” Susie’s changes. This happens a lot in Salesforce on all types of metadata: Apex classes, Objects, Fields, RecordTypes, etc.
To illustrate a bit further, classOne.cls on Joe’s sandbox does not include Susie’s addition of methodC() and methodD() because he hasn’t pulled her changes into his branch or org:
classOne.cls sitting in the Integration environment includes Susie’s changes:
Code clobbering is what would happen if Joe pushed his changes to Integration via a change set or Ant script. Susie’s changes would be lost and there would cascading effects.
A merge conflict is what should happen in the above scenario if Joe and Susie were using Git in the traditional manner. Rather than letting Joe blindly overwrite Susie’s changes, Git would notify Joe that his changes were in conflict and suggest how he might be able to fix the conflict gracefully. Importantly, he’d be blocked from pushing his changes in a way that would overwrite Susie’s hard work. This prevents bugs and the need to rollback.
Unfortunately, many Salesforce admins and developers do not get this type of merge conflict warning because change sets and Ant scripts provide no merging mechanism - they blindly overwrite the target with whatever is in the source org. Git should help, but it’s notoriously difficult to use Git with Salesforce. And even if you are, you’ll find that merge conflicts are an extra special pain.
Even the most sophisticated Salesforce development shops struggle with merge conflicts, even when they are using Git.
Here’s an example from a sales call this week (anonymized of course):
But this setup isn’t catching merge conflicts and “code clobbering” in the way that they expected.
First, the team had to drop a lot of metadata from their Git repo because they couldn’t get declarative metadata to play nicely with Git. This left big holes in what Git could even warn them about when it did catch conflicts.
Second, though Acme’s developers understood the concept of branching, the admin and BA team found it confusing. Each time they created a branch, they would interrupt a developer to ask them for help.
Third (and biggest of all), the sandbox style of development so common to Salesforce teams doesn’t work well with Git flow. XML is notoriously difficult for Git to handle and all declarative metadata in Salesforce is XML.
Furthermore, cherrypicking is difficult in Git. When an admin or developer does successfully get changes into a branch, there are often many other changes that show up in the diff (environments are often wildly out of sync). This leads to false positives and a long list of potential conflicts and a very difficult pull request to merge. An admin or developer has little ability to know what is okay to deploy and what isn’t.
(Note: these same conditions can occur if you’re using Salesforce DX instead of Ant scripts).
At Blue Canvas, we solved the first two issues by tightly coupling orgs to branches in Git. We automatically commit changes to Git branches so that all metadata (including declarative) lives in the repo in real time. Now we had no more gaps in metadata, especially for declarative changes. We created a simple, Lightning Design System based GUI for creating pull requests and cherry picking files that allowed admins and BAs to avoid having to get too bogged down in branching strategies and worked with the traditional sandbox-based development strategy that most teams used.
But, we ran into a new problem: establishing a clear baseline for when to detect the merge conflict.
In most Salesforce orgs, there is seldom a common history between Git branches. Sandboxes are refreshed at various intervals creating massive differences between orgs. These massive differences led to two problems:
Seeing that this wasn’t going to work we went down a more experimental path, leveraging Darcs (a fairly academic source control tool that was initially a potential improvement over SVN alongside Git before Git emerged as the preferred choice of most developers). But Darcs was still very much an academic project and wasn’t performant enough for large Salesforce implementations.
The answer to this challenge ended up being simple: allow admins and developers to cherry pick specific lines based on the diff.
Returning to our example with Joe and Susie, now Joe could safely select just the lines of methodB() while ignoring the other lines of classOne.cls - notably the deletion of methodC() and methodD(). Now Joe’s update can move upstream without jeopardizing Susie’s work.
Extending the cherry picking solution was the simplest, most performant and most effective solution for avoiding code clobbering and merge conflicts in the first place. We found it intuitively made sense to Salesforce developers.
We established an Integration environment (sandbox) and branch where every developer is responsible for merging their changes before they can be promoted to upstream environments. Each developer is expected to carefully select their specific changes and put them into the Integration branch. Release managers or other team members should always feel comfortable pushing from Integration upstream to UAT, staging, QA or production branches.
In summary, what we learned were that the following pieces needed to be in place in order to stop code clobbering and handle merge conflicts in a way that intuitively worked for Salesforce admins and developers.
Setting up a system like this with Git and Salesforce allows me to safely deploy without causing massive cascading effects of clobbering the changes of my other colleagues. And having that safety allows me to move faster without breaking things, which is the point of CI/CD after all.
Part 3 of our 4 part series on data optimizations for large Salesforce orgs.