Sharing is the part of Salesforce where wrong decisions hide the longest. An over-permissive model leaks data quietly for years. An over-engineered one turns every ownership change into a recalculation storm. And both kinds of orgs look fine in a demo. As a certified Sharing and Visibility Architect I have inherited plenty of both, so this is the way I think about record access when I design it from scratch.
Get the layers straight first
Confusion about sharing usually starts with mixing up the three questions the platform answers separately:
- What can you do with the object? Object permissions (CRUD) from profiles and permission sets.
- Which fields can you see or edit? Field-level security.
- Which records can you access? The sharing model: org-wide defaults, role hierarchy, rules, teams, manual and programmatic shares.
CRUD and FLS are gates, sharing is a filter. A user with Read on Case but no shared records sees an empty list, and a user with access to a record but no Read on the object sees nothing at all. Every access bug report I triage starts by finding which of the three layers is doing the blocking.
Start restrictive, open with intention
Org-wide defaults are the foundation, and the principle is boring but crucial: set OWD to the most restrictive level the business can tolerate, then grant access upward through deliberate mechanisms. Going the other way (public defaults, then trying to hide things later) is close to irreversible once processes and reports depend on broad visibility.
In practice, for a typical B2B org I ask, object by object: does a random rep need to see all accounts? All cases? Usually the honest answer is Private or Public Read Only, with specific teams opened up through rules. When someone insists on Public Read/Write "to keep things simple", I ask what happens when the org signs its first enterprise customer with a confidentiality rider. That conversation is cheaper now than during the audit.
The role hierarchy is not your org chart
The hierarchy exists to answer one question: who needs to see the records of the people below them? It is a data access structure, not an HR document. Two consequences:
- Keep it shallow. Every level exists because someone at that level genuinely manages record visibility. Ten-level hierarchies mirroring the org chart mostly create recalculation cost and confusion.
- Integration and system users go in a dedicated branch near the top, isolated from sharing rules. This is also the fix for ownership skew, which I covered in the large data volumes guide: a user owning millions of records must not participate in the sharing math that regular users do.
Choosing the right sharing mechanism
Once OWDs are restrictive, you open access with the narrowest tool that satisfies the requirement:
- Role hierarchy for vertical, manager-sees-team access.
- Sharing rules (owner-based or criteria-based) for stable, structural grants: "EMEA support sees EMEA cases". They are declarative, auditable and fast, and they should be your default answer.
- Account, Opportunity and Case Teams for per-record collaboration that changes with the deal or the case. Teams beat manual sharing because they carry roles and survive ownership changes better.
- Manual sharing for true one-off exceptions. If admins are doing manual shares in bulk, that is a missing rule, not a process.
- Apex managed sharing when the logic is genuinely dynamic: access derived from junction objects, external agreements, territory-like custom structures. Powerful, and expensive to maintain, so it comes last.
When Apex managed sharing is justified, treat share records like any other data your code owns:
CaseShare share = new CaseShare(
CaseId = c.Id,
UserOrGroupId = brokerGroupId,
CaseAccessLevel = 'Read',
RowCause = 'Broker_Agreement__c' // custom Apex sharing reason
);
insert share;
Always use a custom row cause. It documents why the share exists, it survives ownership changes (manual shares do not), and it lets your cleanup job delete exactly its own shares and nothing else. And make the logic idempotent: recalculating a record's shares should converge to the same result no matter how many times it runs.
Public groups are your friend. Grant to groups, put users and roles inside groups, and membership changes stop being sharing recalculations. A share pointing at one group of 200 users is one row; 200 user shares are 200 rows that all need maintenance.
Restriction rules and the newer tools
Classic sharing only ever adds access, which historically made "everyone except contractors" awkward. Restriction rules changed that: they filter records a user would otherwise see, and they are the clean answer for narrow cases like hiding internal-only activities from a partner-facing role. Scoping rules similarly reduce default visible data without changing access itself. They complement, not replace, a well-designed OWD baseline, and I use them sparingly precisely because subtractive logic is harder to reason about when debugging access.
Also worth knowing where the edges are: Experience Cloud sites bring sharing sets and share groups (external users do not use the role hierarchy the same way), and Territory Management adds its own grant layer for sales orgs that genuinely sell by overlapping geography or segment. Both are their own articles, but the design principle carries over unchanged: restrictive base, deliberate grants, narrowest tool.
Performance: where sharing models go to die
Every share is a row in a share table, and every OWD change, role move, or rule edit triggers recalculation over those rows. At small scale nobody notices. At LDV scale, sharing design and performance are the same topic:
- Changing an OWD on an object with 30 million records is a maintenance-window event, not a Tuesday afternoon click.
- Deep hierarchies multiply implicit shares. Every extra level adds group membership math the platform maintains for you, at a cost.
- Criteria-based rules on frequently-updated fields recalculate constantly. A rule keyed on a stable field beats one keyed on a status that changes five times per record.
- Bulk ownership transfers on skewed users can lock an org up. Plan them, use Defer Sharing Rules where appropriate, and run them off-hours.
How I validate a sharing design
Before calling a model done, I walk it through concrete personas: a new rep in Chile, a support manager in EMEA, a partner user, the integration user, an executive with a dashboard. For each one: what should they see, what do they actually see, and which mechanism delivers it? If any answer is "not sure, let me check", the model is not documented enough to survive its first admin handover. A one-page matrix of object, OWD, and grant mechanisms per persona has saved every project I have used it on.