Session 85 · Salesforce Deployment

Packaged Deployment in Salesforce — Unlocked Packages, Managed Packages & SFDX

Published 2026-06-30 · 14 min read · realsyllabus.com

Change sets are the most common Salesforce deployment method. They're built into Setup, they're visual, and they work without any CLI tooling. But change sets have a fundamental limitation: they have no version history. Every deployment is a manual push from one org to another, with no record of what was deployed when.

Packages solve this. A Salesforce package is a versioned container for metadata. You build a version, test it, promote it, and install it into any org. The installed org knows exactly which version it's running, and upgrades are a controlled, auditable process.

First-gen vs second-gen packages

Salesforce has two generations of the packaging system.

First-generation packages (1GP) were created through the Setup UI in a dedicated packaging org. They're still in use — many managed packages in AppExchange are 1GP — but new development should not use them. The tooling is limited, and they don't integrate with modern development workflows.

Second-generation packages (2GP) are the current model. They're built entirely with Salesforce CLI, source-driven from your SFDX project, and managed through a Developer Hub org. 2GP integrates with Git, CI pipelines, and scratch orgs. This is the model this session covers.

Package types

Unlocked packages

Unlocked packages are for building org-specific solutions — metadata that belongs to your org rather than a distributable product.

Think of an unlocked package as a change set done correctly: version-controlled, testable, and installable via a single command rather than manually configured each time.

Managed packages

Managed packages are for ISV distribution — building a product that installs in customer orgs, typically sold or distributed through AppExchange.

If you're not distributing to external orgs, use unlocked packages. The namespace and upgrade management requirements of managed packages add significant complexity without benefit in single-org or internal-team scenarios.

Prerequisites: Developer Hub and SFDX project

Before creating a package, you need:

  1. Developer Hub enabled — go to Setup → Dev Hub and enable it in your production or designated Dev Hub org
  2. CLI connected to Dev Hubsf org login web --set-default-dev-hub
  3. SFDX project — created with sf project generate --name MyProject

All package commands reference your Dev Hub. The Dev Hub maintains the registry of packages and versions associated with your organisation.

The five-step package workflow

Step 1: sf package create

sf package create \ --name "My Org Package" \ --package-type Unlocked \ --path force-app \ --target-dev-hub MyDevHub

This adds a package definition to your sfdx-project.json. The --path flag specifies which source directory belongs to this package. Everything in force-app will be included when you create a version.

Step 2: Add your metadata

Your source code, custom objects, flows, and other metadata live in the package directory. Develop in a scratch org connected to this project. All changes are pulled back to source before creating a version.

Step 3: sf package version create

sf package version create \ --package "My Org Package" \ --installation-key-bypass \ --wait 10 \ --target-dev-hub MyDevHub

This is the build step. Salesforce creates a scratch org, deploys your source, runs your full Apex test suite, and — if tests pass — generates a package version ID (04t record) and an install URL.

The --installation-key-bypass flag skips the key requirement. Use this for internal testing; for production versions, set an installation key instead.

The --wait 10 flag waits up to 10 minutes for the async build to complete. For complex packages, increase this value.

Step 4: sf package install

sf package install \ --package 04t... \ --target-org MySandbox \ --wait 10

Installs the package version into the target org. The 04t value is the package version ID returned from step 3. You can also use the subscriber package version URL. The --wait flag handles the async installation job.

Step 5: sf package version promote

sf package version promote \ --package 04t... \ --target-dev-hub MyDevHub

This marks the version as Released, making it installable in production orgs. This is a one-way transition — you cannot demote a promoted version back to beta. Hold versions in beta until QA is complete and sign-off is received.

Package dependencies

If your package metadata depends on components from another package, declare the dependency in sfdx-project.json:

"dependencies": [ { "package": "Salesforce_CMS", "versionNumber": "1.0.0.LATEST" } ]

On install, Salesforce resolves the dependency chain. If the dependency isn't already in the target org, the install fails with a clear error telling you which package to install first.

Package installation keys

Installation keys add a security gate to unlocked packages. Set at version create time:

sf package version create \ --package "My Org Package" \ --installation-key "yourKey123" \ --wait 10

Anyone installing this version must provide the key. This prevents unauthorised installs while still allowing controlled distribution.

When to use each deployment model

ModelBest forAvoid when
Change setsSmall, infrequent changes, single production org, no CLI setupTeam development, frequent releases, multi-org
SFDX source deployTeam development with Git, no versioning requirementsSame metadata needs to travel to many orgs
Unlocked packageMulti-org deployment, version history, CI pipelineAd-hoc, one-off changes
Managed packageAppExchange, external distribution, ISV productInternal org customisation
Packages are not always the right answer. For a single org with infrequent changes and no team development, change sets are faster to set up and sufficient. The overhead of package versioning makes sense when you have multiple orgs, a CI pipeline, or a need for upgrade paths.

Testing strategy

Package versioning has a built-in quality gate: the version create command runs your full Apex test suite. If tests fail, the version doesn't build. This means your test suite is not optional when working with packages.

Recommended approach:

  1. Develop in a scratch org with the package directory as source
  2. Write Apex tests as you go — minimum 75% coverage per class, but aim higher
  3. Create a beta version and install in a dedicated QA scratch org
  4. Run regression testing in the QA org
  5. On sign-off, promote the version to Released
  6. Install the Released version in production

Any issue found after promotion requires a new version. There is no patch mechanism for released versions — plan your release cycle accordingly.

Session 85 — Packaged Deployment

realsyllabus.com