Skip to content

Custom Metadata Types and Custom Settings

An administrator selects reusable configuration tiles from a central pattern library connected to shared and hierarchical settings

By this point in the admin journey, you have worked through org health, user experience, and automation. Each of those areas depends on decisions that may change over time: which feature is enabled, which queue owns work, what threshold applies, which template is used, or which integration endpoint an org should call. When those decisions are buried inside Flow conditions, formulas, validation rules, or Apex, small changes become harder to review and riskier to deploy.

This chapter is about treating configuration as a first class asset. You will learn when to use Custom Metadata Types versus Custom Settings, how each behaves across deployments and environments, and how to model ownership, naming, documentation, and change control so the org stays predictable as it grows.


🧬 Custom Metadata Types & Custom Settings

Section titled “🧬 Custom Metadata Types & Custom Settings”

Custom Metadata Types and Custom Settings can look similar at first, but they serve different operational purposes.

Custom Metadata Types define the schema for configuration that you store as Custom Metadata records. Both the type definition and its records are treated as metadata, so they can be deployed, versioned with your codebase, and promoted across environments in a controlled release process. They are a strong fit when configuration should move with deployments, such as feature flags, routing maps, or environment aware integration settings.

Custom Settings define configuration structures whose values are stored as org data records. In practice, this means the setting definition is metadata, while the stored values behave like data in the org. They are optimised for runtime access and straightforward admin updates, especially when values may change more frequently without a deployment. They are useful for lightweight defaults and user-level overrides that teams need to adjust operationally.

🔩 When to Use Custom Metadata vs Custom Settings

Section titled “🔩 When to Use Custom Metadata vs Custom Settings”

Both custom metadata types and custom settings can hold org configuration, but they differ in how definitions and values are treated:

Feature Custom Metadata Types Custom Settings
Definition (type) Metadata Metadata
Stored values (records) Metadata records Data records in the org
Deployment model Type and records can move via metadata deployment/package workflows Type can deploy as metadata; values are typically managed as org data
Versioning Type + records fit source control and release pipelines well Type is versionable; record values are more environment specific and harder to version cleanly
Runtime behaviour Optimised for read access; ordinary Apex DML is not supported, but admins and metadata tooling can manage records Optimised for read/write runtime access patterns (for example via Apex)
Use Case Config you version with code (feature flags, org defaults, lookup tables) User customisable settings, org-wide defaults
Scaling Deployment-friendly configuration; org allocations still apply Cached configuration data; org allocations still apply
Admin Updates Setup or a metadata deployment, depending on governance Setup or runtime data tooling

When to use Custom Metadata:

  • Feature flags (enable/disable features per org)
  • Non-secret routing, mapping, and feature configuration that should move with a release
  • Approval routing rules (route expense requests by department)
  • Lookup tables (country codes, picklist mappings)

When to use Custom Settings:

  • Org-wide defaults (default email sender, max records per batch)
  • User-level preferences (debug logging per user, notification preferences)
  • Settings that change frequently without code deploy

🔧 Custom Metadata Types: Setup & Governance

Section titled “🔧 Custom Metadata Types: Setup & Governance”

Let’s build a practical custom metadata type: a feature flag registry that controls which features are enabled in each environment.

  1. Create the Custom Metadata Type

    • Setup → Custom Code → Custom Metadata Types
    • Click New Custom Metadata Type
    • Label: Feature Flag
    • Plural Label: Feature Flags
    • Object Name: FeatureFlag
    • Description: “Controls feature availability across environments”
    • Select: All Apex code and APIs can use the type, and it’s visible in Setup
    • Save
  2. Add Custom Fields

    • Field 1: Is_Enabled__c (Checkbox)
    • Field 2: Description__c (Text, 255 characters)
    • Field 3: Owner_Email__c (Email)
  3. Create Records

    • In the Custom Metadata Type detail page and click Manage Feature Flags
    • Click New to create a record
    • Label: New LWC Component
    • Feature Flag Name: NEW_LWC_COMPONENT
      • Is_Enabled: ☑ (checked)
      • Description: “New Lightning Web Component for opportunity dashboard”
      • Owner_Email: architect@company.com
    • Click New again to create another record
    • Label: Beta Approval Flow
    • Feature Flag Name: BETA_APPROVAL_FLOW
      • Is_Enabled: ☐ (unchecked)
      • Description: “New approval process using orchestration (in beta)”
      • Owner_Email: architect@company.com
  4. Deploy & Version

    • Include the type and intended records in your change set or package
    • Review each target org’s values as part of the release. If environments need different values, document how your pipeline or post-deployment step supplies them; an Environment__c field does not identify the current org by itself

