Custom Metadata vs. Custom Objects in Managed Package

Here are some "advantages" vs using a Custom Object:

  • Can be Deployed
  • Can use Field Definition or Entity Particle to create references to Custom Object Metadata
  • Retrieve doesn't cost against SOQL limits (unless it contains long text)
  • Can be referenced in formula fields
  • Unit Testing isn't too bad as well as the code is well composed. Because they are treated similar to @seeAllData, it actually makes integration testing the current configuration of the ORG much easier.
  • Can be "protected" to store secrets in managed packages

However (IMO), Salesforce missed too many features to make them the clear choice in every scenario:

  • Records cannot be statically referenced in code (this would make dependencies much more robust)
  • No direct/global access in VF or LWC
  • Objects cannot reference them. This results in a lot of "magic" relationships being setup (EG string matching).
  • No Triggers (As mentioned above)

I've created a public google sheet that attempts to compare the features of each of the configuration options (please add comments if I missed anything).

My Approach

  • I tend to favor CMDT over Custom Settings, UNLESS I need user specific configuration.
  • I'll use Custom Objects where it's critical to ensure referential integrity to data records.
  • Custom Labels were once commonly used because they could be deployed, but now with CMDT there isn't much reason to use them (for configuration).

A reason I haven't seen articulated yet applies specifically to the managed package context:

Packaged, protected Custom Metadata Types are an ideal solution for secret storage in a managed package context. While Protected Custom Objects and Protected Custom Settings can also offer secret storage, only Custom Metadata Types allow those secrets to be packaged and deployed, and protected from everything outside the boundary of packaged Apex.

The big reason for me to use Custom Metadata is the logical separation between Custom Metadata and Custom Objects. In my head they serve a different purpose and that helps me better organize things in my org. I would store any configurations for my applications in Custom Metadata rather than Custom Objects. The way I see it Custom Objects should retain data about records in the org.

Additionally as NSjonas pointed out CM doesn't count against the governor limits in the org. They can be refreshed to sandboxes and are cached data which would provide better performance.