[object Object]

The Scale Problem

Sharing rule recalculation on large user bases can take hours. Public groups with deep hierarchies explode permission-set storage. Territory management at scale requires careful design. The cliff appears around 10,000 active users with criteria-based sharing on objects exceeding 50M records. A full recalc on Account in that scenario can run 6–18 hours, locks portions of the sharing model, and generates the dreaded MAX_SHARING_RULES_FOR_OBJECT exception (limit: 300 owner-based and 300 criteria-based per object). Group membership recalculation triggered by a single role-hierarchy change can ripple across millions of records.

Patterns That Work

Role-hierarchy-based sharing over criteria-based. Sharing rules on standard objects where well-optimized. Apex managed sharing where declarative hits limits. Periodic recalculation during off-hours. The decision tree most architects use:

1. Can role hierarchy + OWD = Private/Read-Only solve it?  -> stop here
2. Can ownership-based sharing rules + public groups solve it? -> use these
3. Is the criteria static and the dataset bounded? -> criteria-based sharing
4. Is sharing dynamic, calculated, or external-system-driven? -> Apex managed sharing

For Apex managed sharing, the canonical pattern:

List<AccountShare> shares = new List<AccountShare>();
for (Account a : scopedAccounts) {
    AccountShare s = new AccountShare();
    s.AccountId = a.Id;
    s.UserOrGroupId = territoryGroupMap.get(a.Region__c);
    s.AccountAccessLevel = 'Edit';
    s.OpportunityAccessLevel = 'Read';
    s.RowCause = Schema.AccountShare.RowCause.Manual;
    shares.add(s);
}
Database.insert(shares, false);

Use a custom RowCause (declared in Setup > Sharing Settings > Apex Sharing Reasons) so your shares survive owner changes and never collide with system-managed rows.

Anti-Patterns

Criteria-based sharing on every object. Deeply nested public groups. Manual shares across millions of records. These scale poorly; refactor before business grows into the problem. Specific anti-patterns to hunt down: nested public groups more than 4 levels deep (each level multiplies group-membership calculation cost), criteria filters that reference formula fields (formula evaluation on every recalc), and Account Teams used as a sharing primitive across thousands of accounts when Territory Management 2.0 was the right answer. Triggers that call insert AccountShare per-record without bulkification will brick a data load.

What Changed in 2026

Spring ‘26 introduced Async Sharing Recalculation as GA — kicked off from Setup or via the runAsyncSharingRule Tooling API endpoint. Long-running recalcs no longer block other admin work. Summer ‘26 expanded Sharing Health to include per-rule cost metrics (CPU time, rows-scanned, memory). Restriction Rules continue to be the preferred mechanism when you need to deny visibility on top of broad sharing, rather than build inverse logic.

Monitoring

Sharing Health dashboards in Setup show rule execution time. Alert when approaching platform limits. Proactive refactoring beats Salesforce Support tickets on platform performance. The metrics worth watching weekly: average recalculation time per rule, group-membership recalc duration, share-table row counts per object, and UserRecordAccess query latency. Wire these to Event Monitoring with a Tableau Next dashboard so trends are visible to architects, not buried in admin emails.

Common Failure Modes

Owner reassignment cascades. Reassigning 50K Accounts to a new owner triggers a full sharing recalculation across all child Opportunities, Cases, and Contacts. Schedule reassignments off-hours and use Data Loader’s “Defer Sharing Calculations” setting (Setup > Defer Sharing Calculations). The second classic: territory model activation in production without a sandbox dry-run — territory hierarchies recalculate every share row referencing a territory, and a botched activation can take days to unwind.

What to do this week

Run the Sharing Health dashboard against your top 5 objects by record count. If any rule recalc time exceeds 30 minutes, file it as a refactor candidate. Ship Defer Sharing Calculations into your Data Loader runbook before the next bulk load.

[object Object]
Share