This blog provides step-by-step walkthrough on how to build Salesforce Second-Generation Managed Packages & Deploy them to org using the Salesforce CLI.
Back in the traditional days, Salesforce developers depended on the First-Generation Managed Package (1GP) to build and distribute apps. This approach enabled ISVs and developers to package their custom objects, Apex code, and components into a single unit and publish them on the AppExchange.
Fast forward to today, Salesforce has introduced the Second-Generation managed package (2GP), designed to overcome these challenges. The Second-Generation Managed Package in Salesforce is the modern approach to creating, managing, and releasing apps (packages) on Salesforce. It is built using Salesforce DX (SFDX) and scratch orgs, allowing for a more modular, source-driven, and DevOps-friendly approach. With 2GP, developers can create packages directly from source code, version them properly (v1.0, v1.1, v2.0), and release updates smoothly without breaking existing apps.
In this blog, we will explore the journey of 2GP and why 2GP is a game-changer, and see how it helps developers and businesses build scalable, maintainable apps for the future. Let's dive in.
A development approach where the "source of truth" for all customizations and metadata is stored externally in a version control system (like Git) instead of directly within the Salesforce organization. Imagine you are building a Leave Management App for employees and below are the components,
Custom Object: Leave_Request__c
LWC Component: ApplyLeaveForm.lwc
Apex Class: LeaveHandler.cls
Now upgrades are smooth, flexible, and future-ready.
It is like a unique identifier that makes sure your package components don’t clash with components from other apps. A unique brand name is used to differentiate components (Apex classes, objects, triggers, Lightning components, etc.) in Managed Packages. It helps identify and separate your package’s components from others in Salesforce.
Avoids Naming Conflicts, Uniqueness
Example: Surya and Giri create a Separate Managed Package
Surya - Create one apex class - LeadProcessor
Giri - Create one apex Class - LeadProcessor
With Namespace
Surya - fileTranz.LeadProcessor
Giri - Traznow.LeadProcessor
Here, fileTranz and Traznow are a Namespace and it will not affect the package installation if same name used for components.
Before we start the Hands-on, to create the two developer edition orgs, if you want to create the developer edition org, Click Here. Let's start, we see step by step and I had already created the Dev Org, so in our case, let's assume the,
Org 1 name → Test 123
Org 2 name → Test 456
In the Namespace, I have created the Test123 Org and follow the below steps to create,
Go to Setup → Package Manager → Create Namespace → Click Check Availability → Click Review

The Devhub is enabled in Test456 Org and to enable follow the below steps
Go to Setup → Dev Hub → Enable Dev Hub → Enable Unlocked Package and Second-Generation Managed Packages.

Once you enable the Dev hub, refresh the page, and then continue with the steps below.
To link your Namespace org in the Namespace Registers (Here Test 456 org that is Dev Hub Org) , follow the below steps.
In App Launcher → Search the Namespace Registries → Click Link Namespace → Link your Test123 org Namespace

Once we’ve set everything up, the next step is to create and install the second-generation package! Are you excited? 😃
Run the commands below, step-by-step, in your terminal — follow each one carefully!
Command: sfdx force:auth:web: login -d -a aintiramwebtech
Above the command run in your terminal, where As, aintiramwebtech - Alias Name

To set your namespace name in the sfdx-project.json file, see the image below to change your file as that below file,

After setting the Namespace, run the command below in the terminal,
Command: sfdx force:package:create --name "Test2GP" --path force-app --package-type Managed --target-dev-hub aintiramwebtech
After running the command, the CLI has created the Package ID

Next Step, we have to create the Version. There are a lot of versions that have been released. So that's why the every version creates a separate version number.
The purpose of creating a version
Command: sfdx force:package:version: create --package "Test2GP" --installation-key svkVIJAY@123 --wait 10 -v aintiramwebtech --code-coverage
After running the command, the CLI has created the Version ID.

After creating a package version, the next step is often to promote it. Promoting a version means you’re marking that package version as ready for public or production use.
Command: sfdx force:package:version: promote --package "Check2GP@1.0.0-1" -v aintiramwebtech
Here, Check2GP@1.0.0-1 → Which Version do you have to promote?

Once you had promoted the version, you generated the URL as that below,
URL: https://login.salesforce.com/packaging/installPackage.apexp?p0=04tdL000000F15RQAS
You directly search the browser and log in to your Salesforce org, then start to installation process. I have attached a GIF below for your reference,

Hope this blog gave you a clear idea of how Second-Generation Managed Packages make packaging, versioning, and deployment more efficient in Salesforce. Keep an eye out for our upcoming posts to explore more powerful Salesforce development concepts!