The Short Answer
For almost every new use case in 2026, use Custom Metadata Types. Custom Settings exist for legacy reasons and for one specific capability Custom Metadata still lacks. Unless you have a specific reason to choose Custom Settings, default to Custom Metadata.
That’s the short answer. The rest of this article is the long answer.
What Each One Is
Custom Metadata Types are deployable, queryable, and access-controlled records that behave like configuration. They version with your metadata, move between orgs via deployment, and are read from Apex without DML limits.
Custom Settings come in two flavors: List and Hierarchy. List settings are org-wide key-value records. Hierarchy settings resolve values at multiple levels (org, profile, user) with override semantics.
Both are “configuration records.” The difference is how they deploy, how they’re queried, and what they can do that the other can’t.
Deployment and Packaging
Custom Metadata: fully deployable. Records themselves are metadata. Deploy via change sets, unlocked packages, or source control. Move config from dev → staging → prod exactly like code.
Custom Settings: the definition is deployable, but the data is not. You must manually enter data in each org after deploying the object, or write Apex to seed it.
This difference is the single biggest reason Custom Metadata wins for most use cases. Production deploys shouldn’t require “and then log in and fill out a form with these 40 values.” Custom Metadata eliminates that step.
Queryability From Apex
Custom Metadata:
Feature_Flag__mdt flag = Feature_Flag__mdt.getInstance('EnableNewCheckout');
Boolean enabled = flag.IsActive__c;
// Or SOQL:
List<Feature_Flag__mdt> flags = [SELECT DeveloperName, IsActive__c FROM Feature_Flag__mdt];
Queries do not count against SOQL governor limits. This is a subtle but critical advantage — a config lookup in a hot path never pushes you over the 100-SOQL limit.
Custom Settings (List):
Feature_Flag__c flag = Feature_Flag__c.getValues('EnableNewCheckout');
Boolean enabled = flag.Is_Active__c;
List setting lookups also don’t count against SOQL limits.
Custom Settings (Hierarchy):
Feature_Flag__c flag = Feature_Flag__c.getInstance();
// Returns the effective record for the current user, cascading user → profile → org
Hierarchy resolution is the one capability Custom Metadata cannot natively replicate. If you need config that’s different per user or per profile, use a Hierarchy Custom Setting.
The Hierarchy Argument
Hierarchy Custom Settings let you set:
- An org-wide default value.
- An override per profile.
- A further override per user.
getInstance() returns the most specific value for the current user. No manual cascade logic.
Custom Metadata does not have a built-in hierarchy. You can emulate it by adding Profile lookup fields and writing resolution logic in Apex, but that’s manual work.
If user-level overrides genuinely matter — feature toggles by profile, rate limits by user — Hierarchy Custom Settings are the cleaner fit. Accept that your config data won’t deploy cleanly.
Access Control
Custom Metadata: you can configure field-level read access per profile; the metadata is accessible to Apex code regardless. There is no DML from user context on managed types unless explicitly permitted.
Custom Settings: List settings have an “Enable Public” flag that skips sharing entirely (for performance). Hierarchy settings inherit from the user record. Both are more permissive by default — which is fine for config, usually.
Packaging (Managed / Unmanaged)
Custom Metadata: packages cleanly. Records are part of the package install. ISVs use this heavily.
Custom Settings: object packages, data doesn’t. This is why ISV apps that need out-of-the-box config trigger post-install scripts to seed Custom Setting data.
Performance
Both are cached after first access. For hot-path reads, the difference is negligible.
For very large config datasets (thousands of records), Custom Metadata has slightly more overhead because it participates in the metadata cache, which has its own eviction policies. For small datasets (under a few hundred records), indistinguishable.
DX and Source Control
Custom Metadata records in the DX project format are .md-meta.xml files. Friendly to Git diff. Reviewable in PRs.
Custom Setting data — because it’s not metadata — doesn’t live in source control without a separate data-seeding script. Managing config drift across environments becomes a manual discipline.
Concrete Decision Table
| Scenario | Choose |
|---|---|
| Feature flags deployed across sandboxes | Custom Metadata |
| Per-user override of feature flags | Hierarchy Custom Setting |
| App-wide config with 20 values | Custom Metadata |
| Integration endpoint + credentials | Named Credential + Custom Metadata |
| Lookup tables (country codes, currency codes) | Custom Metadata |
| Per-profile default record type | Hierarchy Custom Setting |
| Configuration driven by admins in production | Custom Metadata (with CRUD UI) |
| Small reference table queried in triggers | Either — Custom Metadata preferred |
When to Migrate From Custom Settings
Projects still stuck on Custom Settings (List type) for deployable config should migrate. Benefits:
- Config becomes part of your deployment pipeline.
- No more “oh, we forgot to update the production setting” incidents.
- Cleaner change tracking.
Migration steps:
- Create a Custom Metadata Type with equivalent fields.
- Seed records from existing Custom Setting data (Apex script or manual).
- Update code to read from Custom Metadata.
- Deploy and verify.
- Deactivate old Custom Setting (don’t delete immediately — keep for rollback).
Do not migrate Hierarchy Custom Settings with actual per-user overrides unless you’re also reimplementing the cascade in Apex.
Admin Editability
Admins can edit Custom Metadata records directly in Setup. The interface is straightforward; restricted by field-level security.
For config owned by business users (not admins), consider building a Lightning UI. Custom Metadata records can be created/updated from Apex, so you can wrap a user-friendly form around them.
Frequently Asked Questions
Can Custom Metadata be updated from a standard user?
Not by default. DML on Custom Metadata requires the Customize Application permission or explicit Apex orchestration. Most users read; admins write.
Do Custom Metadata queries consume SOQL limits?
No. getAll(), getInstance(), and SOQL against *__mdt types do not count against the synchronous SOQL limit.
Is there a record count limit?
Yes — Custom Metadata records have storage limits (in records per type). For a few hundred records per type, you’re comfortable. For many thousands, re-evaluate whether Custom Metadata is the right container.
Can I query Custom Metadata from Flow?
Yes. Custom Metadata types are available as resources in Flow. Use Get Records or direct references.