Salesforce Managed Packages: First-Generation Packages and Second-Generation Packages (2GP)

I have over 15+ years of IT experience working in various stages of Software Development Life Cycle of web-based object-oriented enterprise applications. No matter how much experience you have, there is always something new to discover and learn. Recently I have started working with Ribbonfish on their in-house Salesforce app managed using second generation packaging. I learned quite a few things, including working with packages. I would like to present a few things I learned regarding packages.

What is Package?
It is a container or a bundle that holds all the components of a Salesforce app that help your app to run smoothly and can be distributed or installed in other Salesforce orgs. There are two different types of Packages. 1) Unmanaged Packages and 2) Managed Packages. 

Unmanaged packages are like open-source applications when installed developers who installed the package in their Salesforce org can make changes to the components. Upgrades are not possible in Unmanaged packages. 

Managed packages on other hand when installed in a Salesforce org, developers who installed them cannot make any changes to the components. These are usually developed by Salesforce partners and sell them to customers via App Exchange. These packages can be upgradable.

If you want to build a Managed package, there are two ways of creating them. 1) First-Generation Managed package and 2) Second-Generation Managed package (2GP). 

First-Generation Managed packages: 

I never worked on first generation packaging but did some research to understand how it is different to second generation packaging.  If you are using First-Generation managed package, the Developer org containing components for your application will be the source of truth. It is known as classic development model for ISVs. It requires a Developer Edition org as packaging or patch org. They cannot share namespaces across multiple packages.

Second-Generation Managed packages (2GP):
2GP is based on source-driven development model. We use 2GP to integrate with our source control system like Git thus making way to support parallel development by developers. We can execute all operations regarding packaging via Salesforce CLI and can also automate using scripts. There can be more than one package in your project.  

The difference between First-Generation packages and Second-Generation packages are as follows: 

  • Package Source: In First-Generation the developer org where you create your managed package is the source. In 2GP source control is the source. 
  • Package owner: In First-Generation packaging org is the owner whereas in 2GP Dev Hub owns the packages. 
  • Number of Packages: In First-Generation packaging there is only one package per packaging org whereas in 2GP your Dev Hub can have multiple packages. 
  • Namespace: In First-Generation packaging namespace of the managed package is created in packaging org whereas in 2GP namespace of the managed package is created in namespace org and is linked to Dev Hub. Also, in First-Generation packaging a namespace is associated with only one package whereas in 2GP multiple packages can you same namespace. 
  • Operations: In First-Generation packaging operations like create, install, uninstall cannot be automated whereas in 2GP all operations can be automated using Salesforce CLI. 
  • Versioning: In First-Generation packaging package versioning is linear whereas in 2GP it is flexible. 

In this article we will mainly focus on Second-Generation Managed packages.

Salesforce orgs required for 2GP development: 

  • Dev Hub: A salesforce org which is enabled as a dev hub. This lets us to create and manage Scratch orgs, link namespace org and authorise and run force:package commands 
  • Namespace org: Org where the namespace for the packages is created. 
  • Scratch org: All the development and testing happen in scratch orgs. 
  • Target org: Org where the package is installed. 

Workflow for 2GP: 

Salesforce DX has been used for managing packages. So, the following is the order for doing work using Salesforce DX. 

  • Install Visual Studio Code 
  • Install Salesforce Extension Pack. This pack includes Salesforce CLI Integration plugin. 
  • In your salesforce ORG, enable Dev Hub. Please note once this is enabled, you cannot disable it. Enable the following features in your Dev Hub org. 
    • Enable Source Tracking in Developer and Developer Pro Sandboxes. 
    • Enable Unlocked Packages and Second-Generation Managed Packages. 
    • Enable Einstein Features. 
  • Sandbox Expiration Email Opt Out. 
  • Get your repo from your source control system like Git. 
  • Create a DX project. Use Salesforce CLI. It has two executables: sfdx and sf. 

sfdx command: sfdx force:project:create –projectname MyProject 

