Skip to main content

Salesforce Basics: Custom Metadata Types vs Custom Settings vs Custom Labels

๐Ÿ’ฌ In plain words: Three lookalikes: Custom Metadata = configuration that DEPLOYS with your code (best for app settings). Custom Settings = org/user-specific values, changeable at runtime (hierarchy type is great for bypass switches). Custom Labels = translatable text for the UI.
๐Ÿ“Œ Example: API endpoint URLs per environment → Custom Metadata (deploys with code, sandbox vs prod values). A Bypass_Automation__c checkbox an admin flips during data load → hierarchy Custom Setting. The word 'Submit' translated to Hindi → Custom Label.
๐ŸŽฌ Real-Life Example: The Fee Table Trapped Inside Code

Skyline charges a different delivery fee per city. Apex needs those rates on every booking.

The Old/Bad Way: if (city == 'Delhi') fee = 50; else if (city == 'Mumbai') fee = 65; … Every rate change is a code change, a test run, and a deployment.

Why this is bad: Business data is trapped in code. Admins cannot touch it. Sandboxes drift from production. A price change on Friday waits for a release window.

The New/Good Way:

  1. Create a Custom Metadata Type: Fee_Rate__mdt with City__c and Rate__c fields.
  2. Add one record per city. Apex reads them with zero SOQL-limit cost.
  3. The records are metadata — they DEPLOY with your package, so every sandbox carries the same table.
  4. Need per-user or per-org runtime values instead? That is what a hierarchy Custom Setting is for.

The payoff: Configuration is data about the app. Store it as metadata the admin can edit — never as if-else branches only a deploy can change.

๐Ÿง  Deploys, changes, translates: Metadata DEPLOYS with code. Settings CHANGE at runtime. Labels TRANSLATE text. Pick by which of the three you need.

๐Ÿ”— Connects to: Migration bypass flags are built on these  ·  Custom Metadata ships inside packages; Settings do not

Concept

All three keep configuration out of code.

  • Custom Metadata Types (CMT) are METADATA: The records deploy in packages and change sets. You read them with getAll() or getInstance(), and those reads do not count against SOQL limits. They support relationships and serve as your default for app configuration and rule engines.
  • Custom Settings are DATA: List settings are org-wide; Hierarchy settings resolve org, then profile, then user overrides.
  • Hierarchy Custom Settings are the only ones you can use directly in formulas and validation rules via $Setup.
  • Custom Labels are translatable text: Used specifically for UI strings.

Two smaller cousins belong here because both keep text out of code: A Global Value Set is a single picklist value list held once and shared across multiple picklist fields, updating every field when a value is added. Translation Workbench translates labels, picklist values, and error messages per user language. Custom Labels feed directly into Translation Workbench, which is the true reason to put user-facing text in a label rather than hard-coding it.

๐Ÿงญ 360 Card — Metadata vs Settings vs Labels

Rule: Metadata deploys. Settings change at runtime. Labels translate.

Gain: Custom Metadata reads are free. getAll() and getInstance() cost no SOQL limits. Records travel cleanly inside packages and change sets.

Reach for: A Custom Label first if it is display text. A hierarchy Custom Setting when a value must differ per profile or per user at runtime. Custom Metadata when config must DEPLOY with the app, needs relationships, or is read in a loop (zero SOQL cost). A plain custom object only when business users must edit records using normal permissions and you accept the SOQL cost.

Price: Custom Metadata is metadata. Changing it in production requires the Customize Application permission. Updating it programmatically requires an async Metadata API deployment rather than DML.

Limits: Hierarchy Custom Settings resolve org → profile → user, cached with no SOQL cost. List Custom Settings do not deploy their data rows. Custom Labels are text-only, up to 5,000 per org.

Mirror — a plain custom object for config: Business users can edit it using normal permissions, but you pay a SOQL query on every read, and records do not deploy automatically with app releases.

Later: This choice determines how migration bypass switches are structured and what your packages can carry.

At volume: Reading configuration inside a loop is a classic limit killer. Read once and hold the result in a static variable.

Core Q&A

Q: When do you choose Custom Metadata over a Custom Setting, and when is it the other way around?
๐ŸŽฏ Say this first: Custom Metadata for config that deploys with code and differs per environment. Custom Settings (hierarchy) for values you change at runtime, per user or profile — like bypass flags.

A: Default to Custom Metadata Types (CMT). It deploys with the app, eliminating post-deploy data loads. It is packageable, cached, and costs nothing against SOQL limits, making it ideal for feature flags, integration endpoints, business rules, and trigger-bypass switches.

  • Choose a Hierarchy Custom Setting in two specific cases:
  • When you need a PER-USER or PER-PROFILE override (CMT cannot do this).
  • When you need to reference $Setup in a formula or validation rule.
  • List Custom Settings: Rarely chosen now since CMT covers most list scenarios. Their remaining edge is that Apex can create and edit list custom settings synchronously at runtime via DML. CMT cannot do this synchronously because updates go through the async Metadata API.

Follow-ups (Scenario-Based)

Q1: Your trigger framework needs a 'bypass automation for this user during data load' switch. Which mechanism and why?

A1: Use a Hierarchy Custom Setting with a checkbox field.

  • The admin applies the override to the specific integration or migration user.
  • Apex reads it efficiently with zero SOQL cost from the cache.
  • Validation rules honor the exact same switch declaratively using $Setup.
  • One mechanism gates both programmatic Apex and declarative rules cleanly.
  • CMT cannot express a per-user override, and a custom object would require a SOQL query in every transaction.
Q2: A rule engine stores dozens of business rules as CMT records. What breaks when business users ask to edit rules themselves in production?

A2: CMT records are metadata. Editing them directly requires the Customize Application system permission, which is too broad to grant business users. Furthermore, updating CMT records from Apex is an asynchronous metadata deployment, not synchronous DML.

  • Option 1: Build a custom admin UI in front of the CMT that executes Metadata API calls under a service context.
  • Option 2: Move business-editable rules to a custom object (real data with standard CRUD/FLS permissions) while keeping CMT for deployment-time configuration.
  • Architect Note: Distinguishing 'application configuration' from 'business-managed data' is the core architectural decision.

Recommended Reading: Record Types & Page Layout Strategy