Andrew Hanna

Andrew Hanna

How to Put Salesforce Flows Under Version Control Without Merge Pain

How to Put Salesforce Flows Under Version Control Without Merge Pain

Short answer: Commit each Flow as one unversioned .flow-meta.xml file, give every Flow a single owner per change, and tell Git never to auto-merge Flow XML. When two branches do touch the same Flow, keep one side and rebuild the other change in Flow Builder instead of hand-editing XML. Most Flow merge pain comes from parallel edits and layout noise, not from Git.

Why are Salesforce Flows hard to version control?

A Flow is not code you wrote line by line. It is XML generated by Flow Builder, and it carries a few traps:

  • One file holds everything. Elements, connectors, variables and formulas all live in a single file, and a large Flow can run past 1,000 lines of XML.
  • Layout is stored as data. Every element carries locationX and locationY coordinates, so dragging a box produces a diff with no logic change. Switching a Flow between free-form and auto-layout rewrites the positions of every element.
  • Only the latest version comes back. Since Metadata API v44, Flow file names no longer carry version numbers and a retrieve returns the latest version, active or draft. A half-finished draft can land in your repo without anyone noticing.
  • Version numbers drift between orgs. Deploying a Flow creates a new version in the target org, so version 7 in your sandbox might be version 3 in production.
  • Versions are capped. Salesforce allows 50 versions per Flow, so org history is no substitute for Git history.

How do you set up a Flow repository that stays clean?

  1. Use source format. One file per Flow under force-app/main/default/flows/, named after the Flow's API name with no version suffix.
  2. Stop committing FlowDefinition. Since the v44 change, a status field on the Flow itself controls whether it is active, and Salesforce no longer recommends Flow Definitions for activation.
  3. Retrieve by name, from the org where the work happened. For example sf project retrieve start --metadata Flow:Case_Escalation. Check the status value before you commit, so a draft never ships by accident.
  4. Tell Git not to auto-merge Flows. Add force-app/**/flows/*.flow-meta.xml -merge to .gitattributes. Per the gitattributes docs, an unset merge keeps your branch's version and declares a conflict. Avoid the binary shortcut here, because it also switches off diffs, and you still want to review Flow changes.

That last step matters most. A line-based merge can produce Flow XML that is perfectly valid and logically wrong, for example a connector pointing at an element the other branch deleted. A forced conflict is cheaper than a silent one. It also explains why our guide to a Salesforce-aware Git merge driver keeps Flows out of the driver's patterns: profiles and permission sets merge well by node, Flow logic does not.

How do you stop two people editing the same Flow?

  • One owner per Flow per change. Claim the Flow on the work item before anyone opens Flow Builder.
  • Split big Flows into subflows. Parallel work then lands in different files instead of one shared file.
  • Keep branches short. Merge the base branch in often so conflicts surface while they are small.
  • Pick one canvas mode per Flow. Switching between free-form and auto-layout churns every coordinate in the file.

What should you do when two branches change the same Flow?

  1. Do not hand-merge the XML. Treat the conflict as a decision about logic, not text.
  2. Keep the side with the bigger change using git checkout --ours or --theirs on the Flow file.
  3. Deploy that version to a sandbox and reapply the smaller change in Flow Builder.
  4. Check the result with version comparison. Flow Builder gained a version comparison tool in Winter '26, and Summer '26 added a visual mode that colour-codes created, updated and deleted elements on the canvas.
  5. Retrieve, commit and let CI validate the merged Flow before review.

For conflicts across other metadata types, see how to resolve Salesforce metadata conflicts during a merge.

How do you deploy Flows from Git safely?

  • Expect inactive by default. Flows active in a sandbox deploy to production as inactive unless you turn on Deploy processes and flows as active in Process Automation Settings, which comes with an Apex test coverage requirement and covers processes and autolaunched Flows, not screen Flows.
  • Make activation an explicit release step, not something an admin remembers afterwards.
  • Run a check-only deployment on every pull request, so a Flow that references a missing field fails in CI, not in the release window.

Still shipping Flows with change sets? Start with how to graduate from change sets to source control, then apply the steps above.

Where does tooling help?

DevOps platforms such as Gearset and Flosum add Flow-aware comparison and deployment on top of Git, and Salesforce's Lifecycle Center (formerly DevOps Center) offers AI recommendations for conflict resolution through its MCP tools. Serpent resolves conflicts on the pull request itself: Serpent AI proposes a resolution and waits for your review before applying it, and AI code review runs on every plan. See Serpent plans, or browse more Salesforce DevOps guides.

FAQ

Should I commit FlowDefinition files?

No. Since Metadata API v44, the Flow's own status field controls activation, and Salesforce no longer recommends Flow Definitions for that job.

Can Git merge two versions of the same Flow automatically?

It can produce a textually clean merge, but Git does not understand Flow logic. Force a conflict with -merge and reapply one change in Flow Builder.

Why did a draft Flow end up in my repository?

A retrieve returns the latest version of the Flow, active or draft. Check the status value before committing.

Do locationX and locationY changes matter?

They change layout only, not behaviour. Review them as noise, and avoid switching canvas modes so they stay stable.

Related Articles

Curious about faster shipping before you dive in? Let's talk

Commitment free!