Detect unsafe contexts, queries in loops, hardcoded IDs, and more to optimize Salesforce Flows
| Distribution | Best for | Repository | Install / Use |
|---|---|---|---|
| Salesforce CLI Plugin | Local development, scratch orgs, CI/CD | Flow-Scanner/lightning-flow-scanner | sf plugins install lightning-flow-scanner |
| VS Code Extension | Real-time scanning inside VS Code | Flow-Scanner/lightning-flow-scanner-vsx | code --install-extension ForceConfigControl.lightning-flow-scanner-vsx |
| Salesforce App | Run scans directly inside any Salesforce org | Flow-Scanner/lightning-flow-scanner-app | ![]() |
| GitHub Action | Native PR checks | Flow-Scanner/lightning-flow-scanner-action | GitHub Marketplace |
| Copado Plugin | Copado CI/CD pipelines | Flow-Scanner/lightning-flow-scanner-copado | Copado Marketplace |
| Core Library (Node.js + Browser) | Custom tools, scripts, extensions, web apps | Flow-Scanner/lightning-flow-scanner | npm install @flow-scanner/lightning-flow-scanner-core |
Privacy: Zero user data collected. All processing is client-side. → See our Security Policy.
📌Tip: To link directly to a specific rule, use the full GitHub anchor link format. Example:
https://github.com/Flow-Scanner/lightning-flow-scanner#unsafe-running-context
Want to code a new rule? → See How to Write a Rule
ActionCallsInLoop - To prevent exceeding Apex governor limits, it is advisable to consolidate and bulkify your apex calls, utilizing a single action call containing a collection variable at the end of the loop.
APIVersion - Introducing newer API components may lead to unexpected issues with older versions of Flows, as they might not align with the underlying mechanics. Starting from API version 50.0, the Api Version attribute has been readily available on the Flow Object. To ensure smooth operation and reduce discrepancies between API versions, it is strongly advised to regularly update and maintain them.
AutoLayout - With Canvas Mode set to Auto‑Layout, elements are spaced, connected, and aligned automatically, keeping your Flow neatly organized—saving you time.
CopyAPIName - Maintaining multiple elements with a similar name, like Copy_X_Of_Element, can diminish the overall readability of your Flow. When copying and pasting these elements, remember to update the API name of the newly created copy.
CyclomaticComplexity - The number of loops and decision rules, plus the number of decisions. Use a combination of 1) subflows and 2) breaking flows into multiple concise trigger‑ordered flows to reduce cyclomatic complexity within a single flow, ensuring maintainability and simplicity.
DMLStatementInLoop - To prevent exceeding Apex governor limits, consolidate all your database operations—record creation, updates, or deletions—at the conclusion of the flow.
DuplicateDMLOperation - When a flow executes database changes or actions between two screens, prevent users from navigating backward between screens; otherwise, duplicate database operations may be performed.
FlowName - The readability of a flow is paramount. Establishing a naming convention significantly enhances findability, searchability, and overall consistency. Include at least a domain and a brief description of the flow’s actions, for example Service_OrderFulfillment.
GetRecordAllFields - Following the principle of least privilege (PoLP), avoid using Get Records with “Automatically store all fields” unless necessary.
HardcodedId - Avoid hard‑coding IDs because they are org specific. Instead, pass them into variables at the start of the flow—via merge‑field URL parameters or a Get Records element.
HardcodedUrl - Avoid hard‑coding URLs because they are environment specific. Use an $API formula (preferred) or environment‑specific sources like custom labels, metadata, or settings.
InactiveFlow - Like cleaning out your closet: deleting unused flows is essential. Inactive flows can still cause trouble—such as accidentally deleting records during testing, or being activated as subflows.
MissingFaultPath - A flow may fail to execute an operation as intended. By default, the flow displays an error to the user and emails the creator. Customize this behavior by incorporating a Fault Path.
FlowDescription - Descriptions play a vital role in documentation. We highly recommend including details about where flows are used and their intended purpose.
MissingMetadataDescription – Flags Flow elements (Get Records, Assignments, Decisions, Actions, etc.) and metadata components (Variables, Formulas, Constants, Text Templates) that lack a description. Adding concise descriptions greatly improves readability, maintainability, and helps AI tools understand your automation intent.
MissingNullHandler - When a Get Records operation finds no data, it returns null. Validate data by using a Decision element to check for a non‑null result.
ProcessBuilder - Salesforce is transitioning away from Workflow Rules and Process Builder in favor of Flow. Begin migrating your organization’s automation to Flow.
RecursiveAfterUpdate - After‑update flows are meant for modifying other records. Using them on the same record can cause recursion. Consider before‑save flows for same‑record updates.
SameRecordFieldUpdates - Similar to triggers, before‑save contexts can update the same record via $Record without invoking DML.
SOQLQueryInLoop - To prevent exceeding Apex governor limits, consolidate all SOQL queries at the end of the flow.
TriggerOrder - Guarantee your flow execution order with the Trigger Order property introduced in Spring ’22.
UnconnectedElement - Avoid unconnected elements that are not used by the flow to keep flows efficient and maintainable.
UnsafeRunningContext - This flow is configured to run in System Mode without Sharing, granting all users permission to view and edit all data. This can lead to unsafe data access.
UnusedVariable - To maintain efficiency and manageability, avoid including variables that are never referenced.
It is recommend to configure and define:
- The rules to be executed.
- The severity of violating any specific rule.
- Rule properties such as REGEX expressions.
- Any known exceptions that should be ignored during scanning.
{
"rules": {
// Your rules here
},
"exceptions": {
// Your exceptions here
}
}Most Lightning Flow Scanner distributions automatically resolve configurations from .flow-scanner.yml, .flow-scanner.json, or package.json → flowScanner.
Using the rules section of your configurations, you can specify the list of rules to be run. Furthermore, you can define the severity and configure expressions of rules. Below is a breakdown of the available attributes of rule configuration:
{
"rules": {
"<RuleName>": {
"severity": "<Severity>",
"expression": "<Expression>"
}
}
}When the severity is not provided it will be warning by default. Other available values for severity are error and note. Define the severity per rule as shown below:
{
"rules": {
"FlowDescription": {
"severity": "error"
},
"UnusedVariable": {
"severity": "note"
}
}
}Some rules have additional attributes to configure, such as the expression, that will overwrite default values. These can be configured in the same way as severity as shown in the following example.
{
"rules": {
"APIVersion": {
"severity": "error",
"expression": "===58" // comparison operator
},
"FlowName": {
"severity": "note",
"expression": "[A-Za-z0-9]" // regular expression
}
}
}Specifying exceptions allows you to exclude specific scenarios from rule enforcement. Exceptions can be specified at the flow, rule, or result level to provide fine-grained control. Below is a breakdown of the available attributes of exception configuration:
{
"exceptions": {
"<FlowName>": {
"<RuleName>": [
// Suppress a specific result:
"<ResultName>",
// Suppress ALL results of rule:
"*",
...
]
},
...
}
}Example
{
"exceptions": {
"MyFlow": {
"MissingNullHandler": ["*"],
"HardcodedId": ["Old_Lookup_1"]
}
}
}Control the verbosity of violation reports via detailLevel. By default (enriched), outputs include element or flow-level details like variable data types, node connectors/locations, or attribute expressions for comprehensive reports. Set to simple for lighter output with only line and column numbers.
{
"rules": {
...
},
"exceptions": {
...
},
"detailLevel": "simple"
}New rules are introduced in Beta mode before being added to the default ruleset. To include current Beta rules, enable the optional betamode parameter in your configuration:
{
"rules": {
...
},
"exceptions": {
...
},
"betaMode": true
}
sf plugins install lightning-flow-scannerOR
npm install -g lightning-flow-scannernpm install -g @flow-scanner/lightning-flow-scanner-coreReady-to-use CI/CD templates and a native GitHub Action.
All examples: docs/examples/.
| Platform | Template Type | Link |
|---|---|---|
| Azure DevOps | Full Project Scan | azure-pipelines-flow-FullScan.yml |
| Azure DevOps | Change-Based Scan | azure-pipelines-flow-changedFiles.yml |
| Copado DevOps | Full & Change-Based Scans | CI/CD Plugin |
| GitHub | Full & Change-Based Scans | scan-flows.yml |
Lightning Flow Scanner is plug-and-play by default
Use lightning-flow-scanner in the Salesforce CLI:
sf flow:scan
sf flow:fix -d src/force-app
sf flow:scan --sarif > report.sarifUse lightning-flow-scanner-core as a Node.js/browser dependency:
// Basic
import { parse, scan } from "@flow-scanner/lightning-flow-scanner-core";
parse("flows/*.xml").then(scan);
// Get SARIF output
import { parse, scan, exportSarif } from "@flow-scanner/lightning-flow-scanner-core";
parse("flows/*.xml").then(scan).then(exportSarif);This project optionally uses Volta to manage Node.js and PNPM versions. Install Volta with:
curl https://get.volta.sh | bashVolta will automatically use the Node.js version defined in
package.json.
-
Clone the repository
git clone https://github.com/Flow-Scanner/lightning-flow-scanner.git
-
Install dependencies:
pnpm install
-
Compile:
pnpm run build
To compile just the core package::
pnpm build:core
-
Run tests:
pnpm test
Or to test a new version of the core:
pnpm test:core -
Linking the core module locally(Optional):
To link the module, run:
pnpm link --global @flow-scanner/lightning-flow-scanner-core
You can now do Ad-Hoc Testing with node:
node -i -e "import('@flow-scanner/lightning-flow-scanner-core').then(m => { Object.assign(global, m.default ? m.default : m); console.log('✅ Core loaded! Try: await parse(...), scan(...), etc.'); })"Or test in a dependent project:
npm link @flow-scanner/lightning-flow-scanner-core
-
Deploy Demo Flows (Optional):
cd assets/example-flows && sf project deploy start
Navigate to the Demo Readme for full details
-
Create a standalone UMD Module(Optional):
pnpm dist // creates UMD at`dist/lightning-flow-scanner-core.umd.js`.
Want to help improve Lightning Flow Scanner? See our Contributing Guidelines