sf command: sf project generate –name MyProject 

  • Authorise your Dev Hub org 
  • If you have a namespace and want to use it with your scratch org, you should link the Developer edition where the namespace is registered to your Dev Hub org. 
  • Create a Scratch org and a project scratch definition file. Use this command to create a scratch org:  

sfdx force:org:create -f config/project-scratch-def.json -a NameOfTheScratchOrg [email protected] edition=Developer [email protected] –durationdays 30 -v yournamespaceorg -w 10
use this command to create a project scratch definition file:
sfdx force:org:create –definitionfile config/project-scratch-def.json – targetusername YOUR_SCRATCH_ORG 

  • Push source to your scratch org. 
  • Work on the features or enhancements in the scratch org as needed.  
  • Pull the source from scratch org if you have made any changes to components like Flows, Page layouts etc. 

At the root level of your project folder there will be a file called sfdx-project.json. This fille will describe the configuration for each package in the project directory. The contents of the file might look like this:

{
  “packageDirectories”: [
    {
      “path”: “util”,
      “default”: true,
      “package”: “Utility Manager”,
      “versionNumber”: “1.2.NEXT”,
      “dependencies”: [
          “package”: “Core Library”,
          “versionNumber”: “2.5.Latest”
      ]
    },
    {
      “path”: “core”,
      “default”: true,
      “package”: “Core Library”,
      “versionNumber”: “2.5.NEXT”
    }
  ],
  “name”: “MyProject”,
  “namespace”: “ribbonfish”,
  “sfdcLoginUrl”: “https://login.salesforce.com”,
  “sourceApiVersion”: “56.0”,
  “packageAliases”: {
      “Utility Manager”: “0Ho8d002134wkASCAY”,
      “Core Library”: “0Ho8d001230wkASCAY”
  }
} 

 

It specifies that package namespace is ribbonfish, API version is 56.0 and has two packages. Utility Manager and Core Library. Utility Manager package has a dependency. Package aliases section maps user friendly strings to package IDs. 

 

  • Use this command to create a package:  

sfdx force:package:create -n ‘Core Library’ -d “Helpers. It is also now locked” -t Managed -r sfdx-source/MyProject/core -v namespaceorg
 

  • Use this command: to create a version: 

sfdx force:package:version:create –package “Core Library” –installationkey test1234 –wait 10 -v namespaceOrg -f config/project-scratch-def.json –wait 30 -c 

  • Install and test the new package version in a new scratch org. To install use this command:
    sfdx force:org:package:install -p “0Ho8d001230wkASCAY” -k test1234 -u YOUR_SCRATCH_ORG 
  • After the package is installed, open the scratch org and check the application. To open a scratch org use this command:
    sfdx force:org:open -u YOUR_SCRATCH_ORG 
  • Promote the packages to a managed released state. To do this use this command:
        sfdx force:package:version:promote –package “Core [email protected] 

 

Some issues faced while working on 2GP: 

As I was completely new to this, it took some time to get my head around how this works, but once it is over, it was very easy. 

 

Unless the namespace is linked with Dev hub, we cannot do anything, so it is important for the admins to have access to the namespace org to register link between the namespace and Dev hub.  

 

At Ribbonfish, we temporarily lost access to the namespace org which meant I was blocked and couldn’t do any development on packages for some time. Marc Defosse created another namespace in the meantime to speed up things. We crossed the first hurdle of namespace linking but the deployment to scratch org failed because the metadata in the repository was pointing to the old namespace. 

  • Ensure the namespace is not hardcoded in the repository, or at least make sure the reference to the namespace is as minimal as possible. 
  • By the above, even if you have to change the namespace, developers will not have to do a lot of changes to the components in the repo. 

 

Best Practices for 2GP managed packages 

  • Use only one Dev Hub and enable Dev Hub in your salesforce partner business org. 
  • When you run force:package:create command include –tag option if possible so that it keeps your version control system tags in sync with specific package versions. 
  • Create user friendly aliases for package IDs and include them in sfdx-project.json file.