Skip to main content

SF-9227 · Scenario · Medium

Deploy custom code from sandbox to production — how?

✓ Verified by Vikas Singhal · Last reviewed 5/19/2026 · Updated for Spring '26

In 2026 there are three deployment paths from sandbox to production. The interview answer needs to name all three, recommend the modern one, and explain when the legacy options are still right. Naming only Change Sets in 2026 dates the candidate badly.

The 60-second answer

Modern path: source-tracked sandbox → Git → CI pipeline (GitHub Actions / GitLab / Copado / Gearset) running sf project deploy start --target-org production --test-level RunLocalTests. Production deployments require 75% Apex test coverage across the org, no triggers below 1%, all tests passing. Before deploying, validate with --dry-run against production. Pre-2024 alternative is Change Sets between connected orgs — still in use for admin-heavy orgs with no Git workflow. For metadata not in source format, the Metadata API (sf project deploy start -d manifest/) is the universal fallback.

The three paths

PathWhen it’s rightLimits
Salesforce CLI + Git + CI (recommended)Any org with developers, multi-environment promotion, version controlRequires sfdx-project.json, Git workflow discipline
Change Sets (legacy)Admin-only orgs, urgent one-off fixes between two connected sandboxes/prodNo version control, no rollback, deploys must be done by a person clicking
Metadata API directHeadless deploys, packaged distributions, large one-off metadata movesNeeds package.xml; lower-level than DX

For a developer interview, lead with the CLI/Git path. Mention Change Sets as the alternative for admin-heavy work.

Modern path — Salesforce DX + CI

Step 1 — Source-tracked sandbox + Git

Every Apex class, LWC, flow, and custom object lives in version-controlled source under force-app/main/default/. The sandbox is source-tracked (Developer or Developer Pro), so the CLI knows what changed without you remembering.

# Pull what changed in the sandbox into source format
sf project retrieve start --target-org my-sandbox

# Stage and commit
git add force-app/
git commit -m "feat: add scenario-based opportunity validation"
git push origin feature/scenario-validation

Step 2 — Pull request and CI validation

A PR triggers CI, which runs a validation-only deploy against production (no commit):

sf project deploy start \
  --target-org production \
  --dry-run \
  --test-level RunLocalTests \
  --wait 60

Validation does everything a real deploy does except commit. If coverage drops below 75% or any test fails, the build is red and the PR can’t merge.

Step 3 — Actual deploy on merge

After merge, the same command without --dry-run:

sf project deploy start \
  --target-org production \
  --test-level RunLocalTests \
  --wait 120

RunLocalTests runs every Apex test in the org except those from managed packages. For a small change you can scope:

sf project deploy start \
  --target-org production \
  --test-level RunSpecifiedTests \
  --tests OpportunityValidationTest \
  --tests OpportunityValidationTest2 \
  --wait 120

The narrower test scope is faster but requires you to know which tests exercise the changed code. Most pipelines default to RunLocalTests for safety.

Step 4 — Quick deploys

If you validated within the last 4 days and want to ship the validated package without re-running tests:

sf project deploy quick --job-id 0Af3z00000abcXYZ --target-org production

This is the Friday-afternoon hotfix path — validate Monday, ship later in the week without burning another 30-minute test run.

Production deployment requirements

RuleDetail
Test coverage75% of all Apex code in the org, post-deploy
Per-trigger coverageAt least 1% on every trigger (the trigger must be touched by a test)
All tests must passA single failure blocks the entire deploy
Validation expires in 4 daysQuick Deploy must happen within 4 days of validation
Change Set deploysLock the receiving org during deploy; concurrent admin changes are blocked

The 75% number is org-wide post-deploy — not per-class. You can have a 0%-coverage class as long as the rest of the org is high enough to clear 75%. (Don’t actually do this — it’s just clarifying the math.)

Legacy path — Change Sets

Still common in admin-heavy orgs. The flow:

  1. Source org (Sandbox) → Outbound Change Sets → Add components.
  2. Upload to target org (Production) → appears in Inbound Change Sets.
  3. Validate Inbound Change Set → click Deploy.

Pros: no CLI, no Git, no engineer. Cons: no version control, no rollback, no PR review, no CI testing, easy to miss a dependency, every deploy is a manual click.

In 2026, Change Sets are mostly used for admin-only metadata (flows, page layouts, permissions) when there’s no DevOps practice. Mention you’d migrate them to DX if you joined the team.

Pre-deploy checklist

Before pushing the deploy button:

  • Validate first (--dry-run). Surface coverage gaps and test failures without consuming a deploy slot.
  • Backup production metadatasf project retrieve start --target-org production --manifest package.xml lets you re-deploy the previous state if you need to roll back.
  • Run tests in the sandbox matching production scope, including managed-package tests if relevant.
  • Check for destructive changes — deleting a custom field or object requires destructiveChanges.xml. Treat carefully; deleted fields can take data with them.
  • Schedule a deployment window if it’s a major release; users get errors during the deploy.
  • Have a rollback plan — either the previous-state metadata package or a documented manual undo for each change.

Anti-patterns

  • Deploying with NoTestRun — production won’t allow it for code. Even where allowed, you’ve removed your safety net.
  • Pushing directly from a developer’s laptop without PR review or CI — bypasses every quality gate.
  • Skipping validation--dry-run exists for a reason. Use it.
  • Destructive changes in the same package as the new code — split into two deploys; if the new code fails, you haven’t already deleted the old field.
  • Forgetting the org-wide 75% threshold — adding one untested 500-line class can drop the whole org below 75% and block the deploy.
  • Deploying flows from inactive state and re-activating manually — automate flow activation in your CI; manual activation is the #1 cause of “I deployed and nothing changed” tickets.

How to answer in 30 seconds

“DX path: source-track the sandbox, commit to Git, CI validates with sf project deploy start --dry-run --test-level RunLocalTests, then deploys the same way on merge. Production requires 75% org-wide Apex coverage, all tests passing, every trigger touched. Change Sets are the legacy admin-friendly fallback. Quick Deploy lets you ship a previously validated package without re-running tests.”

How to answer in 2 minutes

Walk the three paths (DX, Change Sets, Metadata API direct). Then walk the DX flow: source-tracked sandbox → Git → PR → CI validation with --dry-run and --test-level RunLocalTests → merge → deploy. Mention the production-specific rules (75% coverage, every trigger ≥1%, all tests pass, validations expire in 4 days). End with the pre-deploy checklist — validate, backup, schedule, plan rollback.

Likely follow-up questions

  • What’s the difference between RunLocalTests and RunAllTestsInOrg?
  • How do you handle destructive changes in a deployment?
  • What’s the difference between a Developer and Developer Pro sandbox?
  • How do you deploy a profile without overwriting target-org permissions?
  • What’s the Quick Deploy and when do you use it?

Verified against: Salesforce CLI Reference — sf project deploy, Deploying Apex to Production, Change Sets Overview. Last reviewed 2026-05-19.