---
name: salesforce-devops
description: >
  Set up and run Salesforce delivery for Spring '26 — SFDX project layout and `sfdx-project.json`, scratch orgs vs sandboxes and source tracking, the `sf` CLI (`project deploy validate` then `deploy quick`, `retrieve start`, `org create scratch`), branching and release flow, DevOps Center vs Gearset/Copado vs a GitHub Actions pipeline you own, unlocked and 2GP package promotion, quality gates with Code Analyzer v5 and `code-analyzer.yml`, Apex and Flow test levels, deploying flows as active, destructive changes, environment variables and secrets, Sandbox Preview testing and rollback. Use this whenever the user sets up or debugs a Salesforce pipeline, writes a deploy or validate command, hits a failed deployment, a coverage gate, a `package.xml` or `.forceignore` problem, asks how to promote a change from a sandbox to production, how to delete metadata safely, or how to test against the next release. Also apply it when a "deployment problem" turns out to be a project-structure or environment-strategy problem.
metadata:
  technology: Salesforce
  type: devops
---

# Salesforce DevOps
> **Targets:** Salesforce Spring '26 (API 66.0), `sf` CLI · **Verified:** 2026-09 against https://developer.salesforce.com (Salesforce DX Developer Guide, CLI command reference) and the `salesforcecli/plugin-deploy-retrieve` and `forcedotcom/code-analyzer` repositories

Git is the source of truth; an org is a deployment target that people can edit behind your back. Everything in a Salesforce pipeline follows from that: source-tracked development, a validation that is reused rather than repeated, gates that fail fast and cheap, and a release that can be described in one command.

## 1. Decide first

| Question | Default | Change when |
|---|---|---|
| Development environment | **Scratch org** from `config/project-scratch-def.json`, recreated per feature | The org's metadata cannot be recreated (legacy tangle, huge managed-package estate) → developer sandbox with source tracking |
| Source of truth | Git, with source tracking pushing to the dev environment | Never the org. Metadata changed by an admin is retrieved into a branch and reviewed like code |
| Unit of deployment | **Metadata from the repo** through `sf project deploy` | Reusable modules shared across teams or orgs → unlocked / 2GP packages (`salesforce-frontend-architecture` → `packaging-and-sharing.md`) |
| Production deploy | `sf project deploy validate` in CI, then **`sf project deploy quick --job-id`** in the window | A small, low-risk change on a quiet org can deploy directly with `--test-level RunLocalTests` |
| Test level | `RunLocalTests` on staging and production | `RunSpecifiedTests` for a hotfix with a named list; `RunRelevantTests` (Beta) to shorten long validations; `NoTestRun` only in scratch/dev sandboxes |
| Tooling | A pipeline you own (`sf` CLI in GitHub Actions/Azure Pipelines) | Admin-led org with little git literacy → **DevOps Center**; large regulated estate wanting delta deploys, backup and audit out of the box → Gearset/Copado, and the gates below still apply |
| Static analysis | **Code Analyzer v5** (`sf code-analyzer run`) with a committed `code-analyzer.yml` | Never the retired `sf scanner run` / standalone PMD plugin |
| Branching | Trunk-based with short-lived feature branches; release branch cut per release | Scheduled release trains with long stabilisation → `main` + `release/*`, hotfix branched from the release tag |

## 2. Project layout and source tracking

```
sfdx-project.json        packageDirectories, sourceApiVersion (bump every release)
config/                  project-scratch-def.json, dev-scratch-def.json
force-app/               source-format metadata
manifest/                package.xml, destructiveChangesPre.xml, destructiveChangesPost.xml
scripts/                 apex/ (data seeding), shell/
data/                    sf data import tree plans for test data
.forceignore             profiles, settings and anything the org owns
```

- `sf project deploy preview` before every deploy shows what would change and, with source tracking, what conflicts with the org.
- `.forceignore` is a design decision, not a dumping ground: usually most profiles (use permission sets), org-specific settings, and package-installed metadata. Everything ignored is metadata nobody reviews — so do not blanket-ignore the four admin profiles, whose FLS the org requires on every new field.
- Retrieve admin changes deliberately: `sf project retrieve start --metadata Flow:Order_After_Update` into a branch, review the diff, merge. "Who changed this in production" is answered by git only if this is a habit.
- `sourceApiVersion` and `apiVersion` in metadata files drift; bump them on the release cadence, not per file.

## 3. Environments

| Environment | Refresh cadence | Use |
|---|---|---|
| Scratch org | Created and deleted per feature (`--duration-days 1–30`) | Development and CI; the only place you prove the repo builds from nothing |
| Developer / Developer Pro sandbox | Daily | Dev work that cannot use scratch orgs; Dev Pro holds more data |
| Partial Copy sandbox | Every 5 days | Integration and UAT with a sampled data set |
| Full sandbox | Every 29 days | Performance and load testing, migration rehearsals, the final release rehearsal |
| **Sandbox Preview** | During each preview window | A sandbox on the *next* release: run the whole suite there before the org is upgraded, three times a year |

Scratch org creation belongs in CI: `sf org create scratch --definition-file config/project-scratch-def.json --alias ci --duration-days 1 --wait 20`, deleted in an `always()` step. Leaked scratch orgs exhaust the daily allocation and block everyone.

## 4. Validate once, deploy fast

```bash
# in CI, on the pull request or the release branch
sf project deploy validate --target-org prod --source-dir force-app \
  --test-level RunLocalTests --coverage-formatters cobertura --junit \
  --results-dir test-results --wait 90        # prints the job id; valid for 10 days

# in the release window, no tests re-run
sf project deploy quick --target-org prod --job-id 0Af0x000017yLUFCA2 --wait 30
```

A validation is valid for 10 days, so the release window costs minutes instead of an hour of re-running tests. Other commands worth knowing by heart: `sf project deploy report --job-id`, `sf project deploy resume --job-id`, `sf project deploy cancel --job-id`, and `--dry-run` on `deploy start` for a local check. Full workflow, delta deploys and artifacts in `references/pipeline-workflow.md`.

## 5. Quality gates

Run cheapest first, and fail the build on each:

1. `npm run lint` (ESLint `@salesforce/eslint-config-lwc`) and Prettier with the Apex plugin.
2. `npm run test:unit` — Jest for LWC (`salesforce-frontend-testing`).
3. `sf code-analyzer run --workspace force-app --severity-threshold 3 --output-file code-analyzer.csv` — PMD, ESLint, RetireJS, Regex, CPD, the Flow Scanner and the Graph Engine in one result set, configured by a committed `code-analyzer.yml` (`references/code-analyzer-config.md`).
4. `sf project deploy validate --test-level RunLocalTests` against the target org, with JUnit and coverage output published to the build.
5. `sf flow run test --test-level RunLocalTests` for Flow Tests (the CI user needs **View All Data**), or `sf logic run test` for Apex and Flow together.

Coverage: the org's bar is **90 %+** across Apex; the platform's 75 % is only the deployment floor, so a build that merely clears 75 % still fails review. Flow coverage matters separately when deploying flows as active (§6).

## 6. The awkward metadata

- **Flows**: with *Deploy processes and flows as active* on, a deployed flow version becomes active and the org enforces flow test coverage from Apex tests; with it off, every flow lands inactive. Decide once, per org, and write it down. Details in `flow-testing-and-deployment.md` in `../../automation/salesforce-automation/references/`.
- **Deletions**: `--pre-destructive-changes` / `--post-destructive-changes` with a manifest, in the same deploy as the change that stops using the component. `--purge-on-delete` skips the recycle bin on sandboxes. Never delete by hand in production and "fix the repo later".
- **Profiles**: deploy permission sets and permission set groups for end-user access; keep profiles otherwise minimal, because a profile in a deploy silently carries every unrelated setting the source org had. The exception is deliberate: the four admin profiles (System Administrator, System Specialist, System Specialist Lite, System Integration) carry FLS for every new field, so they need a deployment path — deploy them with an explicit field-permissions-only manifest rather than the whole profile.
- **Secrets and per-environment values**: Custom Metadata for configuration, Named/External Credentials or `Credential__c` records for secrets (seeded per environment, never deployed), and environment differences expressed as metadata the pipeline selects — never a `sed` on a deploy artefact.
- **Managed packages**: install them into scratch orgs from the definition file or a script step; a build that needs a hand-installed package is not reproducible.

## 7. Deliverable format

```
## Environments — what each org is for, refresh cadence, who owns it
## Branching & release — branch model, cut points, hotfix path
## Pipeline — jobs in order with the exact sf commands and the gate each one enforces
## Deploy — validate command, quick-deploy command, rollback plan
## Gates — Code Analyzer config, test levels, coverage expectations
## Risks — destructive changes, active flows, profiles, package installs, Sandbox Preview date
```

## Checklist

- [ ] Every change reaches production from git; admin changes are retrieved and reviewed
- [ ] `sfdx-project.json`, `.forceignore` and `sourceApiVersion` deliberate and current
- [ ] Feature environments are disposable; CI creates and deletes its own scratch org
- [ ] Pipeline order is lint → Jest → Code Analyzer → validate → flow tests
- [ ] Production goes through `deploy validate` + `deploy quick`, not a fresh deploy
- [ ] Test level stated per environment; `NoTestRun` never targets production
- [ ] Destructive changes are in a manifest and in the same deploy as the code change
- [ ] Flow activation policy decided; flow coverage understood before an active deploy
- [ ] Rollback plan exists: previous package version or a reverse deploy, rehearsed once
- [ ] The suite runs against a **Sandbox Preview** org before each Salesforce release

## Anti-patterns

- **Change sets** as the release mechanism → source-tracked deploys from git.
- **`sf scanner run` / a standalone PMD plugin** → Code Analyzer v5 with `code-analyzer.yml`.
- **Deploying straight to production with full tests in the window** → validate in CI, quick-deploy in the window.
- **`--test-level NoTestRun` to "get it out"** → fix the tests; `NoTestRun` is for dev environments only.
- **`--ignore-conflicts` as a habit** → it overwrites somebody's org change; look at the conflict.
- **Whole profiles deployed between orgs** → permission sets and permission set groups; the four admin profiles travel as field permissions only.
- **Deleting metadata by hand in production** → destructive changes manifest in the deploy.
- **Long-lived feature branches against a shared sandbox** → short branches, disposable orgs.
- **No Sandbox Preview run** → three times a year the release breaks something nobody tested.

## Go deeper

- `references/pipeline-workflow.md` — a complete CI/CD workflow with validate, quick deploy, delta deploys, artifacts and rollback.
- `references/environments-and-release.md` — scratch org definitions, sandbox strategy and refresh planning, Sandbox Preview, the release checklist and rollback drill.
- `references/code-analyzer-config.md` — `code-analyzer.yml` structure, engines and rule selectors, severity gates, suppressions with an expiry habit.
- Sibling skills: `salesforce-frontend-architecture` for packaging decisions, `apex-testing` and `salesforce-frontend-testing` for the test suites, `salesforce-automation` for flow deployment, `salesforce-security` for the security gates, `salesforce-code-review` for what a reviewer checks, `salesforce-org-conventions` for the org's naming and mandated patterns.
