I’ve long advocated for ISVs to avoid use of namespaced scratch orgs and often find myself repeating the same reasoning behind that assertion. I’ll try to collect that reasoning here for future reference.
The fundamental problem with namespaced scratch orgs is that they represent an environment that behaves differently from any production environment your customers would ever have. What’s that difference? In a namespaced scratch org, all metadata get the namespace prefix applied to it, not just the metadata in the package.
![namespace-1](https://muselab.com/hs-fs/hubfs/namespace-1.jpg?width=720&height=480&name=namespace-1.jpg)
The challenge with namespaced scratch orgs becomes more visible when viewed through the lens of the Product Delivery Model. To briefly recap, the Product Delivery Model defines a product as more than just a package. Instead, a product is an automation recipe built in version control to automate the complete delivery of a product experience into an org. You can read more about the concepts of the Product Delivery Model in the previous post: A Product is More Than a Package.
A Product Delivery Model recipe contains layers of automation to deploy configuration on top of the package, often utilizing unmanaged metadata:
![namespace-2](https://muselab.com/hs-fs/hubfs/namespace-2.png?width=720&height=819&name=namespace-2.png)
So, what’s the challenge with namespaced scratch orgs in the Product Delivery Model? As you start to build out more complex recipes to automate the delivery of complete product experiences, the is increasing risk of encountering issues where the automatic namespace prefixing in namespaced scratch orgs causes build or test failures in the recipe.
As we got further down the road of implementing the Product Delivery Model at Salesforce.org, we started encountering more and more of these odd failures only in namespaced scratch orgs. These were alway tricky problems to debug and the resolution required the addition of a new type of namespace token string in CumulusCI: NAMESPACED_ORG
and NAMESPACED_ORG_OR_C
. These tokens are used to handle the unique situations only encountered in namespaced scratch orgs. The work implementing them would never benefit the delivery to customers because no customer environment behaves like a namespaced scratch org. For me, the existence of these tokens is an anomaly that really shouldn’t exist.
After spending many hours of developer and release engineering time to debug and fix each of these confusing and unrealistic problems, we set out on the goal of eliminating the use of namespaced scratch orgs altogether.
But How Do I Handle Namespaces in Package Source?
There are two different types of namespace references to consider:
- Static References: Direct references to metadata such as a CustomObject being directly referenced in an Apex class
- Dynamic References: Dynamically constructed strings used to look up and dynamically reference metadata without building a dependency relationship such as a dynamically constructed SOQL query string
Handling Static References is pretty easy: the packaging machinery of Salesforce is supposed to handle this for you automatically. If your package source is calling other metadata in the package with a static reference, it should just work without any namespace prefixing. If not, you may have discovered a platform bug!
Dynamic References are a tricker but solvable challenge. Salesforce.org’s Nonprofit Success Pack (NPSP) includes Apex classes for handling namespace prefixing in dynamic strings. Since NPSP is open source under a BSD3 license, you can use these classes in your own package:
https://github.com/SalesforceFoundation/NPSP/blob/5363fa8f8ae3ef46a5f305c49e7af4e492a4f0cf/force-app/main/default/classes/UTIL_Namespace.cls
You can see examples of how this class is used throughout NPSP’s codebase such as here:
https://github.com/SalesforceFoundation/NPSP/blob/5363fa8f8ae3ef46a5f305c49e7af4e492a4f0cf/force-app/main/default/classes/BDI_BatchOverride_CTRL.cls#L54-L81
There’s a third category that I’ll refer to as the Twilight Zone, weird edge cases often in obscure metadata types. For those, you can use CumulusCI’s namespace tokenization functionality to dynamically inject the namespace prefix at deploy time when needed.
But I Want to Test Package Source in the Namespace!!!
Fair point. Not using namespaced scratch orgs means your first level dev orgs (you are using scratch orgs for this right?) are working with purely unmanaged package source. How do you know if your package source and recipe works correctly when the package’s source is namespaced?
After considering many options, we wound up settling on creating a new process for building namespaced, unverified 2GP managed package versions in a separate packaging line from production for every feature branch commit. The CI system handles this automatically and records the package version id (04t…) as a commit status in GitHub. This allows anyone on the team to easily create a new scratch org with a namespaced version of the package installed using CumulusCI’s built in qa_org_2gp
for testing before ever touching the production packaging line (1GP or 2GP). It also allows your CI system to spin up and test orgs with the package installed.
You can read about this approach in much more detail in the excellent Salesforce Architects Blog article written by David Reed and Brandon Parker: Find Bugs Earlier with Second Generation Packaging
Conclusion
As a former debater, I often like to start from a more radical ideal point of view and work my way backward by thinking through the exceptions.
There is no 100% applicable guidance on anything package related in Salesforce. Every situation needs to be evaluated based on the unique characteristics of the product, the company, and its customers. However, after years of implementing this process across numerous ISV packages, I can say that exceptions from the rule to stay away from namespaced scratch orgs are incredibly rare edge cases. That said, you might be one of those edge cases and can make the decision to break the rules where appropriate.
Still have questions about this approach or have some use cases that are good examples of exceptions? I’d love to hear about them. Grab a time for us to chat at https://calendly.com/muselab
Comments