Never Hard-Code IDs in Sharing Rules, and How Rules Sprawl
Module 5 of 6 in the chapter Public Groups and Criteria-Based Sharing Rules, chapter 7 of Securing Salesforce: Sharing and Visibility. Each module stands on its own.
The question this module answers: What makes a sharing rule break when it moves from one org to another, and what makes an object’s rules impossible to understand a few years later?
Has anyone ever opened a client’s sharing rule and seen “share this where the ID equals” something? I have. So when you see that, what do you tell your customer they can’t do?
They can’t deploy it. If it was created in production, it only works in production. You can’t deploy it from staging to production, and you can’t bring it back from production to staging. And so staging no longer behaves the same. It’s a nightmare.
Chapter 5, module 3 gave you the short version: try to avoid hard-coded IDs. This module shows why, and then looks at the slower problem next to it: rules that keep adding up until nobody knows why they’re there.
How an ID gets into a rule
Section titled “How an ID gets into a rule”Usually it gets there honestly. A criteria-based rule can only read fields on the record itself, and “you can’t use lookup fields” (module 2). So when the rule needs the related account, or the related region record, someone fills a text field with the lookup’s ID and shares on that. The rule works. Today, in this org.
The value in a criteria-based rule “is always a literal number or string” (Create Criteria-Based Sharing Rules (opens in a new tab), Salesforce Help, checked 2026-11-07). So the rule doesn’t hold a link to that record. It holds a string of characters that happens to be that record’s ID in this org.
What not to do
Section titled “What not to do”Here’s what one looks like. This is an example, with a made-up ID, but I’ve seen rules just like it.
| Part of the rule | Hard-coded: don’t do this | Shares on a meaning: do this |
|---|---|---|
| Object | Account | Account |
| Criteria | Region Record ID equals a0B5j00000AbC12EAF | Region equals West |
| Share with | Public group: West Sales | Public group: West Sales |
| Access | Read/Write | Read/Write |
| What it means to the next person | Nothing. They have to look the ID up, in this org only. | Accounts in the West go to the West sales team. |
Read the left column as the next admin would. What is a0B5j00000AbC12EAF? You can’t tell from the rule. You have to search for it, and only in this org will you find it. Read the right column, and you know what the rule does without looking anything up.
Why an ID doesn’t travel
Section titled “Why an ID doesn’t travel”Here’s what Salesforce says about IDs and sandboxes. “When data is copied to a sandbox, object IDs for records are copied. … After being copied, however, object IDs don’t synchronize between the production org and sandbox. The sandbox and its corresponding production org act as independent orgs.” Records created in production after the sandbox is created or refreshed don’t reach the sandbox, and records created in the sandbox don’t reach production (Servers and IDs (opens in a new tab), Salesforce Help, checked 2026-11-07).
Now follow one rule through your orgs.
- Production. The rule matches the region record’s ID. It works.
- A full sandbox, just refreshed. “Metadata and all data” are copied (Sandbox Licenses and Storage Limits by Type (opens in a new tab), Salesforce Help, checked 2026-11-07), with the same IDs. So the rule seems to work here too. That’s what makes this trap hard to see.
- A developer sandbox. Developer and Developer Pro sandboxes copy “Metadata only” (same page). The rule arrives, and no record in that org has that ID. It matches nothing.
- Anything new. A region added in production next month has an ID no sandbox has ever seen. A region built in a sandbox for a project gets an ID production will never have.
So the rule you test is not the rule you run. That’s what I mean by “stage no longer behaves the same anymore.”
What the deployment carries
Section titled “What the deployment carries”Sharing rules are metadata. “In API version 33.0 and later, retrieving, deleting, or deploying of all sharing rules in an organization is available,” one file per object (SharingRules (opens in a new tab), Metadata API Developer Guide, Winter ‘27, API version 68.0, checked 2026-11-07).
Look at what’s in that file, and the difference becomes clear. Salesforce’s own example names its target by name: shared to the role “CEO.” A name means the same thing in every org where that role or group exists. The criteria carry a field, an operator and a value, and the value goes across exactly as written (same page). A word like “West” means West everywhere. An ID means one record in one org.
So you can’t deploy these through your sandboxes to production, because those IDs will be different. Every time you deploy, someone has to remember to fix the rule by hand. And the day someone forgets, the rule shares the wrong records, or none, and nothing tells you.
What to share on instead
Section titled “What to share on instead”Share on something that means the same thing in every org.
- A value with meaning, not an ID. If the rule needs the related region, have the automation copy the region’s name or code into the field, not its ID. “West” deploys. Record type is the one to watch: rules read the label, and “make sure the record type’s label and its translations are unique” (module 2).
- A code you control. When names can change, give the related records a code of your own that’s the same in every org, and copy that. You decide what it is, so it never depends on which org made the record.
- A group as the target. The target travels by name (module 2 showed it’s also the part you can’t change after saving). Build the same group in every org, and deploy the rule that points at it.
There may be some situations where an ID is unavoidable. If you truly have one, write it down where the next person will find it: in the rule’s description, and as a step in your deployment that changes the value for each org. But most of the time, when a client comes up with a scenario that seems to need an ID, let’s figure something else out.
How rules sprawl
Section titled “How rules sprawl”Hard-coded IDs break in a day. Sprawl takes years.
It goes like this. Each rule is added for one request, by one team, for a good reason. Rules get added; they rarely get removed. The people who asked move on. A few years later the object has 40 criteria-based rules, a reorg has made half the groups mean something else, and the next team finds 10 left of the 50 (module 4). You’ve used up the resources, and you can’t tell which rules are safe to delete because nobody wrote down why they exist.
None of this needs a new feature. It needs habits.
- Write the reason. Every rule has a label, a rule name “used by the API and managed packages,” and “a description of the sharing rule of up to 1,000 characters” (Create Criteria-Based Sharing Rules). Use the description for the why: who asked, what requirement, and when it can go. Then the rule explains itself in every org it’s deployed to.
- Name them as a set. A rule name that says the object, the bucket and the target reads like a list when you open the Sharing Settings page. A name like “Rule 7” reads like a mystery.
- Keep them with your other metadata. Because every rule on an object lives in that object’s sharing rules file, a change to a rule can be reviewed like any other change, and you can see who added it and when.
- Count them. For each object: how many of the 50 and the 300 are used, and who decides when a new one is added (module 4).
- Ask what happens when something is added. When I review sharing, I ask a simple question: what if a user is added? What happens? For rules, ask it about the values too. What happens when a new region, a new state, a new department appears? If the answer is “someone adds a rule,” sprawl will follow. Guam again.
- Remove what’s no longer needed. A rule nobody needs still writes share rows and still recalculates on every change. Taking it out is part of the design, not a cleanup someday.
What an architect decides here
Section titled “What an architect decides here”- That no sharing rule matches a record ID, and, for any rule that must, where that’s written down and which deployment step changes it.
- For each field a rule reads, what the automation copies into it: a name or a code that means the same in every org.
- Which groups and roles the rules target, and that each one exists, by the same name, in every org.
- The naming pattern for rules and what every description must say.
- Who owns each object’s rule count, and when rules are reviewed and removed.
- How sandboxes are tested: a rule that seems to work in a full sandbox may match nothing in a developer sandbox.
Beyond the exam: what AI changes. An agent that builds a sharing rule for you in one org can only see that org. Ask it to “share these accounts with the West team’s region record,” and an ID is the easiest thing for it to use. Before you accept a rule an agent built, read its criteria for anything that looks like an ID, and read its description: if the agent didn’t write the reason, write it yourself before you deploy.
Explore
Section titled “Explore”- Servers and IDs (opens in a new tab), Salesforce Help (checked 2026-11-07)
- Sandbox Licenses and Storage Limits by Type (opens in a new tab), Salesforce Help (checked 2026-11-07)
- Create Criteria-Based Sharing Rules (opens in a new tab), Salesforce Help (checked 2026-11-07)
- SharingRules (opens in a new tab), Metadata API Developer Guide, Winter ‘27, API version 68.0 (checked 2026-11-07)
