When should you use Apex Managed Sharing?
You set up record access with the standard tools and they do not cover the case in front of you. That is where Apex Managed Sharing takes over. I still tell people to try sharing rules and the rest of the declarative toolbox first, but some business logic is too odd for a criteria-based rule.
In my experience you know it is time for code when the sharing decision depends on a related object or an external system. Maybe access has to expire after forty-eight hours. Maybe the team structure the business keeps describing does not map to the role hierarchy at all. If a Flow or a sharing rule cannot express the rule, Apex Managed Sharing can.
One caution: do not jump to code too fast. I have seen teams build a whole sharing engine and then realize a better role hierarchy would have done the job. Weigh the Apex vs Flow trade-off before you start writing Share records by hand.
How Apex Managed Sharing works in the real world
Every object that supports sharing has a Share object sitting next to it. A custom object called Project__c gets Project__Share. Standard objects get AccountShare, OpportunityShare and so on.
Apex Managed Sharing means inserting rows into those Share tables. Each row tells Salesforce to give one user or group one level of access to one record, and records why.
The four fields on a Share record
Four fields do the work when you build these records in code.
ParentIdis the Id of the record you are sharing.UserOrGroupIdis the Id of the user or public group getting access.AccessLevelis usually Read or Edit. Some objects allow All, which I use sparingly.RowCauseis the why. On custom objects, define your own Apex Sharing Reasons in the Setup menu.
Pro tip: on custom objects, never set RowCause to Manual in Apex. When the record owner changes, Salesforce deletes every Manual share automatically. A custom Apex Sharing Reason survives the owner change.
Writing the code: a practical example
Here is a snippet I have used plenty of times for sharing a custom object. Bulkify it before you point it at more than one record.
// Create a new share record for a custom object
Project__Share jobShare = new Project__Share();
// Set the record being shared
jobShare.ParentId = 'a01xx0000001Abc';
// Set the user receiving access
jobShare.UserOrGroupId = '005xx000001Xyz';
// Set the access level
jobShare.AccessLevel = 'Edit';
// Use a custom sharing reason defined in Setup
jobShare.RowCause = Schema.Project__Share.RowCause.Special_Project_Team__c;
insert jobShare;
Developers routinely forget the other half of this. When the access is no longer needed, something has to take it away. If a user leaves a project team, your code should find that share record and delete it.
Handling large data volumes
In an org with millions of records, be careful. Recalculating sharing across a huge table slows performance to a crawl. I have written before about managing Salesforce large data volumes, and sharing is a big part of that story. Insert thousands of share rows in batches and keep an eye on your limits.
Testing your sharing logic
Do not assume the share record was created and move on. Query the Share table in your test class and check. Plenty of people skip that step, and it comes back to bite them during a production deployment.
Create a test user, run your sharing logic, then run a SOQL query on MyObject__Share. A row count of zero means something is wrong. Test the negative case too: a user who should not have access should not end up with it.
Key takeaways
- Use Apex Managed Sharing only when the declarative options (sharing rules, the hierarchy) fail you.
- Always use a custom Apex Sharing Reason as the RowCause on custom objects, so shares are not deleted when the owner changes.
- Bulkify your sharing logic. Inserting share records in a loop is a one-way ticket to Governor Limit hell.
- Do not forget the cleanup. If the business logic says access is gone, your code needs to delete the share record.
- Test by querying the Share objects in your test methods.
Apex Managed Sharing is a power tool, and it will cover almost any record access requirement a client throws at you. Reach for it when the declarative tools genuinely cannot do the job.
Leave a Comment