Skip to content
All writing

Endpoints belong in metadata, not in Apex

Hard-coded endpoints turn an environment difference into a deployment. Named Credentials and Custom Metadata make it configuration instead — and the argument holds for more than just URLs.

3 min readMohamed Ali
ApexIntegrationArchitecture

Most Salesforce integrations start the same way. Someone needs a callout working by Thursday, the endpoint goes into a constant at the top of the class, and it works. The sandbox URL gets swapped for the production one during deployment, and everyone moves on.

The cost shows up later, and it is rarely dramatic. It shows up as a deployment required to change a URL. As a sandbox refresh that silently points at production. As a class that cannot be tested without being edited.

The rule

Configuration and logic change on different schedules. Endpoints, credentials, timeouts, retry counts, and feature thresholds change when the environment changes. Business logic changes when the business changes. Storing them together means the slower-moving thing is held hostage by the faster-moving one.

In practice that means two platform features:

  • Named Credentials for the endpoint and its authentication. Apex names the credential; it never sees the URL or the secret.
  • Custom Metadata for everything else that is a number or a switch — the retry count, the batch size, the tolerance threshold.
// Before: the environment is compiled into the class.
private static final String ENDPOINT = 'https://api.example.com/v2/orders';

// After: the environment is a deployment-time configuration concern.
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:Order_Service/v2/orders');

The second version is not cleverer. It is just honest about which part of the line is logic and which part is environment.

Why this matters more than it looks

The obvious benefit is that changing an endpoint stops being a deployment. The less obvious one is what it does to review.

When scoring weights, retry policy, and thresholds live in Custom Metadata, someone who does not read Apex can still read every number that affects behaviour. That turns a code review question — "is this logic right?" — into a configuration question anyone on the team can answer. On one project I worked on, the entire scoring policy was four metadata records; a reviewer could check the maths without opening a class.

The trade-off, stated plainly

You gain a level of indirection. Reading the code no longer tells you the target URL — you have to look it up. For a small org with one integration and one environment, that is a real cost against a benefit you may never collect.

The threshold I use: the moment there is a second environment, or a second person who might need to change the value, put it in metadata. Below that, a constant is defensible.

One failure mode to know about

Custom Metadata text fields have a length limit, and exceeding it does not always fail loudly. I once spent several debugging cycles chasing what looked like model misbehaviour in an AI integration, and it turned out a 255-character field had been silently truncating a 2,200-character prompt on every deploy. The model was working correctly — from two sentences.

Configuration that can quietly discard part of its own value is worse than no configuration layer at all. Prompts, and anything else long enough to be truncated, belong in version-controlled code where a diff shows the whole thing.

That is not an argument against metadata. It is an argument for knowing which kind of value you are storing.


Mohamed Ali

Senior Salesforce Engineer at Emaar, Dubai, UAE. Writing about Salesforce architecture and integration design.

Get in touch