Governance for Custom Metadata:

  • Use names that communicate intent immediately: A good Custom Metadata Type name tells readers what decision space it controls (for example, feature flags, routing, or environment behaviour). Clear names reduce setup mistakes and make metadata easier to find during incident response.
  • Adopt a consistent record-key pattern: Choose one record naming style and keep it consistent (for example, upper snake case such as FINANCE_ROUTING and ENGINEERING_ROUTING). Consistent keys are easier to reference in formulas, Flow conditions, and deployment reviews.
  • Document ownership and purpose on each record: Include a concise description of what the record controls and who owns it. This prevents “mystery metadata” and makes change approvals faster.
  • Run periodic hygiene reviews: Review metadata records on a regular cadence to remove deprecated entries, confirm owners are still valid, and ensure values still match current process design.

Take a look at the Custom Metadata Types Basics to continue your learning.

📝 Custom Settings: Organisation-Wide and User-Level

Section titled “📝 Custom Settings: Organisation-Wide and User-Level”

Custom Settings are simpler operationally than Custom Metadata as the stored setting values (records) are org data. They come in two flavors:

  1. List (legacy choice for new designs): Named, org-wide configuration records. Salesforce disables the creation of new List custom setting types by default
  2. Hierarchy: Org-wide + user level overrides (users can override org defaults). This is the primary modern use case for Custom Settings.

Hierarchy custom settings let you define values that vary by organisation, profile, or user. Salesforce automatically resolves the most specific match (user > profile > org default), making them perfect for per-team feature flags, user specific thresholds, or profile based exceptions. List custom settings, by contrast, store org-wide lookup data that applies equally to everyone, like country codes, API endpoints, or configuration mappings, think shared reference tables rather than user specific overrides. Use hierarchy when behaviour changes by context (getInstance() auto-resolves), list when you need static org-wide records accessed by name.

Example: Org-Wide Custom Setting for Email Configuration

  1. Create the Custom Setting

    • Setup → Custom Code → Custom Settings
    • Click New
    • Label: Email Configuration
    • Plural Label: Email Configurations
    • Setting Type: List
    • Visibility: Public
  2. Add Fields

    • Default_Sender_Email__c (Text, 255 chars)
    • Max_Email_Recipients__c (Number)
    • Enable_Bounce_Tracking__c (Checkbox)
  3. Create Org-Wide Record

    • Click Manage Email Configurations
    • Click New
    • Record:
      • Name: Default
      • Default_Sender_Email: noreply@company.com
      • Max_Email_Recipients: 100
      • Enable_Bounce_Tracking: ☑
  4. Access in Apex

    Email_Configuration__c emailConfig = Email_Configuration__c.getInstance('Default');
    String senderEmail = emailConfig.Default_Sender_Email__c;

For Hierarchy custom settings (user-specific overrides):

  1. Set Hierarchy as the Setting Type
  2. Create an org-wide record (for all users)
  3. Have an administrator create profile- or user-specific records where an override is genuinely needed
  4. Access in Apex: Email_Configuration__c.getInstance(userId)

🔄 Using Custom Metadata & Settings for System Configuration

Section titled “🔄 Using Custom Metadata & Settings for System Configuration”

A worked example: fallback and escalation routing for Equipment Requests

The Equipment Request approval from Admin Essentials routes each request to the requester’s manager, and the capstone asks you to decide what happens when that manager is blank or the cost is high enough that a manager should not be the last word. The first build already answers the first question with a public group name hard-coded in the approver formula. The second is a new requirement, and the obvious place for it is a cost threshold in a Decision element. Both would be correct on the day and wrong the first time IT reorganises or Finance changes the limit, and neither can be seen without opening the approval process.

Store the routing decisions in custom metadata instead, one record per team. The type needs four fields:

Custom Metadata Type: Equipment_Approval_Routing__mdt

Field label API name Type Holds
Team Team__c Text (40) The requester’s team. The Equipment Request app treats the record owner’s role as their team, so this is a role name and the lookup matches on it exactly
Fallback Approver Group Fallback_Approver__c Text (40) The API name of the public group that receives the work item when the requester has no manager
Escalation Approver Group Escalation_Approver__c Text (40) The API name of the public group that gives the second approval
Escalation Threshold Escalation_Threshold__c Number (16, 2) The Estimated Cost above which the second approval is required. Custom metadata has no Currency field type, so this is a plain number in the org’s currency, and the flow compares it with the currency field

The group fields are Text rather than a lookup because custom metadata cannot reference a public group; the flow returns the name and the approval step resolves it at run time, so a misspelt name fails when a request arrives, not when you save the record. Because the match is on the owner’s role, every role that can submit a request needs a record, and a manager’s role is not covered by their staff’s record. The Equipment Request app’s two teams therefore need four:

