A common challenge when designing a Salesforce CI pipeline is how to organize the metadata. Which components should be put into a package? Which components are better kept in the famous „happy soup„?
To solve this challenge, an architect has to understand the benefits and downsides of packages. Here are the most critical ones that drive our decision-making:
- Packages are considerably slower to iterate than unpackaged source. On the other side, they reduce interdependencies and are much more stable to develop and deploy.
- Package versions are immutable artifacts. This makes rolling back inherently easier. However, deployment is more challenging from a technical perspective.
- Some metadata types are well supported for unlocked packaging. Others not so much. The Metadata Coverage Report is an acceptable starting point to learn about that.
- Some types require more changes than others. Understanding which, is vital.
All these factors should influence your decision, how you design your packages, how you manage their dependencies, and what metadata you put into them.
Because the Metadata API constantly evolves, it doesn’t make sense to publish a final list of recommendations here. Instead, I created a repository on GitHub that will be kept up to date. You can find it here:
The repository offers an overview of the most important metadata types and my recommendation, on where to organize them.
Why Organize Your Metadata?
A good architecture combines the best of both worlds: The quality and feature-richness of a pro-code implementation and the efficiency and agility of Salesforce’s no-code and low-code features. To achieve this, we need a holistic strategy for how we organize our metadata. These are the typical requirements I design for:
- React swiftly to new or changed requirements that do not affect critical business logic (such as layouts or quick actions).
- Keep up with changes in the standard features such as assignment rules, auto-response rules, etc.
- Quickly set up and maintain small automation such as email alerts and field updates.
- Integrate admin developers into our workflow. Small changes to no-code features should not require professional developers.
- Provide means to deploy safely. Automate regression testing for everyone.
- Have all configurations under version control. Every change should be traceable.
- Provide lean, stable, and reproducible development environments.
- Production and sandboxes are in sync and easily reproducible.
- Scale to multiple SCRUM teams that can work parallel and deploy independently.
All of this can be achieved. Essentially, you need to get two things right: The functional architecture of your packages and the technical organization of your metadata.
When deciding where to put your metadata, keep the following considerations in mind:
- Packages are extremely powerful because an upgrade can remove metadata.
- As a result, metadata that is moved around a lot benefits the most from being in a package container (think of Apex Classes, UI components, custom objects with fields, validation rules, etc).
- On the other hand, metadata that is org-wide unique does not benefit at all from being in a package (think about assignment rules, layouts to standard objects, flexi pages, etc)
- Packages have considerable overhead, compared to your unpackaged source.
- New package versions require setup commits in your VCS. Additionally, they require a sophisticated source management strategy to scale.
- Packages are extremely slow to build and deployment requires multiple commands (package create, package promote, package install).
- Because package versions are immutable, packages make it substantially easier to scale.
- Development teams can rely on stable upstream dependencies. This allows you to decouple your teams and work on multiple modules in parallel.
- Builds are much more stable, and errors are much easier to reproduce and find.
- Rollback is a piece of cake and can be automated with standard commands. Previously, we had to buy expensive third-party vendors.
Rules For Organizing Your Metadata
Based on the above considerations, here are some intuitive rules you can apply.
- When dealing with standard features of Salesforce’s customizability (Email-To-Case, Assignment Rules, Auto-Response Rules, etc), unpackaged is almost always the best choice.
- Metadata that is barely supported, requires some special treatment after deployment, or is buggy as hell, should be kept in the happy-soup as well. For Experience Bundles (formerly known as Communities), I even isolate them in their own repository.
- Metadata that undergoes many changes, but cannot be tested with Apex or LWC is put into happy-soup, too. This encompasses Layouts, Flexi Pages, Quick Actions, Queues, and the likes.
- Everything that is typically part of a complex application, should be packaged. The usual suspects are Apex Classes, Apex Triggers, Custom Metadata Types, LWC and Aura Components, Custom Objects, etc.
- All low code implementations that can be tested, go into packages as well. This applies to record triggered and UI flows, approval processes, and similar features.
These rules should help you understand, why some types should be organized a particular way. However, there are plenty of exceptions and special considerations. Make sure to read all detailed recommendations in the linked repository.
To scale Salesforce development, we need to make smart decisions about how we deploy our code. Some components are best put into packages, other components should stay unpackaged. However, everything should be under version control. The js-guideline-package-architecture repository provides a guideline that supports the decision making how we deploy and manage our metadata.