SfSkills Domains Docs GitHub

Home / devops / managed-package-development

managed-package-development

Building or maintaining Salesforce first-generation managed packages (1GP) for ISV distribution: namespace registration, packaging org structure, PostInstall/UninstallHandler Apex, push upgrades, Flow version management, subscriber org considerations. NOT for choosing between unmanaged, unlocked, 1GP and 2GP — use devops/package-development-strategy. NOT for 2GP packaging — use devops/second-generation-managed-packages.

Open the skill folder on GitHub


Managed Package Development

This skill activates when a practitioner needs to build or maintain a Salesforce first-generation managed package (1GP) for ISV AppExchange distribution. It covers the packaging org structure, namespace registration, the PostInstall Apex interface, the critical constraint that Flow versions in released packages cannot be deleted, and the differences between the packaging org and patch org model.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

Packaging Org and Namespace Structure

1GP managed packages use a dedicated packaging org model:

There is no concept of "Dev Hub" or "scratch org" in 1GP — all development happens directly in the packaging org.

PostInstall and UninstallHandler Interface

1GP managed packages can execute custom Apex logic when the package is installed, upgraded, or uninstalled in a subscriber org. This is implemented via the InstallHandler Apex interface:

global class MyPostInstall implements InstallHandler {
    global void onInstall(InstallContext context) {
        if (context.previousVersion() == null) {
            // Fresh install logic
        } else {
            // Upgrade logic — use context.previousVersion() to branch
        }
    }
}

The class implementing InstallHandler is registered in the Package Settings and its API name is locked after the first upload to a released package. Only the class body (logic) can be changed in subsequent versions.

For uninstall, implement UninstallHandler:

global class MyUninstall implements UninstallHandler {
    global void onUninstall(UninstallContext context) {
        // Cleanup logic
    }
}

Flow Version Management in 1GP

Flows in a 1GP managed package have a critical constraint: once a Flow is uploaded to a released or beta package, the Flow cannot be deleted from the packaging org. You can:

But you cannot delete the Flow itself from the packaging org after it has been included in a released package. This creates a permanent registry of all Flows ever shipped in the package.

Implication: Be deliberate about which Flows are included in a managed package. Test Flows thoroughly before including them in a managed release.


Common Patterns

Pattern 1: PostInstall Script for Configuration Seeding

When to use: The package requires default Custom Metadata records, Permission Set assignments, or Named Credentials to be created in the subscriber org at install time.

How it works:

  1. Implement InstallHandler in a global Apex class
  2. In onInstall(), check context.previousVersion() to distinguish fresh install from upgrade
  3. For fresh install: insert default data records, assign permission sets to installing user
  4. For upgrades: use version comparison to apply migration logic only for specific upgrade paths
  5. Register the class name in Package Settings > Post Install Class before the first released upload

Why not use Setup steps in subscriber org: PostInstall scripts run automatically at install time, reducing subscriber configuration burden. For ISV packages, automated post-install configuration is a quality-of-life feature that reduces support requests.

Pattern 2: Push Upgrade for Bug Fixes

When to use: A critical bug in a released package version needs to be fixed in all subscriber orgs without requiring subscribers to manually install the update.

How it works:

  1. Create a new package version with the fix in the packaging org
  2. Upload the version via Setup > Package Manager > Upload
  3. Navigate to Package Details > Push Upgrades and schedule the push
  4. Push upgrades are subject to subscriber org governor limits — the PostInstall script runs in the subscriber org and must stay within limits

Why not wait for subscriber voluntary install: Push upgrades ensure all subscribers receive critical security or data integrity fixes promptly. Voluntary upgrade adoption rates are often low.


Decision Guidance

SituationRecommended ApproachReason
New ISV managed package for AppExchange1GP packaging org with namespaceAppExchange requires managed package with IP protection
New internal modular package, no AppExchange2GP unlocked package2GP supports CLI-driven workflow and multi-package namespaces
Post-install configuration neededInstallHandler Apex interfaceRuns automatically at install/upgrade time in subscriber org
Bug fix across all subscribersPush upgrade from Package ManagerSubscriber voluntary installs have low adoption rates
Flow in package needs to be deletedNot possible after release uploadCan only deactivate/version — plan Flows before releasing
Package uploadSetup UI > Package Manager or Tooling API2GP CLI (sf package version create) does NOT work for 1GP

  1. Register namespace — In a dedicated Developer Edition org, register the namespace prefix (Setup > Company Profile > Namespace Prefix). Document the namespace — it is permanent.
  2. Configure packaging org — Import the namespace into the packaging org by linking it in Setup > Package Manager. Create the package in the packaging org.
  3. Develop in packaging org — Build all components (Apex, Flows, custom objects, layouts) directly in the packaging org. Test in the packaging org itself or a scratch org linked to it.
  4. Implement PostInstall/UninstallHandler — Write the InstallHandler Apex class and register it in Package Settings before the first released upload. Confirm the class name — it cannot change after the first released upload.
  5. Test Flows before including in package — Flows included in a released package cannot be deleted from the packaging org. Test all Flows thoroughly in a separate org before adding them to a managed release.
  6. Upload new package version — Use Setup > Package Manager > Upload in the packaging org. Set the version number and release type (Beta or Released).
  7. Plan push upgrades — For Released versions with bug fixes, schedule push upgrades via Package Details > Push Upgrades. Monitor upgrade status across subscriber orgs.

Review Checklist


Salesforce-Specific Gotchas

  1. PostInstall class name is locked after first released upload — The API name of the class implementing InstallHandler is registered in Package Settings. After the first Released package version is uploaded with this class, the name cannot be changed — only the body. Plan the class name before the first release.
  2. Flows in released packages cannot be deleted from the packaging org — Once a Flow is included in a Released or Beta managed package, it cannot be deleted from the packaging org. Only individual older versions can be removed (if a newer version exists). Be deliberate about which Flows are included in managed releases.
  3. sf package version create CLI is a 2GP command, not valid for 1GP — First-generation managed packages are uploaded via Setup UI (Setup > Package Manager > Upload) or Tooling API. The sf package version create command in the Salesforce CLI only works with second-generation (2GP) packages.

Output Artifacts

ArtifactDescription
Packaging org setup guideNamespace registration steps and packaging org configuration
PostInstall class implementationInstallHandler and UninstallHandler Apex with install/upgrade branching
Flow management policyWhich Flows are included in managed releases and version management strategy
Push upgrade planVersion selection, subscriber org impact, and rollout schedule

Package files