Skip to main content

SF-0279 · Concept · Medium

What are setup and non setup objects?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

Salesforce splits its objects into two categories with very different transactional behaviour:

  • Setup objects — configuration: User, Profile, Group, GroupMember, QueueSObject, UserRole, PermissionSet, PermissionSetAssignment, ObjectPermissions, FieldPermissions, etc.
  • Non-setup objects — business data: Account, Contact, Opportunity, Case, Lead, custom objects (*__c), and every other day-to-day record.

You cannot do DML on both kinds in the same transaction — that throws the famous MIXED_DML_OPERATION error. Understanding the split is the only way to write code that touches users, profiles, or sharing rules without breaking.

The list — what counts as “setup”

The full list lives in the Salesforce help — Limitations of mixed DML, but the common ones an interview will probe:

  • User — every CRUD operation on the user record
  • UserRole
  • PermissionSet, PermissionSetGroup, PermissionSetAssignment
  • Profile
  • Group, GroupMember
  • QueueSObject
  • ObjectPermissions, FieldPermissions, SetupEntityAccess
  • CollaborationGroup and CollaborationGroupMember (Chatter groups)
  • Territory, UserTerritory

If it controls who can do what, it’s almost certainly a setup object.

Why the rule exists

When you save business data (Account, Contact), the platform applies sharing rules. Sharing rule evaluation depends on the user/group/profile graph. If you could change a user’s profile and save an Account record in one transaction, you’d hit a chicken-and-egg problem: which version of the sharing rules do we use to evaluate the Account save — the old one, or the one with the new profile?

The platform sidesteps the whole question by forbidding mixed DML in a single user-facing transaction.

The error in action

// Throws System.DmlException: MIXED_DML_OPERATION
@isTest
static void mixedDmlInOneTx() {
    User u = TestUtils.makeUser();
    insert u;
    Account a = new Account(Name = 'Acme');
    insert a;
}

The four exceptions to the rule

There are four cases where mixed DML is permitted:

  1. Asynchronous Apex@future, Queueable, Batch, Scheduled. Each async transaction is fresh, so the platform doesn’t care about ordering against the parent.
  2. The five recognised “user-related” objectsUser field updates can sometimes coexist with non-setup DML as long as the rest of the rules are followed. This is narrow and version-dependent; assume mixed DML is forbidden unless your test proves otherwise.
  3. Test methods using System.runAs() — wrapping the setup-object DML in System.runAs(adminUser) { ... } is one of the standard test-class workarounds.
  4. Group membership and queue assignment in test context — special exceptions documented in the Help article.

The standard workaround: defer to @future

public class UserProvisioning {
    public void onboard(Id contactId) {
        Account a = new Account(Name = 'New Customer');
        insert a;                              // non-setup

        provisionUserAsync(contactId);         // setup, in a different tx
    }

    @future
    public static void provisionUserAsync(Id contactId) {
        Contact c = [SELECT Id, Email FROM Contact WHERE Id = :contactId];
        User u = new User(
            Username = c.Email + '.uniq',
            Email    = c.Email,
            // ...
        );
        insert u;
    }
}

The insert u runs in a fresh transaction once the parent commits, so the rule isn’t violated.

In test methods: System.runAs

@isTest
static void mixedDmlInTest() {
    User admin = [SELECT Id FROM User WHERE Profile.Name = 'System Administrator' LIMIT 1];
    System.runAs(admin) {
        User u = TestUtils.makeUser();
        insert u; // setup DML
    }
    Account a = new Account(Name = 'Acme');
    insert a;     // non-setup DML — allowed because the user insert was inside runAs
}

System.runAs does more than change the running user — it also opens a sub-transaction boundary that lets test code do mixed DML legally.

Which objects don’t support DML at all?

A separate question — see “objects that do not support DML” — but worth distinguishing. Setup objects do support DML; they just can’t be mixed with non-setup DML in one transaction. AggregateResult, for instance, doesn’t support DML at all.

What interviewers are really looking for

The signal is knowing why the split exists — sharing rule evaluation order — not just memorising the error name. Bonus points for naming the four workarounds: @future, Queueable, Batch, and System.runAs in tests. Mention PermissionSetAssignment as a common setup object newcomers forget to put on the list.

Verified against: Salesforce Help — Limitations on Mixed DML, Apex Developer Guide — @future Methods. Last reviewed 2026-05-17.