DEV Community

Rohan Mehta
Rohan Mehta

Posted on

Salesforce Custom Metadata Types vs Custom Settings: Which One to Use and When

Salesforce Custom Metadata Types vs Custom Settings: Which One to Use and When

Every Salesforce org accumulates configuration that code needs to read: feature flags, endpoint URLs, threshold values, mapping tables. The two classic places to store that configuration are Custom Settings and Custom Metadata Types. They look similar, they are often confused, and picking the wrong one creates headaches that only show up months later — usually during a deployment.

Here is the practical difference, and a simple rule for choosing.

Custom Settings: configuration that lives in the org

Custom Settings store data that is specific to one org. There are two kinds: List settings (a set of records) and Hierarchy settings (values that cascade from org level down to profile and user level).

The hierarchy cascade is the feature that keeps Custom Settings alive. If you need a value that differs per user or per profile — say, a debug flag that is on for admins but off for everyone else — Hierarchy Custom Settings do that with no code.

But Custom Settings have real limits. Their records are data, not metadata, so they do not travel with change sets or packages. You have to recreate them in every sandbox and in production, by hand or with a data load. They are also invisible to the Metadata API, which means your deployment pipeline cannot see them. And there is a hard storage limit: Custom Settings records count against your org's data storage, not metadata storage.

Custom Metadata Types: configuration that deploys like code

Custom Metadata Types (CMDT) store configuration as metadata. Their records are created in Setup, but they behave like code artifacts: they deploy with change sets, unlocked packages, and the Metadata API. What you build in a sandbox arrives in production exactly as defined — no manual recreation, no data loader, no forgotten record.

CMDT records also support relationships to other metadata, and since a few releases back they support protected records in managed packages, which is why most ISVs prefer them. You can reference a CMDT record directly from a formula, a Flow, or Apex with a simple query, and you can even use them in validation rules.

The trade-off: CMDT records cannot be created or edited by Apex at runtime, and there is no per-user or per-profile cascade. A value is the same for everyone in the org. If your code needs to change a value on the fly — a counter, a last-run timestamp, a toggle flipped by a scheduled job — CMDT is the wrong tool.

The decision rule

Ask one question: does this value change at runtime, or only at deployment time?

  • It changes at runtime (a counter, a timestamp, a switch an admin flips in production): use Custom Settings, or better, a custom object with a single record. CMDT cannot be written by Apex, so it cannot hold live state.
  • It changes only when you deploy (endpoint URLs, feature flags per release, mapping tables, thresholds): use Custom Metadata Types. They travel with your code, they are version-controlled, and they never get forgotten in a sandbox.
  • It differs per user or profile: use Hierarchy Custom Settings. CMDT has no cascade, so this is the one case where Custom Settings still win outright.

A common real-world pattern is to use both: CMDT for the stable configuration (which endpoint, which threshold) and a Custom Setting or custom object for the live state (last sync time, current toggle). They complement each other; they are not rivals.

Migration notes if you are still on Custom Settings

If your org still runs on List Custom Settings for deployment-time configuration, migration is straightforward: create the CMDT object with the same fields, recreate the records as metadata, and update the Apex or Flow references. The query syntax changes slightly — MySetting__mdt.getInstance('Name') replaces the Custom Settings getInstance() call — but the logic stays the same. Do it one setting at a time, and keep the old setting around until the new one is proven in production.

One more thing to check before you migrate: make sure your test classes still cover the code that reads the configuration — a changed query can quietly drop coverage below the 75% deployment gate. And if the configuration drives automation, keep the run context of your Flows in mind: a Flow running in system context sees CMDT values the same way a user-context Flow does, but the records it touches afterwards may not.

The short version

Custom Metadata Types are the default for configuration that deploys with your code. Custom Settings survive for two jobs: per-user or per-profile values, and values that Apex needs to write at runtime. Everything else belongs in CMDT — your future self, deploying at 11 PM on a Friday, will be grateful.


Originally published at Way2Force

Top comments (0)