ESC

AI-powered search across all blog posts and tools

Architecture · April 22, 2026

Stop Using Custom Settings for Integration Config - An Integration Team's Custom Metadata Playbook

Custom Settings travel as data, not metadata. That single fact has broken more production deployments than any other Salesforce anti-pattern. Here is the Custom Metadata Types playbook every integration team should adopt — with the limits, patterns, and trade-offs nobody writes about.

☕ 9 min read 📅 April 22, 2026
  • Custom Metadata Types deploy with your code; Custom Settings data does not — the single biggest reason to switch
  • CMT reads are cached and do not count against SOQL governor limits
  • Per-org limits: 200 Custom Metadata Types, 100 custom fields per type
  • CMT counts against configuration data limits, not data storage limits
  • Use Named Credentials for secrets; CMT for endpoints, feature flags, and business rules

If you’ve been in Salesforce integration work long enough, you’ve seen this incident: a team deploys a fresh integration to production. The Apex class deploys. The flow deploys. The external service endpoint configuration, stored as a Custom Setting, does not — because Custom Settings are data, not metadata.

Half an hour later, the team is scrambling to manually re-create records in production, against a clock, while integrations are either silently failing or retrying against the wrong endpoint. I’ve watched this happen three times in three different orgs.

The fix has been sitting in the platform for a decade. Custom Metadata Types. Yet integration teams still reach for Custom Settings first — mostly, I think, because nobody ever gave them a focused playbook on what to use when.

Here it is.

The One Rule That Changes Everything

ℹ️ The sentence worth tattooing on your ops team's forearm

Custom Metadata Types deploy with your code. Custom Settings records do not. Everything downstream follows from that single fact.

Custom Settings are a data-model feature — the records you create live in your org’s data storage and are not part of your metadata deployment. Custom Metadata Types, per Salesforce’s Custom Metadata Types documentation, are fully metadata — both the type and every record travel in Metadata API deployments, change sets, and packages.

That single difference is the reason integration configuration should live in CMT.

What You Should Store in CMT for an Integration

The Integration Config Pantry — What Goes Where
Where Integration Configuration Should LiveCustom MetadataAPI endpoints (non-secret)Feature flagsRetry counts / timeoutsMapping tablesValidation rule thresholdsBusiness constantsDeploys with code ✓No SOQL limits ✓Named CredentialsAPI keysOAuth client secretsUsername + passwordBase URLs + authCertificate refsEncrypted at rest ✓Platform-managed ✓Custom SettingsPer-user overridesPer-profile overridesRuntime ops toggles(that change often)Not deployable ✗Requires manual steps ✗

An integration has three kinds of configuration. Each belongs somewhere different:

  • Endpoints, mappings, feature flags, thresholds → Custom Metadata Types
  • Secrets (API keys, OAuth credentials, passwords) → Named Credentials
  • Per-user or per-profile runtime overrides → Custom Settings (Hierarchy)

The single biggest mistake I see is reaching for Custom Settings when the thing being configured has nothing to do with a specific user or profile. API endpoints are not user-specific. Feature flags that control an integration’s behaviour globally are not user-specific. They belong in metadata, not data.

The Governor-Limit Advantage

Custom Metadata Type records are not data rows in the traditional sense. Per Salesforce’s CMT Allocations and Usage Calculations doc:

  • CMT reads are cached and do not consume SOQL query row or query count governor limits
  • CMT counts against configuration data limits, not data storage limits
  • Records are automatically available in Apex, Flow, and Visualforce without manual load patterns

In practice, this means you can put a hundred mapping rules in a CMT and read them on every trigger without ever worrying about the 100-SOQL-query-per-transaction limit. That’s a structural win for integrations that run at trigger context.

💡 The mapping-table pattern

For field mappings between your Salesforce object and an external system, build a CMT with fields like Source_Field__c, Target_Field__c, Transformation__c. Your integration loop reads the collection once, uses the map, and never touches a SOQL query for config. Adding a new field becomes a deploy, not an ops ticket.

The Limits You Need to Respect

The official Custom Metadata Types Limitations documentation is worth bookmarking. The ones that bite integration teams:

