Salesforce Development
Hands-onIntermediate 40 min· Developer · 14 of 17

Salesforce DX, sandboxes and deployment

Learn how professional Salesforce teams work: source in Git, a Salesforce DX project, scratch orgs, sandboxes, change sets versus CLI deployment, and the test coverage rule for production. You create a project, deploy Apex and run tests from the command line.

What you will build: a Salesforce DX project on your computer, a scratch org created from it, an Apex class deployed there with tests, and a plan for moving changes through sandboxes to production.

Prerequisites: a Developer Edition org, Visual Studio Code with the Salesforce Extension Pack, Git, and Node.js (needed by the CLI installer on some systems). Install the Salesforce CLI from the official download page, then check it with sf --version. You should know basic Apex and tests from earlier lessons.

The problem DevOps solves

In the early lessons you clicked in Setup and wrote code in the Developer Console. That works for learning. In a company, twenty people change the same org. Someone breaks a Flow, nobody knows who, and there is no way to go back. DevOps for Salesforce means three habits:

1. Source of truth in Git, not in an org. The code and configuration (called metadata) live in a repository with history. 2. Separate environments for building, testing and production. 3. Automated deployment and tests, so releases are repeatable and boring.

Environments: orgs for different jobs

Org types

Org typeWhat it isTypical use
ProductionThe live org with real users and dataOnly tested changes arrive here
SandboxA copy of production's setup, in several sizes (Developer, Developer Pro, Partial Copy, Full) that differ in storage and how much data is copiedBuild, test with realistic data, train users
Scratch orgA temporary, empty org made from a definition file, which lasts from 1 to 30 daysBuild and test one feature from source code; create and throw away
Developer EditionA free standalone org you ownLearning, experiments, and a Dev Hub for scratch orgs

Sandboxes are created from a production org, so you will not have one in a free Developer Edition org. You can still create scratch orgs for free if you turn on Dev Hub in your Developer Edition org. The licence limits (how many sandboxes or scratch orgs, and their storage) depend on the edition, so check Salesforce Help for your org.

Step 1: set up the project

Create the project

  1. 1In Setup, search Quick Find for Dev Hub and turn on Enable Dev Hub. This cannot be switched off later, which is fine for a Developer Edition org.
  2. 2Log in from the terminal and mark this org as your Dev Hub: run the first command in the next code block.
  3. 3Generate a project: the second command creates a folder with the standard layout.
  4. 4Open the folder in VS Code (code .).
Terminal
sf org login web --alias devhub --set-default-dev-hub
sf project generate --name brew-and-bean
cd brew-and-bean
git init

--set-default-dev-hub means later commands know which org creates scratch orgs.

Project layout (some files can differ by CLI version)
brew-and-bean/
  config/project-scratch-def.json   how to build a scratch org
  force-app/main/default/           your metadata: classes, triggers, objects, flows, lwc
  manifest/package.xml              a list of metadata to retrieve or deploy (optional)
  sfdx-project.json                 project settings and package directories
  .forceignore                      files the CLI should skip
  .gitignore

The file config/project-scratch-def.json describes the scratch org. A small one is enough to start.

config/project-scratch-def.json
{
  "orgName": "Brew and Bean dev",
  "edition": "Developer",
  "features": [],
  "settings": {
    "lightningExperienceSettings": {
      "enableS1DesktopEnabled": true
    }
  }
}

edition picks the licence level. features and settings switch on things your app needs, such as extra features or org preferences.

Step 2: create a scratch org and deploy code

Terminal
sf org create scratch --definition-file config/project-scratch-def.json --alias dev --set-default --duration-days 7
sf org open --target-org dev

The first command takes a minute or two. The second opens the new org in your browser. The org is empty: no data, no setup you made elsewhere.

Now add some code. The CLI can generate a class for you, which is a good habit because it also creates the required -meta.xml file.

Terminal
sf apex generate class --name GreetingService --output-dir force-app/main/default/classes

This creates GreetingService.cls and GreetingService.cls-meta.xml.

force-app/main/default/classes/GreetingService.cls
public with sharing class GreetingService {
    public static String greet(String name) {
        if (String.isBlank(name)) {
            return 'Welcome to Brew & Bean!';
        }
        return 'Welcome to Brew & Bean, ' + name + '!';
    }
}

Create a test class the same way, because production deployments need tests (see below).

Terminal
sf apex generate class --name GreetingServiceTest --output-dir force-app/main/default/classes
force-app/main/default/classes/GreetingServiceTest.cls
@IsTest
private class GreetingServiceTest {
    @IsTest
    static void greetsByName() {
        System.assertEquals('Welcome to Brew & Bean, Asha!', GreetingService.greet('Asha'));
    }

    @IsTest
    static void greetsWithoutName() {
        System.assertEquals('Welcome to Brew & Bean!', GreetingService.greet(' '));
    }
}
Terminal
sf project deploy start --target-org dev
sf apex run test --target-org dev --test-level RunLocalTests --result-format human --wait 10

The first command pushes your local metadata to the scratch org. The second runs all tests in the org (except those from installed packages) and waits up to 10 minutes.

The end of the output should look like this
=== Test Summary
Outcome              Passed
Tests Ran            2
Pass Rate            100%
Fail Rate            0%

Scratch orgs have source tracking: the CLI remembers what changed locally and in the org. That is why sf project deploy start with no extra options sends only your changes. If you change something in the org with clicks (say, you add a custom field in Setup), pull it back into your project with sf project retrieve start. The changed files then appear in force-app and you can commit them. Without source tracking, as in a Developer Edition org or production, you must say what to retrieve, with --source-dir or --metadata.

Terminal
# bring clicks back to code
sf project retrieve start --target-org dev

# save the work
git add .
git commit -m "Add GreetingService with tests"

Keep Git clean

Do not put secrets, access tokens or the .sfdx folder in Git. The generated .gitignore already covers most of this. Check it before your first push to GitHub.

Step 3: the road to production

A common path is: feature branch in Git, then a scratch org or Developer sandbox to build, then a Pull Request, then a shared sandbox for integration testing and user testing (UAT), then production. Each step has a check, and nobody builds directly in production.

Ways to deploy

ToolHow it worksGood forWeak point
Change setsClick in Setup (Outbound Change Sets and Inbound Change Sets) to pick components and send them between connected orgsSmall teams, admins, quick moves from sandbox to productionManual, no version history, easy to forget a dependency
Salesforce CLI deploysf project deploy start from source in Git, with options such as --manifest or --source-dirDevelopers, repeatable and scriptableNeeds command line skills
CI/CD pipelineA server such as GitHub Actions runs the CLI on every Pull Request: validate, run tests, deployTeams; consistent releasesNeeds setup and care with credentials
Salesforce DevOps CenterSalesforce's own tool with a user interface for work items, pipelines and GitTeams that want a guided processCheck the current documentation for features and availability

Before changing production, validate the deployment. A validation runs the whole deployment and tests but saves nothing. Production deployments must run tests and meet the code coverage rule: at least 75% of your Apex lines covered overall, and every trigger needs some coverage. Because of the rule, writing tests is part of being able to release at all.

Terminal
# the manifest lists what to deploy, for example classes and triggers
sf project generate manifest --source-dir force-app --name manifest/package

# validate only: runs tests, saves nothing
sf project deploy start --manifest manifest/package.xml --target-org my-sandbox --dry-run --test-level RunLocalTests

# real deployment
sf project deploy start --manifest manifest/package.xml --target-org my-sandbox --test-level RunLocalTests

my-sandbox is a placeholder alias. Log in to a sandbox with sf org login web --alias my-sandbox --instance-url https://test.salesforce.com.

Dependencies

Always read the deployment errors from the top. A single missing dependency, such as a class that uses a field that has not been deployed yet, can cause many errors. Deploy things the code depends on first or in the same deployment.

A small example of a CI check, written for GitHub Actions. It installs the CLI, logs in with a secret and validates the project on every Pull Request. Real setups store the login as a secret in the repository, never in the file. The details of getting the auth URL are in the Salesforce CLI documentation (look for the auth URL login flow with sf org login sfdx-url).

.github/workflows/validate.yml
name: validate
on: pull_request
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install --global @salesforce/cli
      - run: echo "${{ secrets.SFDX_AUTH_URL }}" > auth.txt
      - run: sf org login sfdx-url --sfdx-url-file auth.txt --alias target
      - run: sf project deploy start --target-org target --dry-run --test-level RunLocalTests

Treat this as a sketch. Secrets, caching, deleting auth.txt and which org is validated need design for a real project.

Good habits

DevOps checklist

  1. Make every change in a lower environment first. Never click-build in production.
  2. Keep metadata in Git and review changes in Pull Requests.
  3. Use small, frequent deployments rather than big releases.
  4. Write tests as you code so that the 75% rule is never a last-minute panic.
  5. Use a dedicated integration user and secrets for pipelines.
  6. Refresh sandboxes when they drift far from production.
  7. Remember data is not metadata: records are not moved by deployments. Use data loading tools or scripts for seed data.
Prompt · Review my deployment plan
I am a Salesforce developer in a team of [number] people. We have production, one Full sandbox and [other orgs]. Today we use [change sets / manual clicks / CLI]. Suggest a simple branching and deployment process with these environments, name the checks at each step, and list 5 things most likely to go wrong when we start. Keep it short and practical.

What to expect: Check that the plan never builds in production, validates before deploying and uses tests as a gate.

Try it: Add a field and pull it back

In your scratch org, use Setup to create a custom field on Account called Preferred Roast (Text, 40). Run sf project retrieve start. Find the new field file under force-app/main/default/objects/Account/fields/. Commit it to Git. Then create a second scratch org and deploy the project to it. Check that the field exists.

Key takeaways

  1. DevOps means source in Git, separate environments and automated, repeatable deployment with tests.
  2. Production holds live data, sandboxes are copies for building and testing, and scratch orgs are temporary orgs built from a definition file. Developer Edition orgs have no sandboxes but can host a Dev Hub.
  3. The Salesforce CLI (sf) creates scratch orgs, deploys and retrieves metadata, and runs tests.
  4. Scratch orgs track source changes: deploy sends your changes, retrieve pulls clicks back into code.
  5. Change sets are simple but manual. CLI and CI/CD give version history and repeatability.
  6. Production deployments need Apex tests with at least 75% coverage. Validate with --dry-run before the real deployment.
  7. Metadata moves through deployment; data does not.

Check yourself

Finishing the quiz marks the lesson as done.

1. Which statement about scratch orgs is correct?

2. You made a field with clicks in your scratch org. How do you bring it into your project?

3. What does a deployment with --dry-run do?

4. What is a weakness of change sets compared with deploying from Git?

5. Why must developers write Apex tests even for small projects?

Saved on this device. Create a free account to keep it on every device.

Practise this with Sam

A real mock interview, out loud, with an honest scorecard. Free to start.

Start free