Skip to main content

SF-0298 · Concept · Medium

What are Custom metadata or custom metadata types?

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

Custom metadata types (CMDTs) are a Salesforce feature that lets you define a schema and then create records of that schema which are treated as metadata, not data. The schema goes in change sets and packages, and so do the rows. That’s what makes them different from custom objects — the records themselves are deployable artifacts.

Why they exist

Every org needs configuration: feature toggles, integration endpoints, tax rates, business rules, mapping tables. Hardcoding that into Apex means a code deploy every time the config changes. Custom objects let admins edit at runtime but force you to migrate the data manually between sandboxes. Custom metadata gives you the best of both:

  • Type-safe schema like a custom object
  • Deployable records that flow through change sets, unlocked packages, and SFDX deployments
  • Cached reads in Apex with no SOQL query limit consumption

Anatomy

You create a Custom Metadata Type the same way you create a custom object — go to Setup → Custom Metadata Types → New. The API name ends with __mdt rather than __c. Define fields (text, number, picklist, checkbox, metadata relationship, etc.). Then you create records under that type — each record has a DeveloperName and a MasterLabel.

Setup
└── Custom Metadata Types
    └── Integration_Endpoint__mdt    ← the type (schema)
        ├── Salesforce_to_Stripe     ← a record (configuration row)
        ├── Salesforce_to_Hubspot    ← another record
        └── Salesforce_to_Slack

Each record carries values for the fields you defined: URL, timeout, auth method, retry count, etc.

Reading CMDT in Apex

// SOQL works, but each query counts against limits
List<Integration_Endpoint__mdt> endpoints = [
    SELECT DeveloperName, Endpoint_URL__c, Timeout_Seconds__c
    FROM Integration_Endpoint__mdt
    WHERE Active__c = true
];

// Better: getAll() / getInstance() — cached, NO SOQL limit consumed
Map<String, Integration_Endpoint__mdt> allEndpoints =
    Integration_Endpoint__mdt.getAll();

Integration_Endpoint__mdt stripe =
    Integration_Endpoint__mdt.getInstance('Salesforce_to_Stripe');

getAll() and getInstance() are the killer feature. They read from a cached store, don’t count against the 100-SOQL governor limit, and return strongly-typed records. This is why CMDT is the default choice for runtime config in modern Apex.

Deployable records — the differentiator

The thing that makes CMDT unique is that records are part of the metadata layer. When you create a record of Integration_Endpoint__mdt in your dev sandbox, you can put that record in a change set and deploy it to UAT and production. The record is XML in the metadata API — no data loader, no scripts, no separate migration step.

This is true for change sets, unlocked packages, managed packages, and SFDX force:source:deploy. A managed package can ship its own CMDT records to every subscriber org.

Writing CMDT records from Apex

CMDT records cannot be inserted or updated with DML. You write them with the Metadata API through Metadata.Operations.enqueueDeployment(), which fires off an async metadata deployment.

Metadata.CustomMetadata cm = new Metadata.CustomMetadata();
cm.fullName = 'Integration_Endpoint.New_Endpoint';
cm.label = 'New Endpoint';

Metadata.CustomMetadataValue v = new Metadata.CustomMetadataValue();
v.field = 'Endpoint_URL__c';
v.value = 'https://api.example.com';
cm.values.add(v);

Metadata.DeployContainer container = new Metadata.DeployContainer();
container.addMetadata(cm);
Id jobId = Metadata.Operations.enqueueDeployment(container, null);

That’s a lot more ceremony than insert record; — and that’s the point. CMDT writes are deployments, not transactions.

What CMDT is good for

  • Feature flags and toggles
  • Tax tables, currency conversion, regional rules
  • Routing rules, escalation tables
  • Integration endpoints, named credentials metadata
  • Picklist value overrides per business unit
  • Trigger framework configuration (which handlers run on which objects)

What CMDT is not good for

  • User-facing data entry — there’s no list view, report builder, or related list integration.
  • High-volume writes — every write is an async metadata deployment.
  • Per-user or per-profile configuration — you can build it, but hierarchy custom settings handle this cleanly out of the box.

What interviewers are really looking for

The cheap answer is “deployable configuration.” The full answer makes three points: (1) CMDT records flow through metadata deployments, so config moves with code; (2) getAll()/getInstance() reads are cached and don’t burn SOQL limits; (3) writes go through the Metadata API asynchronously, not DML. If you can also articulate when not to use CMDT (user data entry, high-volume writes), you’ve nailed the comparison.

Verified against: Apex Developer Guide — Custom Metadata Types, Trailhead module Custom Metadata Types Basics. Last reviewed 2026-05-17 for Spring ‘26 release.