Open Guide to SF Solution Packaging (1GP)
This site is deprecated in favor of https://github.com/DreamOps/og-sfdx
This guide is NOT an official Salesforce resource. |
|---|
Welcome
Each heading on this page (like "Welcome") is a link to the corresponding section in the official Salesforce ISVforce Guide.
The Open Guide to SF Solution Packaging is designed as a companion to the Salesforce ISVforce Guide.
Each chapter in the Open Guide links to the corresponding chapter in the ISV Guide.
By following the links to the ISVforce chapters, you can orientate yourself to the base platform functionality.
The material offered in the Open Guide builds on the essential information provided by the Salesforce ISVforce Guide.
See also
Quick Start
"Learn to plan, build, distribute, market, sell, and support solutions that run on the Salesforce platform."
Basics
Salesforce environments can be heavily customized by end users (and usually are).
Most local customizations can be packaged as a solution, or app, and distributed to other Salesforce customers.
By going through a Security Review process, these packages can be listed on the Salesforce AppExchange.
The Salesforce AppExchange works a lot like the Apple or Android stores.
You can download solutions that extend your Salesforce environment, some of which are free, and others that are paid.
In 2015, license revenues from AppExchange apps topped $1.5 billion. Nice!
For development and testing, customers can clone the Salesforce production environment into a working "sandbox".
A production org comes with several sandboxes and more can be purchased.
Sandboxes can come with or without data. The ones with data come at an extra cost. With some difficulty, you can also import data yourself.
The platform provides various ways to move new features from a sandbox to its production org.
For packaging solutions, Salesforce provides a companion infrastructure, that uses either Developer Edition orgs or DX scratch orgs.
For customer trials, demonstrations, or development, ISV partners can provision new temporary orgs from a snapshot of a special source org using another infrastructure, Trialforce.
Caveats
The Salesforce platform is deeply extensible. Customers have full access to a package's data schema and the storage of all standard data records.
For customers, this extensibility is a huge benefit, and it is a key driver for the immense popularity of Salesforce.
Ironically, for package developers, this same extensibility makes sweeping changes to our solutions more difficult to implement.
Most or many of the Caveats in this open guide are a consequence of the platform's extensibility.
Working on a shared, multi-tenant platform is a significant paradigm shift for developers used to working on a standalone application hosted on private servers.
In practice, accommodating local extensibility is the cost of doing business on the highly collaborative Salesforce platform.
Design and Build Your App
"Discover the architectural concepts that influence AppExchange solution design. Learn how to plan, build, and package your solution for customers."
Basics
Architectural Assessment
Style | General Conventions | Observes your Apex Style Guide. |
Triggers |
| Does your org have one trigger per object? Is there no business or application logic written directly in a trigger? Do triggers “hand off” logic or functionality to other classes (aka trigger handlers)? |
Apex Classes |
| Do Apex classes use common prefixes or even namespaces to group units of code? Do classes have similar names, based on functionality? Is the purpose and authorship of code documented in comments? Do classes have comments that help clarify function? Do components use API version 24 or later? |
Apex Tests |
| How do tests relate to other code? Does each class have its own test? Are tests organized into functional groups? Are all parts of your code base covered by tests? Do tests depend on common data factories or static resources? Do all of your tests not use the 'seeAllData=True' annotation, and run on API version 24 or later? |
Lightning Components and Events |
| Do components use common prefixes or even namespaces to create groups? Do components have clear names, related to functionality? Are Lightning events scoped to be application events or component events? Are the purpose and authorship of components and events clearly documented in comments or Aura documentation files? Do components use Apex controllers? Do components use API version 29 or later? |
Visualforce |
| Do Visualforce pages and components use common prefixes or even namespaces to create groups? Do pages have clear names, related to functionality? Do pages use Apex controllers? Do pages use API version 24 or later? Are no pages used with email templates? |
Objects |
| Does no objects implement a master/detail relationship between a standard and custom object. |
Enterprise Patterns | (TBD) | (TBD) |
Tips - Design
Adjust your Expectations. When Salesforce designed first-generation packaging (1GP), it seems that the first priority was protecting the subscriber, even if that meant simplifying the packaging tools.
As a result, the 1GP model does not always follow what many people would consider common practice for solution versioning and packaging.
Of course, many of those practices evolved for standalone solutions, and they do not consider the needs of a solution that plugs-into a highly extensible and transparent environment.
Creating a solution that plugs-into Salesforce is a very different experience than designing a solitary .NET solution. #WelcomeToTheJungle!
Adopt a Style Guide such as the NimbleUser Apex Style Guide.
Avoid Master/Detail Relationships between a standard object and a custom object. These M/D relationships can't be packaged with a permission set, and they can present performance issues.
Data Storage Costs. Salesforce charges by the row and not by the number of bytes stored. For cost purposes, each row is allocated a flat 2kb, 4kb, or 8kb, regardless of the actual row length.
Field Lengths. Consequently, you can err on the side of longer fields without driving up your storage costs.
Normalization. Conversely, if you over-normalize your data schema, you can drive up data storage costs, since a by-product of heavily normalize schemas is more rows.
Assisting with Data Import. Include an External Id field on each of your objects so that customers can use it when importing data.
External IDs. When creating a text field for an External Id for customers to use, it's helpful to use the full 255 character length.
Declaring the full length doesn't cost us anything and someone may need it later.
Tips - Architecture
Consider Feature Management. Using feature management you can create "software toggles" in your Apex code to hide new capabilities (thereby separating deploying from releasing).
When coupled with permission sets, feature management allows you to "dark launch" features.
Package developers also use feature management to enable extra-cost features.
Document your API with Apex Doc that live in your code.
A webhook in your version control system can be used to to regenerate the docs.
The generated Apex Docs are static HTML and easy to host in a S3 bucket.
Tips - Development
Empty Lists are DML No-Ops. When a data operation is passed an empty list, the operation is not performed on the empty list. In the following example, when
recordsToUpdateis empty, the following two commands are equivalent, with the second form preferred. (Some time ago, the first form was preferred, but the platform is smarter now.)if( !recordsToUpdate.isEmpty() ) { update recordsToUpdate; }
update recordsToUpdate;
Tips - Marketing
Free to Paid. If you start out with a free app, and then decide to to bring out a paid version, you can position the paid version as an extension to the base free app.
Of course, the paid extension will need to pass the Security Review and the usual fee will apply.
See also Publishing Extensions to Managed Packages in Salesforce Help.
Even better, Consider Feature Management (see Tips - Architecture).
Caveats
RTFM. To protect multi-tenancy, the platform has a number of well-documented allocations and limits. There are also a number of considerations when packaging components. For starters, be sure to study these key resources:
Aloha Limits. Lightning apps, custom apps, and custom tabs -- only – don’t count against the allocations for the subscriber's org only when in a managed package that’s passed Security Review .
All other component and runtime limits apply, including components like Custom Report Types.
This point is mainly a limitation for Professional Edition, which has lower limits for several components, such as permission sets and flows.
Apex Test Tour. No matter how many years and how many times you run the Apex tests in an org, every so often the system will ask you if you want to take the guided tour.
Package and Test Your Solution
"Learn how to package, upload, and install a beta version of your solution as part an iterative development approach. After your beta is up and running, learn how to test, fix, extend, and uninstall the solution."
Tips
Checklists Are Your Friends. Successfully designing, packaging, testing, distributing, supporting, and upgrading Salesforce solutions can be an intricate mixture of your own processes and steps mandated by the platform. Developing a set of checklists can go a long way toward making this elegant dance a repeatable, teachable process. 'Nuff said.
Policy Statements. A good companion to a set of checklists is a page outlining your packaging policies, the simpler the better.
Consider Namespaces Carefully. Each package has a full-name, description, and namespace. The namespace is mainly for developer use, but it is shown on the Installed Packages page. The full-name and description can be updated, but the namespace is permanent and can never be changed.
Review how other packages approach namespaces, and consider developing a style guide for namespaces before creating your first package. – It's rarely your last.
Remember there are a lot of packages sharing a 15-character range for namespaces. It's unlikely you will be able to claim anything short and obvious.
Apex is suppose to be case-insensitive, but there are spots where the namespace is expected to be rendered in upper case.
Consider using upper case when you first specify the namespace in your Developer Edition packaging org.
On an Object, Don't Package an "All" List View. If you do, then subscribers are faced with two "All" list views, the original implicit All list view and the one from your package. The list views are subscriber deletable, so it's a quick fix on the customer side, but, it's not a professional look for your solution.
Take Care When Packaging Permission Sets and Designing Profiles. There are restrictions around packaging permissions. The Salesforce Help pages indicate what can be packaged, but, for the most part, do not indicate what cannot be packaged.
If you include a grant in your package that is not permitted, the grant is silently dropped. You won't know it's not working until you try it.
One example is that System Permissions cannot be packaged, but you may need them to allow people to run flows.
Another example is that you can't package Record Type Visibilities in permission sets, and so you have to finesse profiles for record type settings.
In both cases, the documentation does omit these component type from the list of what can be package.
The page does not detail what cannot be packaged, and it's not clear why a simple table is not used.
See also Permission Sets and Profile Settings in Packages and Permission Sets Considerations in Salesforce Help.
Prototype with a Proxy/Beta Package. True betas can be problematic since they cannot be upgraded.
A more useful approach is to create a second, proxy package under another namespace, and deploy your code to the proxy package first.
Ideally, reserve your preferred namespace in a Developer Edition org, and fully develop the initial version in another "proxy" packaging org, under a different namespace.
Automate uploading the package on at least a nightly basis. Some Apex Tests only fail during the package upload process.
Sometimes tests will fail during upload for namespacing issues.
Supposition: Other times it may be because a different test runner is used during packaging.
Then after uploading and testing the proxy, time and again, deploy the same code to your "golden copy" org, and upload it under the customer-facing, production namespace.
Continue to develop new major versions first using the proxy, and then switching to the golden copy for the final release candidate.
Semantic Versioning. Salesforce provides three digits for a version but only requires use of two. The other digit can be used to implement semantic versioning, to indicate a change in dependencies or such.
1.2.3 - Salesforce uses digit 2 and 3, but never touches digit 1. You can advance the first digit whenever there is a significant semantic change to your package.
Seasonal Versioning. Another use is to indicate your own seasonal version in the leftmost digit. Under this approach you can bring out several major versions as release candidates, without rolling the leftmost digit more than once.
20.3.0 - Winter
21.2.0 - Summer
22.5.0 - Spring
Review New Components Before Upload. First, make sure component names meet your style guide standards. Second, if you haven't packaged a component type before, review the considerations before jumping in. Components like Flows and Custom Report Types, among others, have restrictions that you might not expect. See Caveats.
When uploading, Salesforce doesn't itself provide a new component list, though here is a Gist that scrapes the Package Details page:
Review New Dependencies Before Upload. If you are using the Upload Form, the dependencies are conveniently shown at the end of the form. Just remember to look!
If you use a proxy package, with automatic uploads, you can check the dependency afterwards by opening the Package Details page and clicking the Dependency button.
The presentation here is more detailed (and less convenient) than the presentation on the Upload Form.
Standup a Technical Change Log for Each Version. The technical change log should indicate which tracker issues were merged into source control (such as Jira issues), the new components being added, and any new dependencies.
The change log is also a good place to indicate any manual upgrade steps, such as deleting an Apex class or other component, or changes a local administrator might need to make.
Often upgrade steps can be captured in the tracking issue.
If you have indicated an issue tracker ID in every commit, you can use a pull request to determine what issues are being merged.
If you are using Jira and Confluence, it's easy to put a Jira issue on Confluence page, and then filter by the fixVersion. Bitbucket also automatically links Jira IDs to the underlying issue.
Organize by Release. Rather than keep different "types" of documents together, consider keeping all of the documents for a given release together. Often questions might arise about a past release, and it's convenient to have all of the related documents together. (A similar idea is to keep all your tax records for each year together – as opposed to keeping all the bank statements together in the same file year-after-year.)
Use Mocks in Apex Tests. Implement mocks to streamline test run time, both to accelerate development and also to reduce upload time.
Work Around UNABLE_TO_LOCK_ROW False Negatives (Kevin Jones)
Install New Versions to a Firewall Sandbox. In the event of a real emergency, you can undo a major release by reverting your managed package to a beta version. But to be reverted, the version cannot be installed in any org whatsoever. By upgrading a sandbox first, you can catch the worst errors while it's still easy to refresh and revert.
Firewall Regression Tests. If your solution exposes any global components, be sure to run tests against the global API from outside the package. This step will quickly catch any anomalies, which you can still revert and rework new global components.
The simplest approach is to copy the Apex Tests that exercise the global members from within the package, so that they invoke the global members from outside the package – the way your extensions or subscribers will do.
A more effective approach is to document your Global members with Apex Docs, and then develop a set of Apex Tests that validate what's said in the Apex Docs.
Test Against the Next Version. During the seasonal rollouts (October/February/June), the upcoming version is available, giving you the opportunity to test against both the current and preview versions.
There are three ways to access the upcoming release during the rollout period.
Sign-up for the Pre-Release - Google "Salesforce Pre-Release" and look for the upcoming season.
If you signup for a Developer Edition, the org will persist into the next release and be upgraded automatically.
Use a preview sandbox on a test or customer org - Google "Salesforce Sandbox Preview" and look for the upcoming season.
Select the Salesforce Release for a Scratch Org during the preview period.
Caveats
Namespaces are Forever. The namespace assigned to a managed package can never be changed. See also Tips.
Object and other component names are tied to the namespace, making changing the namespace reference non-trivial, as local customization may also reference the namespace.
Be Mindful of Immutability. Once a version is released, global method signatures cannot be changed; global classes and most other components cannot be removed.
Global scope is intended to be used by consumers of your package: local customizations and extension packages (which would immediately be broken if the global signatures change).
See also: Install New Versions to a Firewall Sandbox.
Do not promote a member to global unless it absolutely and positively has to be global for other people to use your package, and you have no other choice.
Global scope is not needed simply because your class is a batchable implementation, or some such.
Global scope is only needed to call a member from outside your package.
Global immutability extends to interfaces as well, which can sometimes be a surprise.
Using the Salesforce Deprecation annotation is generally not useful. If a global should not be used, raise an exception instead instructing the consumer as to what to do instead.
If you mark an existing class as deprecated with the annotation, and upload it as part of your package, two things happen:
1. That method cannot be used in new subscriber orgs, but it remains visible to orgs that had the earlier version of the package installed, before it was deprecated.
2. It becomes impossible to make later changes to this class's content.
The second point can be a foot-shooting moment since it's irreversible, to the extent that it also becomes impossible to fix pre-existing bugs in the deprecated code.
The lesson is that before creating a global, you should be sure that you have a way to communicate changes to your stakeholders, and avoid shackling yourself to obsolete code with the deprecated annotation.
One and Done. Some components can be removed after release – and many others cannot. See Components Available in Managed Packages for details.
You may be able to use a Remove link from the Package Details page, or you may have to delete it from the packaging org.
To delete a component from a package, Salesforce Support will need to enable component deletion for you.
It seems like fewer components are deletable, and most newbies need to be cloned to be subscriber editable.
Once a component is cloned, it it not upgradeable, and so subscribers may need to manage their own updates.
Once a packaged component is deleted, the API Name can never be used again in the packaging org.
Allowing the package to create another component with the same name could conflict with local installs that have the prior component.
Some people will leave an Apex class and just empty it out, so they have the option of using the same name again.
Other people delete the obsolete component, and if it's wanted again then append an integer to the API Name ("API_Name2") and set the user-facing label any way you like.
Automatically deleting visible components (like Custom Fields and Record Types) could break local customizations, so they are left in place, but detached from the package, and left in a deletable not not-editable state.
The package-removed components can be deleted manually from Setup in the Subscriber's org as per customer's business requirements.
Package-removed master/detail indexes have been known to cause performance issues if not deleted.
Some components might be included automatically through dependency resolution, and once packaged, some components cannot be removed.
See also Review New Components Before Upload.
No Rollbacks. Once a new version of a package is installed, you cannot simply "rollback" to the prior version. You can either uninstall the package and reinstall a prior version or roll-forward to another new version. If the package stores data, uninstalling is disruptive, and so we generally roll-forward to correct an issue.
Once the new version is installed, customers can take advantage of new features, and rolling back the version could break local extensions.
Apex Tests Limit Upload Time. The platform runs all of your package's Apex Tests on each and every upload, one at a time. For larger packages, with thousands of Apex tests, uploads can take several hours to complete. (In an extreme case, one developer reports that uploads for their package can take days while the tests run.) The only mitigation (for first generation packaging) is to work hard to keep your tests running fast, or use extension packages to modularize your code.
Net-new projects that expect to grow should consider Second Generation Packaging (2GP) since it's modular by design.
Uploads cannot be cancelled. Once the upload starts, it can't be cancelled. It often runs to the end even if a test fails.
It's unclear why an upload cannot be stopped.
Apex Test False Failures
When run concurrently, it's commonplace for valid Apex tests to fail, often due to row locking issues. If you are running the Apex tests interactively, a good approach is to run the tests in concurrent mode first, and then run the failed tests asynchronously (one-at-a-time mode). Here's a bookmarklet you can run from Chrome to select the tests which failed in the last run. First, open the Select Test dialog in the UI, and then launch the bookmarklet.
javascript:for(var a=document.querySelector(".x-grid-group").querySelectorAll("img.Failed"),b={},c=0;c<a.length;c++){for(var d=a[c];"TR"!==d.nodeName;)d=d.parentNode;b[d.querySelector("a").nextSibling.textContent.trim().replace(/\w+\./, "")]=!0}for(var f=document.querySelectorAll("#testOverlay .x-grid3-row:not(.x-grid3-row-selected)"),c=0;c<f.length;c++){var g=f[c].querySelectorAll("td");b.hasOwnProperty(g[1].textContent)&&(e=new MouseEvent("mousedown",{bubbles:!0}),g[0].querySelector(".x-grid3-row-checker").dispatchEvent(e))};
Pass the Security Review
"At Salesforce, nothing is more important than the security and success of our customers. To distribute a solution on AppExchange, it must pass our comprehensive security review. Learn how to design a solution security strategy and prepare for the security review."
Tips
Start with your most minimal, minimal viable product. As soon as the package is approved, you can bring on successive upgrades to improve on your MVP.
Salesforce does run automatic scans on your package, but they do not require you to submit every new version for another in-depth security review.
Use a TSO for Ongoing Security Reviews. If you plan to keep your listing current, it can be worthwhile to setup a Trialforce Source Org to use with the security reviews. You can then spin off trials when you submit for an updated review.
Publish Your Offering on the AppExchange
"You turned an idea into a solution and are ready to get it in front of customers. To publish your solution on AppExchange, use the AppExchange publishing console. The publishing console is where you create a listing for your solution, connect orgs, manage license settings, and view analytics for your published listings."