Salesforce deployment approach from scratch in 2021
This story will be describe the step of Salesforce deployment by merge CI/CD, Version control, unlocked package and SFDX. This approach will apply when you face with the problems about the long period for deployment with new project of Salesforce or big release of your implementation. In this story, I separate 4 topics.
- Unlocked package evolution
- Application Life Cycle
- Deployment Strategy
- Development Strategy
Noted: This story is created in 2021. Salesforce might provide the usable feature for your life better in the future. If you don’t have any problem about a change set, you can skip this.
Unlocked package evolution
Change set problems
The biggest problem of Salesforce developer when you have to deploy Big release or small release of change set. Normally, you will face problem following below:
- Object dependency such as you need to deploy objects and field before your code or you need to bundle with your code as know as prerequisite object. As a consequence, missing object, redeploy change set and time of validation are usually jobs you need to get it done! 😵
- The waiting period for change set deployment lead to overnight. 😭
- Other reasons, we cannot track the source of change and who edited on this object and already deploy or not. That effect my team need to create another document for planning for deployment. 😿
In around 10 years ago, Other web development has version control, CI/CD, and Docker and so on for easier for deployment and make happy to develop. Including SAP have Transport Management System (TMS) which is tracked every my activity of my configuration that easier for BASIS team. But Salesforce is the hell for developers.
Try to solve problems from communities and Salesforce
ANT: Previously, developers try to use ANT to deploy metadata for automate deployment, but still have problem about dependency again.
1GP: Following that, developers using First-generation managed package as same as a package for app exchange, to deploy the object.
- That means you will not have ability to see the code inside the package.
- It is hard for developers to track the issue in production org.
- Also, don’t have version control to manage this.
- Single big package limited.
2GP: Winter 2018, Salesforce release 2nd Generation managed package with Salesforce DX but this package was designed for app exchange. It is not designed for internal development. Cannot track the version and code visibility also.
- It is hard for developers to track the issue in production org
- Single big package limited.
Reference 1GP and 2GP comparison and SFDX API version 41
Unlocked package: Summer 2019, Unlocked package concept come up but don’t have any update. Until Winter 2020, Salesforce release unlocked package officially which is nearly the same as the second generation package but that have visibility to see the code inside the package and you can edit that but they still don’t have approach for deployed Salesforce also.
The powerful reference of this by Louis Bompart via coveo
Application Life cycle
In winter 2020, the same period of unlocked package release. Salesforce announced Package development model (API Version 47.0). link

This year sales force tried to use ALM approach to manage development cycle. But it still confused for CI/CD and package management and repository management. This approach, we can use the second generation package and unlocked package to improve our development and our life. 😏
Deployment strategy
This section I tried to find solutions for many websites, watched many YouTube, trailhead from the approach of the second generation package, unlocked package and package development model. To summarize, the solution to deploy with unlocked package.
On March 2021, This time Salesforce has 3 strategies for deploying Salesforce
I. Change Set Development Model
This model you will encounter problem about
- Tracking everyone’s changes
- Keeping all the development and testing environments in sync
- Creating and deploying change sets multiple times because of errors and differences between environments
II. Org-Based Development Model
- This process is using a repository to store our metadata in Salesforce. These can help, developers can track changes. Similar to change sets, the release artifact is a set of metadata changes to apply to the production org
- This process mitigates another pain point: creating and deploying change sets multiple times because of errors and differences between environments.
III. Package Development Model
This process is using version control to be a source of truth and using modular concepts to develop or separating the package.

