Skip to content

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.

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.

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 ruleHard-coded: don’t do thisShares on a meaning: do this
ObjectAccountAccount
CriteriaRegion Record ID equals a0B5j00000AbC12EAFRegion equals West
Share withPublic group: West SalesPublic group: West Sales
AccessRead/WriteRead/Write
What it means to the next personNothing. 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.

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.”

Why a hard-coded ID breaks Why a hard-coded ID breaks. A sharing rule that matches one record's ID, followed through four orgs. Production: the rule matches, and it works. A full sandbox, just refreshed: records are copied with their IDs, so the rule seems to work, which hides the problem. A developer sandbox: metadata only, no records, so the rule matches nothing. Anything created after the refresh: a new ID in one org that the other never has. Footnote: a rule holds the ID as text, and after the copy each org makes its own IDs. {"author": "Steve Simpson", "blueprint": "1.0.0", "builder": "standard 1.57.1", "built": "2026-11-01", "id": "DGM-SVA-GR-09", "rights": "\u00a9 2026 Twinshark LLC. All rights reserved.", "template": "comparison 0.5.0", "theme": "stevetecharc 1.4.0"} Production where the rule was made The rule matches one record's ID It works Full sandbox, just refreshed metadata and all data Records copied with their IDs The rule seems to work That hides the problem Developer sandbox metadata only The rule arrives, the record doesn't It matches nothing Anything new made after the refresh A new ID in one org The other org never has it A rule holds the ID as text. After the copy, each org makes its own IDs. © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

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.

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.

Share on an ID or on a meaning Share on an ID or on a meaning. Shares on an ID: the value is one record in one org; it matches nothing, or the wrong records, after a deployment, and someone has to fix it by hand each time. Shares on a meaning: a name or a code you control, the same in every org; the target is a group, found by name; it deploys as it is and behaves the same in staging and production. Footnote: if an ID is ever unavoidable, write it in the rule's description and in your deployment steps. {"author": "Steve Simpson", "blueprint": "1.0.0", "builder": "standard 1.57.1", "built": "2026-11-01", "id": "DGM-SVA-GR-10", "rights": "\u00a9 2026 Twinshark LLC. All rights reserved.", "template": "comparison 0.5.0", "theme": "stevetecharc 1.4.0"} Shares on an ID one record in one org Matches nothing, or the wrong records, after a deployment Someone fixes it by hand every time Staging no longer behaves like production Shares on a meaning a name or a code you control The same value in every org The target is a group, found by name Deploys as it is and behaves the same If an ID is ever unavoidable, write it in the rule's description and in your deployment steps. © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge

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.
How sharing rules sprawl How sharing rules sprawl, and the habits that stop it. The sprawl: each rule is added for one request; rules get added and rarely removed; the people who asked move on; years later, 40 of the 50 are used and nobody knows why. The habits: write the reason in the description, up to 1,000 characters; name the rules as a set; keep them in source control with the object's sharing rules file; count them against the 50 and the 300; ask what happens when a new value or user is added; remove what's no longer needed. Footnote: a rule that can't explain itself can't be safely deleted. {"author": "Steve Simpson", "blueprint": "1.0.0", "builder": "standard 1.57.1", "built": "2026-11-01", "id": "DGM-SVA-GR-11", "rights": "\u00a9 2026 Twinshark LLC. All rights reserved.", "template": "comparison 0.5.0", "theme": "stevetecharc 1.4.0"} How rules sprawl slowly, for good reasons Each rule is added for one request Rules get added, rarely removed The people who asked move on Years later, 40 of the 50 used, nobody knows why The habits that stop it from the day a rule is made Write the reason in the description Name the rules as a set Keep them in source control Count them against the 50 and the 300 Ask what happens when a value is added Remove what's no longer needed A rule that can't explain itself can't be safely deleted. © 2026 Twinshark LLC · stevetecharc.com
Click to enlarge
  • 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.