You did the hard part. You figured out what you want your app to be, and you have designed the functionality you want it to have. Beyond that, you have an idea of what the permissions will be and even diagrammed some reports that you’d like to package in there. You’re about to create a data model and you see all kinds of different options. And you get stuck.

Most of us have a similar experience. Salesforce gives us a few options, and they all look good. However, choosing the wrong approach for your use case can lead to a lot of headaches, namely problems with how your users access your application and problems with how to keep upgrades functioning properly.

Below, we’ll elaborate on a few of your options and outline what they’re best suited for, when to use them, and when to stay away. 

Custom Objects

Custom objects are the most common storage option and for a reason. Almost everything a user does is stored there, and they are super powerful. They provide you with things like Sharing, Triggers, easy automation with Flows and Process builders, FLS, Reports, and all the other things you’ve gotten used to. 

Why wouldn’t you always use them, then? Well, the answer is that sometimes you want to give all of that up for one very important priority: privacy. Objects and their data cannot be hidden from view. In other words, no matter how much you secure an object, somebody will always have access to the data you store there, and could potentially change it. That can be a big deal.

Suppose you want to store some critically private API information or an app secret. You wouldn’t want a user reading those, right? Exactly. Leaving aside the fact that it is a bad idea, it would never pass Security Review! Besides, you can’t deploy data with your app. This means that anything that lives in an object inside a subscriber’s app must be put there after installation. Yes, you can use a Post-Install script for that, but wouldn’t it be easier if the app just did it by itself during installation? Besides, if you ever were to save unencrypted secrets inside a custom object, Security Review will stop you. So, Custom Objects are great for a lot of things, but not for everything

Custom Settings

Custom Settings are fairly well known to most Salesforce users and, before the advent of Custom Metadata, were a very popular choice for all “behind the scenes” storage. Nowadays they are still useful, but there may be a better option. 

Custom Settings lack versatility but they are very lightweight and, more importantly, can be easily updated using standard Apex DML. This makes them perfect for storing application secrets that are user-configurable (think about a Company-specific Key for accessing an API, for example) or to keep global preferences. Keep in mind that each one counts against your allocation of Custom Objects. Also, they are limited to 300 Custom Fields each and any data stored in them is counted against your Data Storage limits.

The main thing to remember about Custom Settings is that they are not really objects. This means that you can’t create multiple records (although for certain use cases, List Custom Settings are still around) except when the record-identifier is a user. For example, you can store some “Org Defaults” and then override those defaults on a user-by-user basis, such as “number of records to display on a related list.” 

Some other advantages that Custom Settings have over Custom Objects are that they require no SOQL to access, they are cached for very fast reads, and they are protectable. The SOQL benefit is useful since it helps you keep your limits low, but the key points here are that they are super fast to access and that you can protect the Custom Settings.

What does “protectable” mean for your package, then? It means that by configuring your Custom Setting as “Protected,” you can ensure that no subscriber user can see or access your settings. You can only read or write code in the same namespace as the Custom Setting. 

As previously stated, Custom Settings are very much not Objects. You cannot create a relationship between two of them, you can’t have Validation Rules, Flows, Triggers, or any of the Salesforce “magic.” Furthermore, and this is the main issue with Custom Settings, you cannot deploy any values in it. Just like with Custom Objects, Custom Settings deploy empty and you must create all data inside either by asking the user to enter it post-install or by adding them yourself in a post-install script. While these downsides make the usability of Custom Settings relatively limited to the use cases above, they still are pretty powerful tools for the right scenario.

Custom Metadata

Custom Metadata are the new kids on the block (well, relatively new) and they are intended to replace a lot of what Custom Settings were used for. In fact, Salesforce discourages the use of “List” Custom Settings because almost everything they did, Custom Metadata does better.

But what is it? Custom Metadata behaves like an object in that you have multiple records in a “container” but, as the name suggests, Custom Metadata records are not really data, they are metadata. What does this all mean to you? It means that you can deploy them like any other metadata, or even access them directly from a Validation Rule, Flow, or App Page configuration pane, which is huge! 

This means that you can now create an “object” with “records” and you can include and deploy those “records” with your package and subscribers can migrate them between environments. No more complicated Post-Install scripts. You can send URLs to private systems, or pre-configure a screen. Also, you can protect an entire Custom Metadata Object, or a single Custom Metadata Record! Custom Metadata also comes with some of the advantages of Custom Settings in that they require no SOQL to access them, and they are cached, allowing them to read really fast.

And it gets better. You can have picklists (although not Global Value Sets) and parent-child relationships between Custom Metadata Objects. You can even have a lookup for other metadata, such as pointing a Custom Metadata Record to a field in a custom object.

However, the real downside is that since Custom Metadata is not traditional data, you cannot update them using DML. Instead, you can do so asynchronously with some special Apex methods. This makes it a bit harder to work with. Also, and just like with Custom Settings, they also can’t have Flows, Triggers, or any of the other Salesforce “magic” – although you can have Validation Rules.

Custom Metadata is a wonderful tool and has eliminated a lot of what Custom Settings were used for in the past. If you have some field mappings, for example, they are often the perfect solution!

Bonus: Static Resources

You’ve probably heard about Static Resources before and now are wondering why they are included in this article. Well, occasionally, they are a good idea for data storage!

In static resources, we can store a lot of data (in formats like JSON or CSV) very cheaply. If we are smart about how we store it (serialized sObjects in JSON), we can even retrieve it and insert it very cheaply.

There aren’t a lot of use cases for this, but they do exist. For example, some apps must include sample data with them. If this is the case, you may want to store all of that data in one or more Static Resources. Then either in your post-install script or, better yet, in a special screen, you can load it all up really quick.

You would not normally store anything in a Static Resource that you have to transact (read or write) with, but it is a good way to transport data using it and have it “magically” appear in the org with relatively little work.


Protection gets its own section because it is a big decision to make. When an App Developer protects something, he or she essentially hides it from view. If you configure something as “Protected” then you are making sure your namespaced code can edit it (via Apex, usually) but nobody else can even see it. It is important to note that if you make this decision lightly, you run the risk of leaving something visible that cannot be seen or you can hide something that should be visible.

Custom Settings are either protected or public, while Custom Metadata Objects can be Public, Protected-Namespace, or Protected-Package. Protected-Namespace means that all the packages that make up a namespace (using packaging 2.0) have access to it, while Protected-Package means that only components within the same actual package, regardless of the namespace, see and edit it.

Keep in mind that Custom Metadata Records can also be protected. This means that you may choose to make a Custom Metadata Object public, but protect some of its records so they cannot be seen by the subscriber. This allows you to keep some items “private” to you while allowing the subscriber to add their own. Keep in mind that if you protect Custom Metadata, then the only way to update those records is by upgrading your package. That may be a deal-breaker in some cases.

Another important caveat is that you can always make a Custom Setting or Custom Metadata less restrictive via package updates (for example, from Protected to Public) but you cannot make it more restrictive. So make your decisions carefully.

The Best Approach

You’ve now seen the main options that exist for you to store your data and configuration items. Below, you can find a chart that summarizes the best and worst parts of each type of storage.

If you’re still undecided, the below guide can help you figure out what to select for each individual scenario.

Companies build their Salesforce businesses better with CodeScience. It is our mission that no ISV navigates Salesforce alone. If you’re looking for guidance on your product, help supporting your customers, or just need to ask an expert, get in touch today!