Salesforce CI-CD: Our implementation


Salesforce CI-CD

Recently we started Salesforce development in our Company and I was tasked with setting up CI-CD. During my last two months, I learnt many more things about Salesforce and CI-CD than I learnt when writing Apex code for Salesforce development.

In this article, I just provide an overview of Salesforce CI-CD in our Company.
I shall write in detail (aka technical) about commands and pipeline in next article.

Pre-requisites

  • Knowledge of Salesforce
  • Knowledge of Gitlab and Jenkins
  • Able to connect sfdx with your Org

Why Salesforce CI-CD

With Salesforce development, you are tempted to just write your code, and promote it to production. This works very well. It works for your first project, second project. But then things become unmanageable. So if you are serious about using Salesforce in your organization, then you should be serious about a CI-CD development pipeline from first project itself. If you miss, then you are in for a lot of maintenance. 

Suggestion: Invest in Salesforce CI-CD at least before shipping first code to production.

Why this article

What makes Salesforce CI-CD unique is that it is new, and there are not many articles about it. Some of the articles I referred to during CI-CD pipeline development were:
  • https://trailhead.salesforce.com/en/content/learn/projects/automate-cicd-with-gitlab/package-your-app-and-automate-cicd
  • https://gitlab.com/sfdx/sfdx-cicd-template/-/blob/master/Salesforce.gitlab-ci.yml
  • https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/
For this article, you do not need any of these.

Some points to consider

Salesforce CI-CD is not like a programming language CI-CD. It is different.
  • Salesforce is no friend of micro-service developers. Everything you do, you do for one monolith.
  • Salesforce has many restrictions like execution time limits, ScratchOrg creation restrictions, etc.
  • Need to have a connected app - so that CI-CD could do its work
Restrictions we had to work with were
  • 100 active scratch org
  • 200 daily scratch org limit
  • 100 packages creation
  • 500 unverified package creation 
I wish to stress on ScratchOrg. To run unit tests, you need to deploy your code on ScratchOrg, and then run tests.
Some may argue that 200 scratch org limit is more than enough.
But I would argue that we run CI on each commit. A given Merge Request (or git Pull Request) may have multiple commits. With multiple teams working to meet deadlines, 200 CI runs is nothing.

Our CI-CD pipeline

So here is a gist of pipeline we envisioned.
  • Phase 1: Gitlab ( CI )
  • Phase 2: Jenkins ( CD )
  • Phase 3: Production deployment
Rationale: We wished Gitlab just to keep our unit tested code working as Continuous Integration. And Jenkins will work as Continuous Delivery to build a package, and test it on various environments. And finally when everything goes well, promote the package.
Note: Only promoted packages can be deployed to production Org.

For each Merge Request at a minimum we consume
  • 3 Scratch Orgs (for testing)
  • 1 Package

Phase 1: Gitlab ( CI )

On each commit, we do not run CI pipeline. 
  • Developer writes code on his own branch. Lets call it "feature branch". 
  • Once feature is ready, a Merge Request (or Pull Request) is created. 
  • Developer manually triggers CI pipeline for his branch. This limits auto trigger on intermediate commits. 
  • When CI pipeline passes, then only Merge Request can be "Merge"d into master.
  • Ofcourse, Merge Request needs to be peer reviewed and approved.


Phase 2: Jenkins ( CD )

Once code is in master branch, Jenkin's SCM change picks up:


Phase 3: Production deployment

On CI-CD pipeline is complete, QA team gets into action. 

We have already had tests run as part of pipeline - when why more QA? I do not have a legitimate answer for this. But it is important to maintain quality. And take product approval.

Once when everything is a pass, we give Package Version ID to our Salesforce Administrator for deployment.

We have this part to maintain quality, and also security. Deployment to our Production Org is a manual process.

Extra

Overstepping on each others toes

We had our own codebase, deployed stuff on ScratchOrg, demoed it to product. CI-CD pipeline was also being built in parallel. And were were on right path.

But our first deployment to a common SIT (system integration testing) environment opened a can of worms.
Our code deployed well, but we started seeing Unit test failures.
This is when we realized we are stepping on toes of another project. 

One example:
Our triggers started giving problems. e.g. Existing case trigger made sure Subject is populated when Case is created.
In our use-case, we didn't have Subject created until Service Agent looked at it and updated it.

Solution

Identify and put common code and changes into one project. Think of it as a base project - on which other projects are dependent. So now when deploying your project for testing or CI-CD, you deploy base project first.

How to know which common code to put here:
Simple: If you are touching pre-defined objects - you put code here.
e.g. 
  • Custom objects on pre-defined objects (e.g. you added a new field on Case object)
  • Triggers on pre-defined objects (AgentWork trigger, Case trigger etc)

Comments