Different types of validation
sfp provides validation techniques to verify changes in your Salesforce projects before merging. The validate command supports two primary modes to suit different validation needs.
Validation Modes
| Mode | Description | Flag |
|---|---|---|
| Thorough (Default) | Include package dependencies, code coverage, all test classes during full package deployments. This is the recommended mode for comprehensive validation. | --mode=thorough |
| Individual | Ignore packages installed in scratch org, identify list of changed packages from PR/Merge Request, and validate each of the changed packages (respecting any dependencies) using thorough validation rules. | --mode=individual |
Release Config Filtering
Both validation modes scope packages to a release configuration file through the --releaseconfig flag, which is required. Only packages defined in the release config that have changes are validated. This is useful for:
- Large monorepos with multiple domains
- Focusing validation on specific package groups
- Reducing validation time by limiting scope
# Validate with release config filtering
sfp validate org --targetorg myorg --mode thorough --releaseconfig config/release.ymlWhat gets deployed — component checksum skip
Validation runs component checksum skip by default. For a changed source or diff package, sfp fetches the artifact previously installed on the org and deploys only the components whose source bytes differ, instead of the package's full component set.
The point is fidelity rather than speed alone: a release with the skip enabled deploys the same reduced payload, so validating with it means validating what you will actually ship.
Turn it off for a run with --no-checksum-skip, for one package with the disableChecksumSkip descriptor, or per project, environment or target branch from codev.
One interaction is worth knowing when a review org is reused across pull-request iterations. With artifact update on — the default — sfp records the locally built validation version on the org after the first validation. That version is never published, so the next iteration cannot fetch it and the package falls back to a full deploy. Setting disableArtifactUpdate keeps the org pinned to the published baseline, so every iteration deploys only what changed. The Component checksum skip page covers this and the rest of the mechanism in full.
Validating the merge result (--ref and --baseRef)
--ref names the branch or commit under validation and --baseRef the branch it targets. --ref depends on --baseRef, so the two are always passed together.
They do more than select a comparison point. sfp checks --ref out into a separate git worktree, unshallowing the clone if needed, then merges --baseRef into that worktree. Validation therefore runs against the merge result — what the branch will look like once merged — rather than against the branch as it stands.
Two consequences follow:
- Every file sfp reads afterwards comes from the worktree:
sfdx-project.json, release configs, and any validation scripts. A path in a validation script resolves relative to the worktree, not your original checkout. - A merge conflict between the two refs fails the command. This is the same conflict you would hit when merging the pull request, surfaced earlier.
sfp validate org -o ci -v devhub \
--releaseconfig config/release-sales.yml \
--ref feature-branch --baseRef mainSequence of Activities
The following steps are orchestrated by the validate command:
Initial Setup
When using pools:
- Fetch a scratch org from the provided pools in a sequential manner
- Authenticate to the scratch org using the auth URL fetched from the Scratch Org Info Object
When using a provided org:
- Authenticate to the provided target org
Package Processing
-
Identify packages to validate:
- Build packages that are changed by comparing the tags in your repo against the packages installed in the target
- If
--releaseconfigis provided, filter packages based on the release configuration
-
For each package to validate:
Thorough Mode (Default):
- Deploy all the built packages as source packages / data packages (unlocked packages are installed as source packages)
- Trigger Apex Tests if there are any apex tests in the package
- Validate test coverage of the package depending on the type:
- Source packages: Each class needs to have 75% or more coverage
- Unlocked packages: Package as a whole needs to have 75% or more coverage
Individual Mode:
- Ignore packages that are installed in the scratch org (eliminates the requirement of using a pooled org)
- Compute changed packages by observing the diff of Pull/Merge Request
- Validate each of the changed packages individually
- Install any dependencies required for each package
- Apply thorough validation rules (deployment, testing, coverage)
Additional Options
Test Execution
Package tests during validation run serially — sfp sets the org's parallel Apex testing switch off before running them, so --disableparalleltesting has no additional effect on this path. Tests that fail on row locks are retried.
--skipTesting: Skip test execution entirely (use with caution). This flag is currently honoured bysfp validate orgonly--skipCoverage: Run the tests but skip coverage validation for every package. This is the global equivalent of the per-packageskipCoverageValidationdescriptor--continueOnTestFailure: Carry on validating the remaining packages after one package's tests fail. The failed packages are folded back into the result at the end, so the run still fails — this changes how much you learn from a run, not whether it passes
Coverage Requirements
- Default coverage threshold: 75%
- Configure custom threshold:
--coveragepercent <value>. Values below 75 are raised back to 75 - Coverage is validated per class for source packages and per package for unlocked packages
Combined Deploy + Test Mode (--deploywithtests)
By default, validate runs deploy and apex tests as two separate phases — the package metadata is deployed first, then a follow-up RunSpecifiedTests job triggers the package's apex tests and a third query reads the coverage. The --deploywithtests flag collapses this into a single Metadata API deploy that embeds the package's apex tests using RunSpecifiedTests with rollbackOnError=false. Coverage is then read directly from the deploy response.
# Combined deploy + test mode against a target org
sfp validate org -o ci -v devhub --deploywithtestsWhen combined mode is automatically disengaged
For correctness, sfp falls back to the classic deploy → test flow for the packages below. The artifact header logs the reason (Deploy With Tests: skipped — <reason>) so you can see what ran for each package.
| Package situation | Reason for fallback |
|---|---|
| Sync-only package (not impacted by the branch under validation) | Sync-only packages are deployed without running tests in the classic flow; combined mode would force RunSpecifiedTests to fire for unrelated packages. |
| Pure unlocked (Unlocked AND not org-dependent) | The Metadata API deploy response only carries coverage entries for classes touched by tests. The classic ApexCodeCoverageAggregate query is needed to get the full denominator across all package classes. sfp probes the DevHub once for Package2.IsOrgDependent to decide; if the hub is unavailable or the query fails, the package is conservatively treated as pure unlocked. |
| Diff package with no impacted test classes | Mirrors the classic getTestOptionsForDiffPackage behaviour — running every org-local test for an unimpacted change set is wasteful, so the package is skipped with a warning. |
Non-optimized source packages with --deploywithtests on | Falls back to RunLocalTests (rather than RunSpecifiedTests) inside the same combined deploy, mirroring classic non-optimized validate semantics. |
Other guardrails
- Per-package
skipTestingandskipCoverageValidationdescriptors are honored --coveragepercentis clamped to ≥ 75 (matches the classic flow)- Org-dependent unlocked, source, and diff packages (with impacted tests) keep combined mode enabled
When to use it: Turn on --deploywithtests when you want faster validate cycles and your pipeline is heavy on source/diff packages or org-dependent unlocked packages. Pure unlocked packages will continue to use the classic flow under the hood — no change in behaviour for them.
Best Practice: Use "thorough" mode for comprehensive validation before merging to ensure all packages are properly tested and deployable. For faster feedback during development, consider using "individual" mode or filtering with release configs.
Skipping tests with --skipTesting bypasses critical quality checks. Only use this option in development environments or when you're certain the changes don't require test validation.