LimitValueWhy it matters
Custom Metadata Types per org200Shared across installed packages — watch this in managed-package-heavy orgs
Custom fields per type100Design tables with normalized fields, not one big blob
Record size500 KB per record (varies)Not a table for large free-form content
DeploymentMetadata API / change sets / unlocked packagesNo surprise — metadata behaves like metadata

The 200-type limit is the sneaky one. If you’re in an org that ships with three or four managed packages and each consumes 20-40 CMT slots, you can exhaust the budget faster than you expect.

⚠️ Check System Overview before adding CMTs at scale

Setup → Company Information → System Overview shows your current CMT usage. Check this before rolling out a pattern that uses dozens of types. Easier to consolidate early than re-architect at 180/200.

Named Credentials — The Secrets Pair

CMT is not encrypted, full stop. Do not put API keys, OAuth client secrets, or passwords in it. Salesforce ships Named Credentials specifically for this purpose — credentials are encrypted at rest, the platform manages token refresh, and the integration Apex or Flow references the credential by name, not by value.

The pattern that works cleanly:

  1. The endpoint’s non-secret configuration (base URL, timeout, retry count, feature flags) lives in CMT
  2. The endpoint’s secret configuration (auth header values, tokens) lives in the Named Credential
  3. The integration code resolves both at call time

This gives you the deployability of CMT for the everyday config, and the security of Named Credentials for the sensitive bits.

A Migration Plan for Teams Still on Custom Settings

The four-step migration I recommend

Step 1 — Inventory. List every Custom Setting in the org. For each, classify: does it change per user/profile (keep in Custom Settings), or is it a global config value (migrate to CMT)?

Step 2 — Design. For the global ones, design the CMT structure. Don’t just mirror the Custom Setting one-to-one — you often discover normalization opportunities (one mapping type instead of five sibling settings).

Step 3 — Dual-write during migration. For the transition period, have your Apex read CMT first, fall back to Custom Settings if not found. This lets you deploy the CMT, populate it with a data-loader / one-time script, and cut over without a hard switchover.

Step 4 — Remove the Custom Setting. Once traffic is fully on CMT in production for a release cycle, remove the fallback code, delete the Custom Setting. Stop paying for the operational overhead of keeping two sources in sync.

🚨 Real-World Scenario

Problem: An integration team stored their SFTP host, port, timeout, and retry-count values in a Custom Setting. Every production deploy, they had a runbook entry: “remember to update XYZ_Config Custom Setting after deploy.” Twice in eighteen months, somebody forgot. Each time, the integration pointed at the sandbox SFTP until someone noticed.

Fix: A single CMT Integration_Endpoint__mdt with Host__c, Port__c, Timeout_Seconds__c, Retry_Count__c. Deployments now move the config along with the code. The runbook entry disappeared, and so did the incident class.

What to Leave in Custom Settings

Not everything should move. Hierarchy Custom Settings are genuinely the right tool when:

  • A value needs to be overridden per user or per profile without a deployment
  • An ops team needs to tune a threshold at runtime without involving developers
  • A feature toggle needs to be flipped quickly in production without a package upgrade

The test is: “does this value change based on who is using the system, or does it need to change faster than our release cycle?” If yes, keep in Custom Settings. If no, it belongs in CMT.

What Still Confuses Teams

Three sneakier distinctions

CMT isn’t free in change sets. Adding a new CMT type to a change set adds the type definition. Adding a record requires also adding it as a separate component in the change set. If you use unlocked packages or Metadata API deployments, it’s cleaner.

CMT records are read-only in Apex. You cannot DML-insert or update CMT records from Apex. Changes go through the Apex Metadata API or Metadata API deployments. If your “config” needs to be modifiable at runtime by end users, that’s data, not metadata — reconsider whether CMT is the right fit.

Big CMT tables are not a substitute for a custom object. If you have hundreds of rows and the data changes monthly, you want a custom object. CMT is for configuration, not reference data that churns.

Where the Official Docs Live

Read the limitations doc first. It’s the one that prevents design choices you’ll later regret.


An integration has an API base URL, an API key, and a retry count. Where should each live?
Why don't Custom Metadata Type reads count against SOQL governor limits?

What Custom Setting is currently embarrassing your integration team the most — and why hasn’t it moved yet?

How did this article make you feel?

Comments

Salesforce Tip

🎉

You finished this article!

What to read next

Contents