DeveloperName Team__c Fallback_Approver__c Escalation_Threshold__c Escalation_Approver__c
OPS_ROUTING Ops Team Equipment_Request_Ops_Fallback 1000 Equipment_Request_Finance
OPS_MANAGER_ROUTING Ops Manager Equipment_Request_Ops_Fallback 1000 Equipment_Request_Finance
IT_ROUTING IT Team Equipment_Request_IT_Fallback 2000 Equipment_Request_Finance
IT_MANAGER_ROUTING IT Manager Equipment_Request_IT_Fallback 2000 Equipment_Request_Finance

The role names are the ones the Essentials build uses; map them to your org. The capstone practice pack carries these rows as a CSV, plus the deliberately missing role its failure test relies on.

The approver columns hold public group API names, which is what a Flow approval step’s Resource approver resolves at run time. The Essentials chapter already uses that mechanism for the manager. The approval canvas has no Get Records element of its own, so the record is read by an autolaunched flow run as a background step at the start of the process, which hands back four step outputs: the fallback group, the escalation group, whether the request crosses the threshold, and whether a routing record was found at all. A Decision on that last output stops a request whose role has no record before anyone is asked to approve it. The manager step’s approver formula uses the fallback output when Manager is blank, and a Decision between stages runs the escalation stage only when the manager has approved and the threshold output is true; a rejected request never reaches Finance. The Equipment Request capstone delivers exactly that change.

If the same rule needs to be read from Apex, the query looks like any other object:

List<Equipment_Approval_Routing__mdt> routing = [
SELECT DeveloperName, Team__c, Fallback_Approver__c,
Escalation_Threshold__c, Escalation_Approver__c
FROM Equipment_Approval_Routing__mdt
WHERE Team__c = :requesterTeam
LIMIT 1
];

Now a new team, a new fallback group, or a changed threshold is a metadata record: create it, deploy it, and the flow behaves differently without anyone opening Flow Builder. A team with no record is the case to design for first, because a lookup that finds nothing must stop the request rather than approve it; the capstone builds exactly that branch. The Sandbox Strategy chapter’s release runbook applies to that record the same way it applies to a field.

Maintenance Pattern:

  • Create one metadata record per team
  • Review quarterly: retire teams that no longer exist and confirm the public groups still have members
  • Add notes to each record (owner, effective date, the brief or ticket that set the threshold)
  • Version all changes: track in source control or changeset history

Continue to learn how to use Custom Metadata programmatically with the Programmatic Development with Custom Metadata Types Trailhead module.

Custom metadata is code like (definition + records are deployable as metadata); custom settings are data like (records live per org and are usually moved with data tools).

Deploy custom metadata via:

  1. Change Sets: Setup → Outbound Change Sets → add the custom metadata type and its records → deploy
  2. Packages: Include the type and records in managed/unmanaged packages so they move with your app
  3. Metadata API / CLI: Use Salesforce CLI, Ant, or other metadata tooling to retrieve and deploy XML

Deployment Gotcha: Custom metadata records can’t be changed through ordinary Apex DML. Updates go through metadata changes (change sets, packages, Metadata API) or the Setup UI. Treat the record’s DeveloperName as a stable key: a rename requires you to update and deploy every Apex, Flow, formula, or configuration reference that uses it.

Custom settings records behave like data, so you typically move them separately from their definition:

  1. Data Loader / data tools: Export from source org and insert into target org using the API
  2. Deployment tools: Use products like Gearset/BOFC or custom scripts to migrate custom setting data alongside other config
  3. Manual UI entry: Setup → Custom Settings → Manage → New/Edit for small numbers of records
  4. Apex / scripts: Seed records in test/migration code when you need repeatable initial data

Change sets and packages move the custom setting definition (fields, type), but not the data itself. If you deploy a bad value, there’s no built-in versioning—you roll back by restoring prior data via another load or script.

Salesforce enforces a few important limits around custom metadata and custom settings that are easy to bump into in real projects:

  • Custom metadata usage: The org has allocations for custom metadata types, fields, and total record size. Check the current Custom Metadata Type Usage page and Salesforce limits for the target org rather than designing around a copied number.
  • Custom settings usage: Custom settings count against platform allocations and their cached data allowance depends partly on licensing. Check the target org’s current limits before using a setting for a large lookup table.
  • Query behaviour: Apex queries for custom metadata records are generally exempt from the standard SOQL query and row limits, although queries containing long text area fields are an exception. Flow queries for custom metadata count towards the transaction’s limits. Review Salesforce’s Custom Metadata Type limitations before using a Get Records element repeatedly in bulk automation.
  • Record size: Custom metadata usage is affected by the size of the fields you define, so large text fields are a poor fit for bulky content.
  1. Runtime caching: Custom settings and custom metadata are cached for fast read access. Changes made via Setup or deployments are visible to new transactions, but long running Apex or integrations won’t see updated values mid transaction. Don’t rely on “hot swapping” configuration during critical batch runs.
  2. Deployment order: If Custom Metadata Type A has a relationship field to Type B, deploy B first or in the same deployment bundle. Out of order deployments often fail with dependency errors.
  3. Custom settings performance: Because custom settings are cached and can be accessed via $Setup, they don’t consume SOQL limits but hierarchy resolution still happens per call. Read once into a variable and reuse rather than calling getInstance() repeatedly in loops.
  4. Permissions & access: Editing custom metadata records requires administrative permissions. If access to the type is restricted, grant the required custom metadata type access through permission sets and verify the behaviour as the intended user.

