Both custom metadata types and custom settings store configuration. The decisive difference is how the records move between orgs: custom metadata records flow through metadata deployments alongside the schema, while custom settings records do not. That single fact drives almost every other decision between the two.
Side-by-side
| Aspect | Custom Metadata Types (__mdt) | Custom Settings (List or Hierarchy) |
|---|---|---|
| API suffix | __mdt | __c (like custom objects) |
| Records are metadata? | Yes — deploy with change sets / packages | No — must be migrated as data |
| Runtime writes from Apex? | No — use Metadata API (async) | Yes — standard DML |
| Cached read (no SOQL limit)? | Yes — getAll() / getInstance() | Yes — getAll() / getInstance() / getOrgDefaults() |
| Relationships | Metadata relationships to other CMDT/objects | Limited; no master-detail |
| Per-user/profile values | No (would need to build manually) | Yes — Hierarchy type natively supports it |
| Reportable? | No | No (with rare exceptions) |
| In packages | Records ship with the package | Schema ships; records do not |
| Best for | Config that should travel with code | Per-user toggles, runtime-writable counters |
The deployment difference (the headline)
When you create an Integration Endpoint record in dev as a custom metadata record, you can include it in a change set, deploy it to UAT, then deploy to production. The record is XML in your repo. Three orgs, same record, no scripts, no data loader.
The same scenario with a custom setting requires:
- Export the records from dev (data loader, anonymous Apex, or workbench).
- Re-create them in UAT.
- Re-create them in production.
- Hope nobody forgot to do steps 2-3.
This is why CMDT has become the default for new configuration in modern Salesforce projects. Custom settings predate CMDT (CMDT was introduced in Winter ‘15) and are slowly being deprecated as the recommended approach for application configuration.
The write-path difference
// Custom setting — write at runtime with normal DML
Integration_Config__c cfg = Integration_Config__c.getInstance();
cfg.Last_Sync__c = System.now();
upsert cfg;
// Custom metadata — write via Metadata API, async
Metadata.DeployContainer container = new Metadata.DeployContainer();
Metadata.CustomMetadata cm = new Metadata.CustomMetadata();
cm.fullName = 'Integration_Endpoint.Stripe';
cm.values.add(new Metadata.CustomMetadataValue(
field = 'Endpoint_URL__c', value = 'https://new.url'
));
container.addMetadata(cm);
Metadata.Operations.enqueueDeployment(container, null);
Custom settings can be written from Apex synchronously. Custom metadata cannot. If your use case writes records at runtime — usage counters, last-sync timestamps, per-user preferences — custom settings are the only option among the two.
When to use which
Use Custom Metadata Types when:
- Configuration moves with code releases (feature flags, integration endpoints, routing rules).
- Admins should rarely or never edit values at runtime.
- You want records in the package so subscribers get them automatically.
- You want cached, no-SOQL-cost reads.
Use Custom Settings when:
- You need to write at runtime from Apex (counters, timestamps, audit markers).
- You need per-user or per-profile values (Hierarchy type handles this natively).
- The values are genuinely runtime data that shouldn’t ship with code.
Use neither when:
- The data is user-facing and needs list views, reports, or related lists → use a custom object.
- The value is a single org-wide string → consider a Custom Label for translatability.
Hierarchy vs List custom settings
Custom Settings come in two flavours, and the question often combines them:
| Type | Granularity | API |
|---|---|---|
| List | One value per record key | MyConfig__c.getValues('MyKey') |
| Hierarchy | Org default + per-profile + per-user override | MyConfig__c.getInstance(userId) |
Hierarchy is the closest thing in Salesforce to “per-user feature flags” without writing logic yourself.
What interviewers are really looking for
The shorthand answer is “metadata vs data.” The full answer hits three points: deployment (records-as-metadata is the headline advantage), write semantics (DML vs Metadata API), and Hierarchy custom settings as the one use case CMDT cannot replicate. Mention that CMDT is the modern default for configuration that ships with code, and you’ve nailed the architect-level signal.
Verified against: Apex Developer Guide — Custom Metadata Types, Custom Settings. Last reviewed 2026-05-17 for Spring ‘26 release.