We are developing a managed package in Salesforce. In our package, we are providing a set of functionalities but we have to allow customers to have their specific logic in the execution flow.

For our case, we were thinking on using global virtual classes that the customer can extend and inject into the managed package core method. An example would be:

// Managed package main class
global class AccountProcessor {
    // set a default loader for accounts
    private static AccountsLoader m_accountsLoader = new AccountsLoader();
    global static void setAccountsLoader(AccountsLoader extension) {
        m_accountsLoader = extension;

    // main processing method
    global static void ProcessAccounts() {
        // use the accounts loader to retrieve accounts
        List<Account> lAccounts = m_accountsLoader.getAccounts();
        // do some processing/logic

Base dependency

// accounts loader core implementation. Classes are virtual by default
global class AccountsLoader {
    // we flag the method as virtual to be able 
    // to override it on a custom implementation
    global virtual List<Account> getAccounts() {
        return [select id,name from account];

With this approach, we let the customer to extend our core implementation:

global class AccountsLoaderExtension extends Namespace.AccountsLoader {
    global override List<Account> getAccounts() {
        return [select id,name where name = 'Test Account'];

And inject the new behavior to the managed package:

// on customer's code (non managed)
// we can set the loader on the managed package
Namespace.AccountProcessor.setAccountsLoader(new AccountsLoaderExtension());
// and call the code that does the logic

We have some questions regarding this approach:

  1. This approach is intended to be used in a managed package uploaded to the AppExchange. Is this approach accepted in the security review?

  2. Are there any security issues regarding this approach that can arise during the security review?

1 Answer 1


Generally speaking, yes, this is allowed, but usually gets additional questions asked during the review process (e.g. to make sure that such functionality is necessary and/or desirable). Note that this additional scrutiny isn't specific to global virtual classes; anything marked global tends to get looked at a bit closer.

Your current code, as written, won't pass security review. You'll want to read the notes in the Requirements Checklist, but the most important rule is this:

Controllers retrieving user-specified objects as well as global classes must always use sharing.

You'd also probably get dinged for having an unrestricted query that may exceed governor limits; it should include some non-trivial filter or limit. You can void both of these possible objections by using an interface instead.

Your current implementation forces a new class for each thing that they want to do (or a rather annoying mix of inner classes), while interfaces allow easier development for your clients.

Consider this example:

global interface AccountsLoader {
  global Account[] getAccounts();

Your code is easier to read and write; presumably, you'll need to check if your input is non-null anyways, returns correct information, etc, so interfaces are easier to write.

As far as your clients go, they then simply write this:

global class AccountsLoaderExtension implements Namespace.AccountsLoader {
    global Account[] getAccounts() {
        return [select id,name from account where name = 'Test Account'];

They also get the benefit of writing slightly less complicated code, and they can also mix together multiple types:

global class MultiLoaderExtension implements Namespace.AccountsLoader, Namespace.LeadsLoader {
    global Account[] getAccounts() {
        return [select id,name from account where name = 'Test Account'];
    global Lead[] getLeads() {
        return [select name from lead where name = 'test lead'];
  • Many thanks! I think I over-simplified the code for the sake of the example. We already know about this checklist and have our code following these guidelines, sorry for the misleading example.
    – Ruben B
    Feb 26, 2018 at 12:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .