Before writing the first line of code, run through the native versus composite decision so you know what you are committing to support for the next five years.
- List every data store the feature needs
Write down every entity the app will persist. If all of them can live in custom objects, Big Objects, or files attached to records, you are still in native territory. Any requirement for a relational schema with joins Salesforce cannot express, or for storage beyond the Files cap, pushes you to composite.
- Map each logic step to a platform primitive
Walk the user story step by step and label each step Apex, Flow, validation rule, formula field, or external call. If more than one step needs an external call to function (not just enrich), you are composite. One outbound webhook on success is still native.
- Estimate the governor-limit cost
Add up SOQL queries, DML statements, CPU milliseconds, and heap usage per user transaction. If a single user action could consume more than 20 percent of an org's per-transaction limits, redesign with async patterns or move to composite.
- Confirm the AppExchange security review path
Native packages run through a standard security review that takes 4 to 8 weeks. Composite packages need additional review of the external service's security posture. Plan timeline accordingly and budget for a Checkmarx scan, a Burp Suite penetration test, and a remediation cycle.
All data in subscriber org, all logic in Apex or Flow. Lowest install friction, highest partner support burden, qualifies for AppExchange "native" badge.
Salesforce holds the system of record, external service handles compute-heavy steps or specialized UI. Iframe-style canvas brings the external UI into Lightning.
External system calls Salesforce via OAuth for read/write. No UI in Salesforce. Suits ETL, BI tools, and middleware.
Modern composite pattern. Salesforce invokes a serverless function or Heroku service for heavy compute, gets results back, persists natively.
- Native does not mean "uses no internet". You can still make outbound callouts for optional integrations and stay native, as long as the core flow works without them.
- A managed package cannot be uninstalled if customer data references its custom objects. Customers must delete the data first. Document this in your install guide.
- Namespaces are permanent. Choose a short, distinctive namespace prefix; you cannot change it after the first package version is uploaded.
- Second-generation packaging (2GP) gives you more flexibility than 1GP for component deletion and namespace handling. Default to 2GP for any new native app.