I will explain and how to deploy package development model.
Perquisite:
- Visual Studio Code
- Install Salesforce extension pack -> link
- Version control <- I will use GitLab
- Activate your Developer Edition Org or Trailhead Org
Summary Step:
- Create project with manifest in visual by:
- Create your scratch org and push your code to scratch org
- Push you code to version control
- Update your code
- After you push your code to the repository, GitLab CI will run by create scratch Org and push your code to the scratch org and CI will run apex test in the scratch org.
- After Apex Test pass, CI job will transfer to CD to create a new package version
- Consequently, the step deploy your new package version will be manual jobs to install your version to production
—
Detail Step:
I — Create a project with manifest in visual by:
- Open the VS Code editor and from the Command Palette, run SFDX: Create Project with Manifest.
- CTRL (CMD) + Shift + P and type SFDX: Create project with Manifest
- Select Standard Project
- Define your project name
- Select directory and click Create project

You will get project like

II — Create your scratch org and push your code to scratch org
- Please check your org enable Dev Hub or not. If not, Please follow:
- Go to Set up in your org
- Search Field from the setup menu (Typing Dev hub) and select Dev Hub. This menu will be under Development
- Enable Dev Hub
- Enable Unlocked Packages and Second-Generation Managed Packages - Go to Visual Studio Code and authorize you dev hub with your local machine
- Command Palette, run SFDX: Authorized a Dev Hub
- Run commandssfdx force:auth:web:login -d -a DevHub
- Create your scratch Org
- Command Palette, run SFDX: Create a Default Scratch Org
- Define your definition, file select default (config/project-scratch-def.json) ENTER
- Set alias for you scratch Org MyScratchOrg
- Define the number of expiration day between 1–30 days Type 7 and Enter
- Run commandssfdx force:org:create --definitionfile config/project-scratch-def.json --durationdays 30 --setalias MyScratchOrg -v DevHub
- Create your code. For example, I will use GIPHY trailhead
- Download code from https://github.com/developerforce/GIFter
- You can copy the code from force-app/main/default/ in the GIPHY repository to your project in visual studio
- Create GIFter API
- Reference Read more - Push your code to scratch
- Using Command palette, Run SFDX: Push Source to Default Scratch Org or SFDX: Push Source to Default Scratch Org and Override Conflicts (to override all metadata). Or run in terminalsfdx force:source:push
- For GIFter App, You should add permission to your users by run in Terminalsfdx force:user:permset:assign -n GIFter
- Validate your code by Open you scratch Org
- Command palette SFDX: Open default org orsfdx force:org:open -u MyScratchOrg
or run with default app GIFTer bysfdx force:org:open -p lightning/n/GIFter


III — Push you code to version control
If you are first time the CI and CD will create fail, it is because don’t have a version number of unlocked package. You need to create a new unlocked package manually by
Create new Unlocked package
- Run command
sfdx force:package:create --name "<YOUR PACKAGENAME>" --path force-app --packagetype Unlocked
– name: unlocked package name that should be the same with sfdx-project.json
–path: directory for create package
–pakcagetype: Unlocked because we want to create unlocked package - After you create unlocked, you will get updates in file sfdx-project.json like JSON below
sfdx-project.json before run command
{
"packageDirectories": [
{
"path": "force-app",
"default": true
}
],
"namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "51.0"
}
sfdx-project.json after run command
{
"packageDirectories": [
{
"path": "force-app",
"default": true,
"package": "GIFTer",
"versionName": "ver 0.1",
"versionNumber": "0.1.0.NEXT"
}
],
"namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "51.0",
"packageAliases": {
"GIFTer": "0Ho---------------"
}
}
Prepare repository in GitLab
- Go to GitLab and create your repository
2. Download CI/CD template from https://gitlab.com/sfdx/sfdx-cicd-template and copy to your project in root directory
3. Push your code the repository
cd existing_folder
git init
git remote add origin <REPOSITORY PATH>
git add .
git commit -m "Initial commit"
git push -u origin master
4. Define GitLab pipeline file on Gitlab -> Project page -> Setting -> CI/CD and expand CI/CD configuration file to be Salesforce.gitlab-ci.yml

