Develop and Distribute Apex for Managed Packages

As an independent software vendor (ISV) or Salesforce partner, you can distribute Apex code to customer orgs by using managed packages. For first-generation managed packages (1GP) and migrated second-generation managed packages (2GP), use versioning to evolve components of your managed package gracefully without breaking existing customer integrations. Understand how global Apex in managed packages behaves and learn how to develop global Apex in managed packages specifically for agents.

As you develop managed Apex, keep these points in mind.

  • The code contained in an Apex class, trigger, or Visualforce component that’s part of a managed package is obfuscated. You generally can’t view this code in an installing org. The only exceptions are methods declared as global. You can view global method signatures in an installing org. In addition, license management organization users with the View and Debug Managed Apex permission can view their packages’ obfuscated Apex classes when logged in to subscriber orgs via the Subscriber Support Console.
  • 1GP managed packages each have a unique namespace. 2GP managed packages can have the same namespace as other 2GP managed packages. However, you can’t associate a single 2GP managed package with more than one namespace. The namespace is prepended to your class’s names, methods, variables, and so on, which helps prevent duplicate names in a subscriber’s org.
  • You can use the @NamespaceAccessible annotation to make public Apex in a 2GP managed package available to other 2GP managed packages that use the same namespace.
  • In a single transaction, you can reference only 10 unique namespaces. For example, suppose that you have an object that executes a class in a managed package when the object is updated. Then that class updates a second object, which in turn executes a different class in a different package. Even though the first package didn’t access the second package directly, the access occurs in the same transaction. It’s therefore included in the number of namespaces accessed in a single transaction.
  • You can use the @Deprecated annotation to identify methods, classes, exceptions, enums, interfaces, and variables that can no longer be referenced in subsequent releases of the managed package in which they reside. This annotation is useful when you’re refactoring code in managed packages as the requirements evolve. See Deprecate Apex in Managed Packages.
  • For 1GP and migrated 2GP managed packages, you can write test methods that change the package version context to a different package version by using the System.runAs() method. See Testing Behavior in Package Versions.
  • You can’t add a method to a global interface or an abstract method to a global class after you upload that interface or class in a Managed - Released package version. If the class in the Managed - Released package is virtual, the method that you can add to it must also be virtual and must have an implementation. If the class in the Managed - Release package extends another class, you can’t remove the existing class’s contract. See Best Practices for Using Global Apex in Managed Packages.
  • Salesforce blocks managed package session IDs from authenticating anonymous Apex via Tooling API or SOAP API. Managed packages can’t use UserInfo.getSessionId() to obtain a session ID and then use the session ID to execute anonymous Apex. This update is available to package subscribers starting in Summer ’26 and is enforced in Summer ’27. See Block Execute Anonymous from Managed Packages (Release Update). Instead, we recommend that managed packages interact with subscriber org code through standard mechanisms, such as a shared global interface and Type.forName().

If a ConnectApi class has a dependency on Chatter, the code can be compiled and installed in orgs that don’t have Chatter enabled. However, if Chatter isn’t enabled, the code throws an error at run time. See Packaging ConnectApi Classes.

Note