For callout endpoints, also consider Named Credentials, which carry the URL and the authentication together and avoid hardcoding either.

  • “Insufficient access”: The user or integration may not have access to the custom metadata type. Check custom metadata type access in the relevant permission set and confirm whether restricted access is enabled for the type.
  • “Record is locked”: The record lives in a managed package or is otherwise read-only. Override by creating your own custom metadata record or custom setting record in your unmanaged namespace.
  • “Deployment failed: dependent metadata”: You tried to remove or change a type/field referenced in Flows, Apex, validation rules, or other metadata. Remove or update the references first, then re-deploy.

📋 Governance: Naming, Documentation & Ownership

Section titled “📋 Governance: Naming, Documentation & Ownership”

Treat custom metadata and custom settings as shared platform infrastructure, not ad-hoc configuration. A small amount of governance up front prevents “mystery records” and makes it obvious what can be safely changed.

Use consistent, self explanatory names so admins and developers can recognise intent at a glance.

  • Custom metadata types: name the type the way you name your custom objects, so it reads as one of them in Setup; the platform appends the __mdt suffix automatically (for example, Equipment_Approval_Routing__mdt, matching Equipment_Request__c).
  • Records (DeveloperName): SCREAMING_SNAKE_CASE keys that read like constants (for example, IT_ROUTING, FINANCE_ROUTING). Treat DeveloperName as a stable API key and choose it carefully; renaming it means coordinating every reference.
  • Descriptions: Use the Description field on the type and on each record to explain what it controls, where it’s used, and any caveats. Type level descriptions explain the purpose of the configuration; record level descriptions explain how each instance is used.

Example:

Field Value
Type Name Equipment_Approval_Routing__mdt
Record IT_ROUTING
Description “IT team approval routing. Names the fallback approver group when a requester has no manager and the escalation group and cost threshold for Equipment Requests where the requester’s team is IT. Used by Equipment Request Approval. Created 2026-01-15 by J. Smith.”

Back your in-org descriptions with a living reference in your documentation tool (Confluence, Notion, internal wiki).

For each custom metadata type or custom setting, capture:

  • Purpose – What problem does this configuration solve?
  • Records – Which records exist and how each is used (flows, Apex, formulas, validation rules, etc.).
  • Owner – Who is accountable for changes (role or named person).
  • Change profile – How often it changes (rarely, monthly, per-release) and any related processes.

Keep the in org Description fields aligned with your wiki so someone starting from Setup can quickly find deeper context. Also consider adding a ‘Used in’ section listing Flows, Apex classes, validation rules, or formula fields that reference the type. This becomes invaluable during refactoring.

Treat each type like a small product with a clear steward.

  • Assign every metadata type to an owner (architect, tech lead, admin, or business SME).
  • Require at least lightweight review/approval for new records or structural changes.
  • Schedule a quarterly review to retire unused records, consolidate duplicates, and confirm that documented usage still matches reality.

Configuration that drives behaviour should be as traceable as code.

  • Track changes in source control wherever possible (Git commits, deployment scripts); for change sets, reference them in your wiki or release notes.
  • Capture intent: every change should have a short “why” (linked user story, incident, or design decision).
  • Deploy safely: validate changes in a sandbox first, then deploy to production during a planned window if the impact is broad.
  • Test paths: for critical config, maintain simple regression checks (Apex tests, Flow tests, or playbook steps) that exercise each key record after deployment.

With clear naming, documentation, ownership, and change control, custom metadata becomes a stable, predictable foundation rather than a source of hidden behaviour.


In this part, you learned how Custom Metadata Types and Custom Settings help separate behaviour from hardcoded logic. You compared deployment models, runtime patterns, org-wide settings, user level settings, limits, troubleshooting habits, and the governance needed to keep configuration understandable over time.

Custom configuration should clarify behaviour, not hide it. Used well, metadata becomes a stable control layer: admins can adjust known levers, developers can version important records, and the org can evolve without scattering critical decisions through automation and code.

Configuration you can change safely is only half the job. The other half is proving a change works before it moves. In Testing Salesforce Configuration Changes, you will build a test plan covering the people a change affects, the paths where it should fail, and the things you did not touch, then decide in advance what evidence would make you reverse it.