5. Configure CI/CD Environment on Gitlab -> Project page -> Setting -> CI/CD and expand the Variables section
- DEVHUB_AUTH_URL: force://PlatformCLI::<Hash coded>@XXX.my.salesforce.com
- You can get AUTH URL by commandsfdx force:org:display --targetusername <your email> --verbose
- Please copy value of the role Sfdx Auth Url to value field in Gitlab Ex. force://PlatformCLI::<Hash coded>@XXX.my.salesforce.com - DEPLOY_SCRATCH_ON_EVERY_COMMIT: true
- PACKAGE_NAME: GIFTer
- SALESFORCE_DEPLOYMENT_APPROACH: PACKAGING
- TEST_DISABLED: true
- SCRATCH_DISABLED: false
- SANDBOX_DISABLED: true
- PRODUCTION_DISABLED: false
Update your code and run test class
Create test classes for APEX Class ChatterHelper.cls
- Command palette SFDX: Create Apex Class and select a default folder class name AnimalsHttpCalloutMock and TestChatterHelper
AnimalsHeetCalloutMock.cls
@isTest
global class AnimalsHttpCalloutMock implements HttpCalloutMock {
// Implement this interface method
global HTTPResponse respond(HTTPRequest request) {
// Create a fake response
HttpResponse response = new HttpResponse();
response.setHeader('Content-Type', 'application/json');
response.setBody('{"animals": ["majestic badger", "fluffy bunny", "scary bear", "chicken", "mighty moose"]}');
response.setStatusCode(200);
return response;
}
}
TestChatterHelper.cls
@isTest
private class TestChatterHelper {
@isTest static void testMain() {Test.setMock(HttpCalloutMock.class, new AnimalsHttpCalloutMock());
// This causes a fake response to be sent
// from the class that implements HttpCalloutMock.
// Verify that the response received contains fake values
// Call method to test
String url = 'https://media0.giphy.com/media/';
String res = ChatterHelper.getChatterGroups(url, 'chatterText');
System.assertNotEquals(res,'a');
}
}
2. Push your code to scratch org run command in terminal
sfdx force:source:push
3. Run Apex Test by Command palette SFDX: Run Apex Tests
Create a version of unlocked packages
You can create your version by command
sfdx force:package:version:create -p “GIFTerPackage” -d force-app -k test1234 --wait 10 --codecoverage
— test1234 is pass code for your package
GIFTer@0.1.0–1 will be added in your sfdx-project.json after creating a new version
{
"packageDirectories": [
{
"path": "force-app",
"default": true,
"package": "GIFTer",
"versionName": "ver 0.1",
"versionNumber": "0.1.0.NEXT"
}
],
"namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "51.0",
"packageAliases": {
"GIFTer": "0H---------------",
"GIFTer@0.1.0-1": "04---------------"
}
}
Install your package in your scratch org
sfdx force:package:install — wait 10 — publishwait 10 — package “GIFTerPackage@0.1.0–1” -k test1234 –r
Promote your code to Dev Hub org
sfdx force:package:version:promote -p “GIFTerPackage@0.1.0–1”
Commit and Push your code to your repository
IV — GitLab CI running after push your code
After you push your code to the repository, GitLab CI will run by creating scratch Org and push your code to the scratch org. After that, CI will run apex test in the scratch org.
V — APEX test and create version
After Apex Test pass, CI job will transfer to CD to create a new package version
VI — Deploy your package to production
Consequently, the step deploy your new package version will be manual job to install your version to production

In your trailhead org, GIFTerPackage will be installed.

Inside GIFTerPackage package will have follow picture below.

