September 13, 2026
Building an AppExchange-ready application
By Techventive Editorial
A managed package that works well in a single developer org can behave differently once installed in a customer's production environment: sitting next to other packages, subject to the same governor limits as everyone else, and expected to keep working through every future upgrade. Building an AppExchange-ready application means treating packaging, versioning, and security review as design constraints from the first commit.
What a managed package is, and why 2GP
A managed package is Salesforce's container for the Apex classes, Lightning components, and other metadata an application installs into a customer org. Salesforce enforces stricter rules on managed packages than on unpackaged code, because the vendor, not the customer, controls what ships in each version.
Second-generation managed packaging (2GP), Salesforce's current source-driven packaging model, treats source code in version control as the source of truth rather than a packaging org, so it can be versioned like any other software project. Starting a new application on 2GP avoids a later migration off an older packaging model.
Designing the data model and namespace
Every package is registered with a namespace, a one-to-15-character alphanumeric identifier attached to its custom objects and fields, so two vendors can both ship a field called Status__c without colliding in a shared org. That prefix appears in every API reference to the package's components, so choosing it carefully before the first release matters more than most early calls.
The data model deserves the scrutiny a standalone product would get: which objects are custom versus which standard objects get extended, and how the model behaves once a customer's own fields and automation sit alongside it. A model that assumes it owns the org is harder to support than one designed to coexist.
Configuration through custom metadata and custom settings
Hard-coded values do not survive contact with a real customer base, because every org customizes something. Custom metadata types, records that behave like metadata rather than data, deploy automatically with a package installation, which makes them the standard way to ship configuration, such as default settings or reference data, that a package needs from day one.
Custom settings differ: they support org-wide or hierarchical values scoped to a profile or user, useful for lightweight per-org configuration an admin adjusts after install, though their record data is not carried by the package the way custom metadata records are. Deciding which mechanism fits which setting early avoids a rebuild later.
Permission sets and licensing
Permission sets grant the object, field, and feature access a package's users need without touching the profiles a customer already manages, which is why they are the standard way to control access inside a managed package.
The License Management App (LMA), Salesforce's tool for ISVs, independent software vendors building on the platform, tracks installs and manages AppExchange licenses: it creates a lead and a license record per install, and lets a vendor activate or deactivate licenses. Wiring licensing through the LMA early avoids manual work once there are many customers.
Apex design under governor limits
Salesforce enforces the same per-transaction governor limits on packaged code as on anything else: 100 SOQL, Salesforce Object Query Language, queries in a synchronous transaction, 200 in an asynchronous one, and a combined 50,000 rows returned across those queries in one transaction.
Bulkification, writing methods that process a list of records instead of one, is not optional for a packaged application, because the code runs inside whatever transaction the customer's own automation already started, alongside triggers and flows outside the package's control. Asynchronous patterns such as Queueable and Batch Apex create more room under the limits for heavier processing.
The public surface: global classes, Apex REST, versioning
Only classes and methods marked with the global access modifier are visible outside the package, so marking something global is a decision to support it indefinitely, not a default to leave on. Apex REST endpoints, Salesforce's framework for exposing Apex as web services through the @RestResource annotation, require the class declared global and its methods global static, with a URL mapping such as /services/apexrest/yourns/v1/Cases carrying the version in the path.
Once a global element ships in a package version, it cannot simply be deleted. It can be marked deprecated while it keeps working, which is why a new major version of an endpoint should get a new path or class rather than changing the behavior under a signature customers already depend on.
Upgrade safety
A post-install script, an Apex class implementing Salesforce's InstallHandler interface, runs automatically when a customer installs or upgrades the package, making it possible to set default configuration or migrate data based on which version the customer is coming from.
Upgrades run against real customer data, so testing an upgrade path from a prior version, not only a fresh install, catches problems a clean-org test misses. Combined with the rule that global elements cannot be removed once shipped, upgrade safety is part of the release process for every version.
Test coverage and quality gates
Salesforce requires at least 75 percent of an org's Apex code covered by unit tests before deployment to production, and packaging for the AppExchange or as an unlocked package carries the same requirement, with every trigger needing at least one line of coverage. Meeting the number is necessary but not sufficient: tests should exercise bulk and negative cases, not only the happy path that raises the percentage.
Security review
Every package listed on AppExchange goes through Salesforce's security review before publication, and a significant update to an already-listed package can trigger another review. Reviewers look for issues including CRUD and field-level security violations, SOQL injection, cross-site scripting, and weak authentication, using a mix of automated and manual testing.
A package that already enforces sharing rules, validates input, and keeps credentials out of Apex code through Named Credentials starts the review from a stronger position. Treating security review as a design constraint from the first commit is what keeps the review from turning into a rebuild.
The pre-listing checklist
- Package is built on 2GP with source control as the system of record.
- Namespace is registered and used consistently across custom objects and fields.
- Configuration ships through custom metadata or custom settings, not hard-coded values.
- Access runs through permission sets, with licensing wired through the LMA.
- Every list-based Apex operation is bulkified and tested against governor limits.
- Only deliberately supported classes and methods are marked global.
- API paths carry an explicit version, and old versions keep working after a new release.
- Post-install script is tested against upgrades from a prior version, not only a fresh install.
- Apex test coverage is at least 75 percent, covering bulk and negative cases.
- Sharing enforcement, input validation, and credentials are ready for security review.
Most of this is easier with a team that has taken a Salesforce application through packaging and security review before.
Related reading
