Skip to main content

SF-0297 · Concept · Medium

What is Hierarchy Custom settings?

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

A Hierarchy Custom Setting holds one resolved record per user, picked by a cascade: User → Profile → Organization. The most-specific match wins. The pattern is built specifically for cases like feature flags, per-profile thresholds, and individual override switches — config that varies by who’s running the code.

How the cascade works

When you call MySetting__c.getInstance():

  1. The platform looks for a record whose SetupOwnerId is the current user’s Id. If one exists, return it.
  2. Otherwise, look for a record whose SetupOwnerId is the current user’s Profile Id. If one exists, return it.
  3. Otherwise, return the Organization-wide default record.

You always get something (assuming an Org default exists). The cascade hides the details — your Apex code just does getInstance() and gets the right values.

The Setup UI

When you edit a Hierarchy Custom Setting in Setup, you see three sections:

  • Default Organization Level Value — the fallback for everyone.
  • Profile-Level Values — per-profile overrides.
  • User-Level Values — individual-user overrides.

Each user record overrides each profile record overrides the org default.

A worked example

Custom Setting: Trigger_Settings__c with one checkbox Account_Trigger_Enabled__c.

  • Org default: Account_Trigger_Enabled__c = true (the default).
  • Profile “Data Loader User”: Account_Trigger_Enabled__c = false (bypass triggers during bulk loads).
  • User vikas@example.com: Account_Trigger_Enabled__c = false (he’s debugging).
Boolean enabled = Trigger_Settings__c.getInstance().Account_Trigger_Enabled__c;
  • If Vikas runs the trigger, enabled = false (user-level wins).
  • If another Data Loader User runs it, enabled = false (profile-level wins).
  • If anyone else runs it, enabled = true (org default).

The three accessor methods

// 1) The resolved value for the running user (uses the cascade)
Trigger_Settings__c mine = Trigger_Settings__c.getInstance();

// 2) Force the org default, ignoring user/profile overrides
Trigger_Settings__c org = Trigger_Settings__c.getOrgDefaults();

// 3) The resolved value for a specific user (uses the cascade for that user)
Trigger_Settings__c other = Trigger_Settings__c.getInstance(someUserId);

getOrgDefaults() is useful in tests, async jobs, and any context where you don’t want individual-user behaviour to leak in.

Use cases

ScenarioWhy hierarchy fits
Feature flags admins can enable per-userPer-user override on top of the org default
Trigger bypass switches while debuggingUser-level toggle without code change
Per-profile thresholds (different teams, different rules)Profile-level override
Region-specific integration endpointsOrg default per environment; per-profile if regional teams need overrides

Common pitfalls

  • Forgetting to seed the org defaultgetInstance() returns an empty record (all nulls) if no default exists. Calling .Account_Trigger_Enabled__c on a null record returns null, not the platform’s idea of “the default true.” Always seed the Org default in deployment scripts.
  • Trying to manage these via apex DML in production with concurrent writes — record locks apply just like any object.
  • Testing in @isTest without seeding the settingTest.startTest() doesn’t auto-populate settings. Insert what you need in @TestSetup.

When NOT to use Hierarchy

  • The config never varies — use Org default only with getOrgDefaults(), or move to Custom Metadata Types.
  • The config is a list of records — use List Custom Settings or Custom Metadata.
  • You need to deploy the records along with the code — switch to Custom Metadata Types.

What interviewers are really looking for

The naming check: User → Profile → Org cascade. The senior signal includes: (1) the three accessors (getInstance(), getOrgDefaults(), getInstance(userId)) and when to use each, (2) SetupOwnerId is the field that decides which level a record belongs to, (3) you must seed the Org default or getInstance() returns a record with null fields, (4) it’s the right tool when admins need to toggle behaviour for individual users without a code change, and the wrong tool when the config is purely static (use Custom Metadata).

Verified against: Salesforce Help — Hierarchy Custom Settings. Last reviewed 2026-05-17.