—
Summary Command:
1. Create Visual studio project SFDX: Create Project with Manifest
2. Authorize Deve hub sfdx force:auth:web:login -d -a DevHub
3. Create Scrate Org sfdx force:org:create - definitionfile config/project-scratch-def.json --durationdays 30 -- setalias <YourOrgAliasName> -v DevHub
4. Push and pull your code between local and scratch org sfdx force:source:push
or sfdx force:source:pull
5. Assign Permission set between app and users sfdx force:user:permset:assign -n GIFter
6. Open Your Org sfdx force:org:open -p lightning/n/GIFter
7. Create Unlocked package sfdx force:package:create --name "<YOUR PACKAGENAME>" --path force-app --packagetype Unlocked
8. Create unlocked package version sfdx force:package:version:create -p "<YOUR PACKAGENAME>" -d force-app -k test1234 --wait 10 --codecoverage
9. Install package to your scratch org sfdx force:package:install --wait 10 --publishwait 10 --package "<YOUR PACKAGENAME>@0.1.0-1" -k test1234 –r
10. Installed app to your dev hub org sfdx force:package:version:promote -p "<YOUR PACKAGENAME>@0.1.0-1"
—
Developed your unlocked package with other
When another developer or your team wants to contribute with your unlocked packaged. You can do the following step after the initial project.
1. Developer B should clone the repository to local and open the project with visual studio
2. Authorize Dev hub with developer b’ user
3. Create scratch org for developer B.
sfdx force:org:create --definitionfile config/project-scratch-def.json --durationdays 30 --setalias <YourOrgAliasName> -v DevHub
4. Developer B push code from the repository to their scratch org
5. Develop code, write your test class, and sfdx force:source:push
6. Configure metadata ex object, field, app in scratch org and sfdx force:source:pull
to update your local project
7. Commit and Push edited code to the repository. (CI/CD will do their job by create new version of your code)
8. If you want to deploy to production, just manual click play in the pipeline of GitLab

Latest salesforce suggest Org-dependent unlocked packages to spin off your current org to small package following youtube.
Development strategy
In the next activity, I will explain how to merge the deployment approach with version control strategy (Branch Strategy). First, The selection of Branch strategy follows.
- Git flow: link which has 3 main branches which are the main branches, Supporting branches, and feature branches. These will complicate you to manage package release.
- Trunk-based development or GitHub flow: which have only One branch for development because when you deploy your code to UAT or production, you just install the version. You can try to see it on youtube at 23.30.
Subsequently, when you studied trunk-based development strategy. You will have questions that are“Will we deploy all metadata inside One package?” If we follow the concept of the modular package, we need to split the package by modules, functions, releases, etc. Moreover, if we create by module, it normally has dependencies with another module. How can we solve these issues?
- To solve the problem of the big package: You need to separate the package by modules, function or release, and so on.
- That will create a new issue of dependency for each module.
For example, Module A is the main package, Module B needs Module A, and Module C needs Module A and B.
That is mean you need to design a module to represent a package (a Module = a package) - Next problem “How can I design modules?”: And I found a youtube for design main or child package.
- A repository in version control should be a repository to a package. (That is means A Module = A package = a repository). For easier to manage CI/CD and dependencies.
Reference
- Start with Trailhead SFDX https://trailhead.salesforce.com/en/content/learn/trails/sfdx_get_started
- Trailhead package development model https://trailhead.salesforce.com/en/content/learn/modules/sfdx_dev_model/sfdx_dev_model_release
- Unlocked package https://trailhead.salesforce.com/content/learn/modules/unlocked-packages-for-customers/break-up-your-metadata?trail_id=sfdx_get_started
- VSCode for salesforce development model https://developer.salesforce.com/tools/vscode/en/user-guide/development-models/
- Development salesforce with version control https://www.youtube.com/watch?v=ZESBrJWM-mU
- A project can have multiple package inside your project you can see example following ES-SPACE https://github.com/trailheadapps/easy-spaces
- Salesforce Microservice Architectures With Sales And Service Cloud https://www.salesforce.com/video/1772123/
- Salesforce Developer guide to microservice https://a.sfdcstatic.com/content/dam/www/ocms/assets/pdf/misc/Salesforce_Microservices_E-book.pdf
- Git flow https://nvie.com/posts/a-successful-git-branching-model/