---
title: "The Salesforce CLI commands you actually use"
url: "https://trailassembly.com/salesforce-cli-commands/"
topic: "DevOps and Release"
published: "2026-09-11T08:41:19+00:00"
updated: "2026-10-06T13:52:51+00:00"
---

# The Salesforce CLI commands you actually use

**In one sentence:** there are about twelve commands you will use daily and several hundred you will not. This is the working set, in the `sf` syntax that replaced `sfdx`, with the flags that matter and the two or three places the behaviour surprises people.

## Getting connected

```apex
sf org login web --alias prod --instance-url https://login.salesforce.com
sf org login web --alias uat  --instance-url https://test.salesforce.com
sf org list
sf config set target-org=uat
```

Aliases are the point. Once an org has an alias you stop typing usernames, and `--target-org` takes the alias everywhere. Setting a default with `sf config set` is convenient and is also how people deploy to the wrong org, so on anything touching production, pass `--target-org` explicitly and accept the extra typing.

## Getting metadata in and out

```apex
sf project retrieve start --metadata ApexClass:AccountService
sf project retrieve start --metadata "Flow,PermissionSet"
sf project deploy start --source-dir force-app --dry-run
sf project deploy start --source-dir force-app --test-level RunLocalTests
```

Two things to know. `--dry-run` validates without deploying and is free, so there is no reason to skip it. And the metadata type names are case-sensitive and specific (`ApexClass`, not `apexclass` or `Apex Class`) with the full list available from `sf org list metadata-types`.

For a scratch org or a sandbox with source tracking enabled, `sf project deploy start` with no arguments deploys only what changed, and `sf project retrieve start` with no arguments pulls what changed in the org. That is the loop you will actually spend your day in.

## Running things in the org

```apex
sf apex run --file scripts/fix-orphans.apex
sf apex run test --tests AccountServiceTest --result-format human --wait 10
sf apex get log --number 5
sf apex tail log --color
```

`sf apex tail log` is the one people discover late. It streams debug logs to your terminal as they happen, which beats refreshing the Developer Console by a wide margin. It needs an active trace flag, which the command sets up for you when you agree to the prompt.

## Querying and moving data

```apex
sf data query --query "SELECT Id, Name FROM Account LIMIT 5"
sf data query --query "SELECT Id FROM Contact WHERE Email = null" --result-format csv > orphans.csv
sf data import tree --plan data/sample-plan.json
sf data delete record --sobject Account --record-id 001...
```

`--result-format csv` plus a redirect is how you get a file without opening a browser. For bulk work, `sf data import bulk` and `sf data export bulk` handle the job creation and polling, which removes the main reason people opened [Data Loader](https://trailassembly.com/data-loader-vs-bulk-api/).

## Scratch orgs

```apex
sf org create scratch --definition-file config/project-scratch-def.json \
  --alias dev1 --duration-days 7 --set-default
sf org open --target-org dev1
sf org open --target-org dev1 --url-only
sf org delete scratch --target-org dev1
```

`--url-only` prints a front-door URL instead of opening a browser, which is what you want when a script or a headless browser needs the session rather than you.

## The flags worth memorising

- `--json` on any command, for parsing the output with `jq` in a script. The human format is not stable and should never be parsed.

- `--dry-run` on deploy, always.

- `--wait N` on anything asynchronous, or the command returns before the work finishes and your script continues into a race.

- `--test-level` on deploy: `NoTestRun`, `RunSpecifiedTests`, `RunLocalTests`, `RunAllTestsInOrg`. Production deployments require tests regardless of what you pass.

## Where it surprises people

**Deploy order.** The CLI does not sort your metadata for you in every case, which is one reason the [deployment option you chose](https://trailassembly.com/salesforce-deployment-options/) shows up as a pipeline problem rather than a tooling one. A permission set referencing a field that is in the same deployment usually works. One referencing a field that is not deployed yet does not.

**Source tracking is not universal.** Scratch orgs and sandboxes created with tracking enabled have it. A production org does not, so `deploy start` with no arguments there does nothing useful and you must name what you are deploying.

**The old `sfdx` commands still run** and print a deprecation note. They are aliases, and mixing the two styles in a script is how a pipeline breaks when the aliases are eventually removed.

## Frequently asked

**How do I see what changed before deploying?** `sf project deploy preview` lists what would go, based on source tracking.

**Why does my deployment say “no changes to deploy”?** Source tracking thinks the org already matches. `sf project reset tracking` clears that state, and then everything is a change again.

**Can I deploy a single file?** Yes: `--source-dir` takes a path to one file. The CLI adds the associated metadata for you.

---

Source: https://trailassembly.com/salesforce-cli-commands/
Trail Assembly — independent Salesforce write-ups.
