FAQ - Developer-Controlled Packages (DCPs) - Spring ‘18
Q. What are the benefits of DCPs over change sets and ANT Migration Tool?
Q. What are the different types of packages and which one should I use?
Q. How do I build and deploy a new app using DCPs? What’s my Hello World App for DCPs?
Q. How can I organize my unpackaged metadata using DCP?
Q. What are some of the common packaging operations?
Q. How can I specify attributes in the sfdx-project.json file for managing my DCPs?
Q. What types of metadata are supported in a DCP?
Q. What is a namespace and should I use one with my DCP?
Q. How do I iterate on my DCP? What happens when I add, edit and delete DCP metadata?
Q. What are the implications of modifying a DCP’s metadata in installed orgs?
Q. How and where can I install my DCP?
Q. How can I find out packages installed / deployed in my org?
Q. How can I use Salesforce CLI to perform package-related operations?
Q. Can my packages depend on one another?
Q. How can I specify dependencies among DCPs?
Q. Are package dependencies enforced during install time?
Q. Can you give me an example of an sfdx-project.json file with many interdependent packages?
Q. How can I delete my package and package versions?
Q. How can I remove metadata from my package?
Q. How do I work with Version Numbers?
Q. What is the difference between beta package versions and released package versions?
Q. What are Locked Packages? Can I use them?
Q. If I am an ISV, what type of packaging should I use?
Q. Can I downgrade my package?
Q. What is the relationship between version control system and packages?
Q. How can I get more info about Salesforce DX and DCPs?
As part of Salesforce DX, in Spring ‘18, we are launching Developer-Controlled Packages (DCPs) as an open beta. We’re bringing you, our enterprise customers, a new type of packaging option - DCPs - designed with you and your use cases in mind.
DCPs offer a new mechanism to deploy your metadata to Salesforce orgs. We believe DCPs provide distinct advantages over current technologies like change sets and the ANT Migration Tool (See here for the benefits of DCPs over current technologies).
Among other things, DCPs try to answer these two key questions:
Before we move ahead, it helps to clarify what we mean by unpackaged metadata.
Broadly speaking, the metadata in your production org can be categorized into three buckets:
It also helps to clarify what we mean by package and package version.
A Package is a container for Salesforce metadata. You can add metadata to a package and take a snapshot of it any time. This snapshot is called a package version. You can create package versions any number of times. Each package version, once created, is immutable. You can install a package version in any Salesforce environment - scratch orgs, sandbox orgs, or production orgs. Installing a package version deploys the metadata that was specified when the package version was created.
(Back to the table of contents)
The following are some key benefits of DCPs:
(Back to the table of contents)
You may have heard of many package-related terms - managed packages, Package 2, DCPs, unmanaged packages, second-generation packages. Let’s demystify these now. See here if you are wondering what a package really is.
First-Generation Packaging
Before Salesforce DX came along, we had two types of packages:
Second-Generation Packaging (aka Packaging 2 or Salesforce DX Packaging)
With Salesforce DX, we are launching Packaging 2 or Second-Generation Packages. With Salesforce DX, we want packaging to work as well for customers as they do for partners.
Developer-Controlled Packages (DCPs) are designed for customers to organize metadata into packages and facilitate testing, deployment, and upgrades. There are two flavors of DCPs - locked and unlocked. For now, use the unlocked flavor of DCPs as they are open beta and can be installed anywhere. Locked DCPs will stay in Pilot mode for a while and can only be installed in sandbox orgs and scratch orgs.
Managed Second-generation packages (Managed 2GPs) are geared more towards Salesforce partners who want to develop and list apps on AppExchange. While Managed 2GPs are beta, there are certain key parity features (Push Upgrades, LMA, ability to list on AppExchange, patch versions, etc.) that are missing in them. So, if you are a Salesforce Partner, your best bet is to continue with First-Generation Managed Packages until we build those feature parity items in managed 2GPs. See here for more info if you are an ISV.
To summarize in one sentence, if you are a Salesforce customer, contractor, consultant or Systems Integrator, the only type of packaging that matters to you is Unlocked DCPs, which are available to you, NOW!
(Back to the table of contents)
In less than 5 minutes, you can build and test your Hello World App to get a feel for DCPs. The only precondition is that you have a Salesforce DX environment set up with Salesforce CLI and that you have enabled Dev Hub and Packaging 2 in your Dev Hub Org. If you haven’t done it already, refer to this documentation.
Follow these simple steps and you have for yourself a DCP!
git clone https://github.com/dreamhouseapp/dreamhouse-sfdx.git
cd dreamhouse-sfdx/
git checkout spring18
(If you want more info about the DreamHouse app, see here)
sfdx force:auth:web:login --setdefaultdevhubusername
sfdx force:package2:create --containeroptions Unlocked --nonamespace --name Dreamhouse-App
"id": "0HoB0000000009OKAQ",
"versionName": "Spring - 2018",
"versionNumber": "1.0.0.NEXT" (see here for more info about version numbers)
sfdx force:package2:version:create --directory force-app --wait 10
In this example, force-app is the directory in which your package metadata is located in Salesforce DX format. The other pieces of information needed for package version creation are stored in sfdx-project.json.
sfdx force:org:create --noancestors --nonamespace --definitionfile config/project-scratch-def.json --setalias sub1 --setdefaultusername
sfdx force:package:install --id <04t_id_from_step_5> --wait 10
sfdx force:org:open
Congratulations! You have now created and installed your first DCP!
(Back to the table of contents)
Step 1 - Extract a small set of unpackaged, self-contained metadata from your production org and convert it to Salesforce DX format using mdapi:retrieve and mdapi:convert commands.
How to identify this set of self-contained metadata is a bit of a science and a bit of an art. You can try and select metadata that represents an app, or customization to an AppExchange app, or customizations to Sales Cloud or an Account object, or some set of metadata that represents a functional unit (e.g.: a library of apex classes). We know this is a challenging task. We plan to produce material to help in this. See this blog series that talks about DX and DCPs.
Step 2 - Push this metadata to a scratch org using source:push and validate that this is the metadata that you want to be part of a new DCP. If you are missing some metadata, go back to Step 1.
Step 3 - Create a DCP using this metadata (see here for how to create a DCP). Make sure this DCP has no namespace (supply the --nonamespace flag to the force:package2:create command). See here for more info about namespaces and packages.
Step 4 - Test the DCP in CI and UAT environments. If you encounter issues, you may have to go back to Step 1 and validate that you have the right set of metadata. Once the DCP has passed all CI runs and UAT on sandboxes, install the DCP in the production org. This installation does NOT deploy new metadata as what’s contained in the DCP is already present in the production org. But what this does is “migrate” the metadata from the unpackaged set to the DCP so that it is now part of the DCP in the production org. From this point forward, you can iterate over this metadata set using the DCP.
These steps, along with the specification of dependencies among DCPs, can help in organizing all of the unpackaged metadata into a set of interdependent DCPs.
(Back to the table of contents)
Package Creation
A package is a container of metadata. When you create a package, such as a DCP, you specify some characteristic info about the package: name, description, namespace associated with it, package type (unlocked for now), and so on. Associating metadata with a package occurs at the package version creation step.
Package Version Creation
As your development team is working on some functionality, you, as a release manager, may want to take periodic snapshots of the functionality when it is in a functional state. Such snapshots are package versions. You can create any number of package versions. Each version, once created, is immutable.
Package Install
A package install is an operation whereby the metadata of a package is deployed in the target org. A package install is actually a package version install - it is the deployment of metadata contained in a specific package version of a package. A package is typically associated with 100s or even 1,000s of package versions. When you install a package, you specify which one of the many versions you intend to install. Install and deploy mean the same in this context.
Package Upgrade
Installing a version on top of a previously installed package version is a package upgrade. When you perform a package upgrade, the metadata of the new package version is deployed to the org. The package upgrade process can add new metadata to the org, modify existing metadata, and may even delete some metadata depending on what’s there in the new version of the package.
Package Downgrade
See here for more info about package downgrades.
Package Uninstall
This is an operation where you are deleting the metadata and associated data related to an installed package.
See here for how you can perform these operations using the Salesforce CLI.
(Back to the table of contents)
See this example below for a section of sfdx-project.json file
(this deliberately avoids package dependencies to keep things simple. See here for a simple example of package dependencies and here for a more complex scenario)
"packageDirectories": [
{
"path": "revenue-app",
"default": true,
"id": "0HoB00000004CFpKAM",
"versionName": "version 1.0",
"versionDescription": "Spring 2018 Release",
"versionNumber": "1.0.0.NEXT",
"features": "MultiCurrency,PersonAccounts",
"orgPreferences": {
"enabled": [...],
"disabled": [...]
}
},
{
"path": "logic",
…
}
]
path (required field) - root folder where the source metadata of the package is stored in the local workspace in Salesforce DX format
id (required field) - this is the package id that starts with 0Ho. The output of the force:package2:create command provides this ID.
versionName (required field) and versionDescription (optional field) - name and description of the package version that you’re creating.
versionNumber (required field) - version number in major.minor.patch.build format where each one of these is a whole number. See here for more info about package versions.
features and orgPreferences - these are features and org preferences required for the metadata in the DCP. See here for more info.
(Back to the table of contents)
Most metadata that is supported by Salesforce DX is supported in a DCP. See here for more information.
(Back to the table of contents)
A namespace is a unique identifier that is owned by your company when you register it with Salesforce. If you associate a namespace (like Acme) with a DCP (see here for how to associate namespaces with Dev Hub), the API names of all metadata added to the DCP will have the prefix Acme__. Hence, namespaces offer a mechanism to name and organize metadata in your org.
Keep the following in mind when you think of namespaces and DCPs:
(Back to the table of contents)
DCPs provide a very open framework for making metadata changes. Before we talk about these changes, make sure you understand the difference between a package and a package version. See here for more info. Also take a look at this for packaging operations.
Versioning is one of the core differentiating features of packaging. It is versioning that enables iterative development, where you can continually update an artifact (DCP) in a controlled and manageable way in response to business needs (feature requests and bug fixes).
When it comes to versioning and changes introduced in versions, it is helpful to look at them from two angles - development stage and deployment stage.
Development and Build Stages
When you create a new package version of a DCP, you can introduce the following changes in relation to a previous package version:
The above-described changes can be applied in a variety of ways (Setup UI in scratch orgs, in VS Code, in Developer Console, etc.) and ultimately make it to the version control system for the project (See here for relationship between version control system and DCPs). The release manager then pulls in the right set of metadata to the local workspace and creates a new package version so that the package version captures these changes.
Deployment Stage
Let’s presume you are trying to deploy (aka install) v 2.0 of a DCP in an environment where v 1.0 is currently installed. During this package upgrade process, the following changes are applied:
(Back to the table of contents)
You have developed an unlocked DCP, taken it through all of the testing cycles - integration, user acceptance, etc. - and you are now ready to install it in your production org. One question that arises is, what happens if your admin has to make an emergency change to the metadata directly in the production org? For example, the admin might have noticed that some permission sets are granting too much access, a page layout is missing some critical fields, or a report is missing a key column. If this metadata is part of a DCP, should the admin contact the development team and take it through the full cycle of development, testing, UAT and then do the deployment? The answer is yes from a best practice of software development process perspective. But what if one or all of these changes need to be made right now!?
With unlocked DCPs, the admin has the flexibility to make all of these changes directly in production. The fact that these components belong to a DCP won’t restrict the flexibility that the admin has towards making changes. However, the admin has to ensure that the development team is informed of these changes. Once the development team is aware of these changes and incorporate them in the next version of the DCP, installing future versions of the DCP won’t undo the changes made by the admin in the production org. However, if the changes made directly in production are not applied to the metadata of the DCP by the development team, subsequent package installs or upgrades will overwrite the changes made in production.
(Back to the table of contents)
The Dev Hub in which the DCPs were developed is the owner of the DCP. The association between the dev hub and a DCP occurs when you execute the force:package2:create command. The Dev Hub org against which this command is run is the owner of the DCP.
Please keep in mind the following about Dev Hub and DCP:
(Back to the table of contents)
An unlocked DCP can be installed in any Salesforce environment - scratch orgs, sandbox orgs, trial orgs, and production orgs (see here for a note on beta vs released state of a package version).
A DCP can be installed in two different ways:
Salesforce CLI command
force:package:install --id <04t id> --targetusername <org where the DCP should be installed>
The Salesforce UI
By appending the following to the browser URL: packaging/installPackage.apexp?p0=<04t id>
The 04t is the package version ID. This represents the version of the DCP to install. The 04t ID is returned when the force:package2:version:create Salesforce CLI command returns successfully. You can use force:package2:version:list command to get a list of package versions associated with a DCP.
See here if you want to know more about different packaging operations.
(Back to the table of contents)
In any given org, you can obtain information about installed packages in one of the following ways.
Salesforce CLI
force:package:installed:list command
Salesforce UI
See here for how to install a DCP in an org.
(Back to the table of contents)
See here if you want to first understand what these operations are intended for.
Development-side Commands
Create a DCP - force:package2:create
Add, modify and remove metadata from a DCP - Update the workspace files
Get a listing of all DCPs - force:package2:list
Update info about a DCP - force:package2:update
Create a DCP version - force:package2:version:create
Obtain info about the status of a DCP version create request - force:package2:version:create:get
Obtain info about the status of all DCP version create requests - force:package2:version:create:list
Get a listing of all DCP versions - force:package2:version:list
Get detailed info about a single DCP version - force:package2:version:get
Update info about a DCP version - force:package2:version:update
Install-side Commands
Install a DCP - force:package:install
Status of an install request - force:package:install:get
Info about all installed packages in an org - force:package:installed:list
Uninstall a DCP - force:package:uninstall
Status of an uninstall request - force:package:uninstall:get
(Back to the table of contents)
Yes, one of the value propositions of DCPs is that you can develop and maintain a set of interdependent packages. By supporting dependencies, DCPs promote modular development with a dependency framework.
The following are some of the main use cases around dependencies:
Also, keep the following in mind when you think of package dependencies:
See here to see how you can express dependencies in sfdx-project.json and see here for a more advanced example.
(Back to the table of contents)
See here to know more about package dependencies.
You express dependencies within the packageDirectories section of sfdx-project.json.
The following are the different types of supported dependencies
A DCP depending on another package in the same dev hub
"dependencies": [
{
"packageId": "0HoB0000000009EKAQ",
"versionNumber": "2.5.0.LATEST"
}
]
This expresses that the current DCP depends on another package of version number 2.5 with package id 0HoB0000000009EKAQ. This package can be a managed second-generation package or another DCP. By using the keyword LATEST, you can iterate on the base and dependent package versions simultaneously.
A DCP depending on another package developed in a different dev hub
There are two main use cases for this:
"dependencies": [
{
"subscriberPackageVersionId" : "04tB0000000J6iZ"
}
]
This expresses that the current DCP depends on another package with package version id 04tB0000000J6iZ. This is the way to specify dependencies if the package that the current DCP depends on, was developed in a different dev hub.
For a more complex scenario of package dependencies, please see here.
(Back to the table of contents)
(Back to the table of contents)
See here to know more about package dependencies. See here for a simple example of package dependencies.
Let’s consider the following scenario:
packageDirectories section of sfdx-project.json file for Travel Booker DCP
(working on ver 3.2 of the Travel Booker DCP that depends on ver 4.5 of Travel Anywhere! AppExchange package installed in production org. The package version id of ver 4.5 of the AppExchange App is 04tB0000000IB1EIAW)
{
"path": "travel-booker",
"id": "0HoB00000004CFuKAM",
"versionName": "v 3.2",
"versionDescription": "Summer 2018 Release",
"versionNumber": "3.2.0.NEXT",
"dependencies": [
{
"subscriberPackageVersionId" : "04tB0000000IB1EIAW"
}
]
}
packageDirectories section of sfdx-project.json file for Expense Manager DCP
(working on ver 4.7 of the Expense Manager DCP that depends on ver 2.6 of Utils DCP whose package2 id is 0HoB00000004CFpKAL)
{
"path": "expense-manager",
"id": "0HoB00000004CFuKAN",
"versionName": "v 4.7",
"versionDescription": "Summer 2018 Release",
"versionNumber": "4.7.0.NEXT",
"dependencies": [
{
"packageId": "0HoB00000004CFpKAL",
"versionNumber": "2.6.0.LATEST"
}
]
}
packageDirectories section of sfdx-project.json file for Reimburse Me DCP
(working on ver 3.8 of the Reimburse Me DCP that depends on ver 3.2 of Travel Booker DCP and ver 4.7 of Expense Manager DCP)
Note: you have to declare all of the transitive dependencies in the order in which these DCPs should be installed from a dependency stand-point.
{
"path": "reimburse-me",
"id": "0HoB00000004CFuKAP",
"versionName": "v 3.8",
"versionDescription": "Summer 2018 Release",
"versionNumber": "3.8.0.NEXT",
"dependencies": [
{
"subscriberPackageVersionId" : "04tB0000000IB1EIAW"
},
{
"packageId": "0HoB00000004CFuKAM",
"versionNumber": "3.2.0.LATEST"
},
{
"packageId": "0HoB00000004CFpKAL",
"versionNumber": "2.6.0.LATEST"
},
{
"packageId": "0HoB00000004CFuKAN",
"versionNumber": "4.7.0.LATEST"
}
]
}
sfdx-project.json file for All of these 4 DCPs
If the 4 DCPs (Reimburse Me, Expense Manager, Travel Manager and Utils) are developed in a single SFDX project workspace, the sfdx-project.json will be a merge of the above snippets.
{
"namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "42.0",
"packageDirectories": [
{
"path": "reimburse-me",
"id": "0HoB00000004CFuKAP",
...
},
{
"path": "expense-manager",
"id": "0HoB00000004CFuKAN",
...
},
{
"path": "travel-booker",
"id": "0HoB00000004CFuKAM",
...
},
{
"path": "utils",
"id": "0HoB00000004CFpKAL",
...
},
]
}
(Back to the table of contents)
It is currently not possible to delete package or package versions. In a future release, we will provide capabilities for the same. For now, you can rename them (using force:package2:update and force:package2:version:update Salesforce CLI commands) to denote that you are no longer using them.
Click here for more info about packaging operations.
(Back to the table of contents)
If you want to delete some metadata from your DCP because you no longer need that metadata, then you can delete that metadata from your local workspace, create a new package version and install that version in the production org. That process will delete the metadata in the production org. There are some limitations with respect to what gets deleted. Please see here for more details.
However, remove is different from delete. It refers to removing some metadata from a DCP because that metadata does not naturally belong to the DCP. You may want to make that metadata unpackaged and add it to another DCP. This remove capability does not exist in Spring ‘18. Safe Harbor, we plan to provide this capability in Summer ‘18.
(Back to the table of contents)
A package version is an immutable artifact that is a snapshot of a package with a specific set of metadata. A package version is created with a successful execution of the force:package2:version:create command. Every package version (see here for more info about package and package versions) you create has a unique version number. The version number is in the major.minor.patch.build number format where major, minor, patch and build are integers.
Here are some things to keep in mind with respect to version numbers:
(Back to the table of contents)
When you create a package version using force:package2:version:create, we set the state of the package version to beta. Beta package versions are internal test candidates that are used in test cycles including CI. To prevent a beta package version from being inadvertently installed in production orgs, we prevent the installation of beta package versions in any org that is not a scratch org, sandbox org or Developer Edition org.
As part of your development process, you end up creating lots and lots of package versions that are all of beta state. When a package version passes all tests including UAT (User Acceptance Testing), you can promote that package version to be a release candidate by issuing the following CLI command:
force:package2:version:update --package2versionid <05i package version id> --setasreleased
A package version promoted to released state can be installed in any org.
For a given major.minor.patch package version number (see here for more info about version numbers), there can be at most one package version in released state. Once you set a package version to released state, you cannot undo that operation.
(Back to the table of contents)
There are two types of Developer Controlled Packages (DCPs):
The key aspect that is common between these two types of DCPs is that the changes made by the development team wins. i.e. despite any changes made in the installed org, when a package upgrade is done, the contents of the package version overwrite what’s in the org.
The main difference between Locked and Unlocked DCPs is when it comes to modifications that can be made in installed orgs. With Locked DCPs, most changes are locked down so that the metadata deployed as part of a locked DCP is read-only. In the case of unlocked DCPs, the metadata deployed as part of a DCP is editable and deletable.
Locked DCPs work best with organizations where there is a desire to prevent any changes to be made directly in production orgs. Organizations adopting locked DCPs are willing to go through the process (make changes to metadata, build a new DCP version, test and deploy to production) for any changes, including emergency changes to metadata.
Unlocked DCPs work best with organizations where there is a desire to support changes to be made directly in production orgs for emergency use cases - E.g.: a permission set has incorrect set of permissions, a workflow rule is broken, a Flow needs to be deactivated immediately, a report is missing a key column that should be added asap. Organizations adopting unlocked DCPs are willing to set up a process where admins communicate unplanned changes applied directly in production orgs with the development team so that subsequent versions of the DCP incorporate the changes made in production.
Unlocked DCPs are open beta in Spring ‘18 meaning they can be installed in any org. For the next few releases, locked DCPs stay in pilot mode - they can only be installed in scratch org or sandbox orgs.
We recommend that you use unlocked DCPs and wait for us to launch locked DCPs in GA mode.
(Back to the table of contents)
If you are an ISV that develops and lists apps on AppExchange, here are our recommendations (See here for info about different types of packages):
(Back to the table of contents)
In the context of packaging, a downgrade is installing a lower version of a package on top of a higher version. For E.g.: this is when you try to install ver 2.0 of your DCP in an org where ver 4.0 has been installed. When you downgrade a package, the following occurs:
You should consider any downgrade of a package carefully. Package downgrades can fail due to dependency scenarios. For E.g.: if ver 4.0 has an apex class that is referenced by another apex class in a different DCP, downgrading to ver 2.0 would fail with a user-friendly error message if that apex class is not present in ver 2.0 of the DCP.
(Back to the table of contents)
When it comes to Developer Controlled Packages (DCPs), the source of truth is your version control system (vcs). Having said that, DCPs or packages in general don’t explicitly link to a vcs. The step where you link the metadata that you want in a package to your DCP is in the force:package2:version:create step and that requires the directory name of your SFDX project, not a vcs repo. Here are some best practices you can follow to ensure your vcs tracks changes made to your DCPs:
(Back to the table of contents)
We have lots of additional information that goes into the details of how to work with Salesforce DX and DCPs. Following are some helpful resources:
Last but not the least, please join our Trailblazer Communities where so many developers like you are collaborating with fellow developers in their Salesforce DX journey.
(Back to the table of contents)