Salesforce Record Sharing Interview Questions — OWD, Role Hierarchy, Sharing Rules Explained
📌 Layered Security Approach
Use multiple tools in combination for maximum field security.
1️⃣ Field-Level Security (FLS) — First Step
- Setup → Object Manager → [Object] → Fields → Set Field-Level Security
- Set sensitive field to Hidden on Sales Rep profile
- Automatically hides it from page layouts, list views, reports, and API responses
2️⃣ Profiles & Permission Sets
- Sales Rep Profile = minimal baseline access
- Use Permission Sets to grant access selectively to authorized users only
- Follow Principle of Least Privilege
3️⃣ Data Masking — Partial Visibility
- Store masked value in a formula field:
****-****-****-1234 - Hide real field via FLS, show only masked formula field to reps
4️⃣ Salesforce Shield — Compliance
- Use Platform Encryption to encrypt fields at rest
- Even if FLS bypassed via API, encrypted data remains unreadable
- Required for PCI-DSS compliance
5️⃣ Enforce FLS in Apex
Schema.DescribeFieldResult dfr =
Account.CreditCardNumber__c.getDescribe();
if (dfr.isAccessible()) {
// safe to read
}
| Layer | Tool | Purpose |
|---|---|---|
| Field hiding | FLS on Profile | Block field entirely |
| Compliance | Shield Encryption | Encrypt at rest |
| Partial view | Masked formula field | Show last 4 digits safely |
| Programmatic | Apex FLS check | Enforce in custom code |
"FLS on the Sales Rep profile is the primary tool — set the field to Hidden which auto-blocks it from layouts, reports and API. For compliance-grade security add Shield Encryption. In Apex, always enforce FLS using isAccessible() or WITH SECURITY_ENFORCED."
📌 Strategy Overview
Two main paths: Keep both orgs + integrate (recommended first) or full org migration (long-term). Integration first is the practical approach.
1️⃣ Salesforce-to-Salesforce (S2S) — Native
- Setup → Salesforce to Salesforce → Enable
- Org A publishes records → Org B subscribes
- You choose which objects and fields to publish — sensitive fields simply not published
- Limitation: Standard objects only, limited customization
2️⃣ Connected App + REST API — Enterprise
- Create Connected App in both orgs with OAuth 2.0
- Use Named Credentials to store endpoint and tokens securely
- Write Apex Callouts to push/pull data
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:OrgB_Credential/services/data/v59.0/sobjects/Account/');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody('{"Name":"Acme Corp"}');
Http http = new Http();
HttpResponse res = http.send(req);
3️⃣ Platform Events / Change Data Capture (CDC)
- Enable CDC on required objects in Org A
- Org B subscribes via Apex Triggers on Change Events
- Only publish events for non-sensitive fields
4️⃣ Salesforce Connect (External Objects)
- Surface Org B data in Org A as External Objects using OData
- Data stays in Org B — no replication, read-only cross-org visibility
| Security Layer | Control |
|---|---|
| Authentication | OAuth 2.0 with least-privilege scopes |
| Field Filtering | Never include sensitive fields in payloads |
| Integration User | Dedicated user with minimum field access |
| Audit | Event Monitoring + Shield in both orgs |
"Use S2S for quick native sharing on standard objects. For complex logic, build Connected App + Named Credential + Apex Callout. The integration user in each org must have bare minimum permissions — interviewers always ask this follow-up."
📌 Core Solution: OWD Private + Role Hierarchy
Step 1 — Set OWD to Private
Setup → Sharing Settings → Org-Wide Defaults → Opportunity, Lead, Account = Private
With Private OWD, users can only see records they own. Role hierarchy then opens visibility upward to managers.
Step 2 — Build Role Hierarchy
VP Sales
└── Regional Manager (East)
└── Sales Rep A
└── Sales Rep B
└── Regional Manager (West)
└── Sales Rep C
└── Sales Rep D
- East Manager sees Rep A and B records ✅
- East Manager CANNOT see West Manager's reps ❌ (cross-branch blocked)
- Same level = no sideways access
Step 3 — Assign Users to Correct Roles
- Setup → Users → [User] → Role field
- This is critical — role hierarchy only works if users are properly assigned
Step 4 — Dynamic Dashboards for Reporting
- Dashboard Settings → View as: Logged-in User
- Each manager automatically sees only their team's data
- No separate dashboard needed per manager
Security Evaluation Order: OWD → Role Hierarchy → Sharing Rules → Manual → Apex
"Set OWD to Private, build role hierarchy matching org chart, assign users to roles. Hierarchy opens access upward automatically — managers see reps below them, cross-branch access is always blocked. Enable 'Grant Access Using Hierarchies' checkbox on objects."
📌 Key Insight
This is the most important concept here: Role Hierarchy only opens access UPWARD — never sideways. Two users in the same role cannot see each other's records when OWD is Private.
Answer — Set OWD to Private
Setup → Sharing Settings → [Object] → Default Internal Access = Private
With OWD = Private:
- A owns Record 1 → only A sees it ✅
- B owns Record 2 → only B sees it ✅
- Neither can see the other's records ✅
⚠️ Why Sharing Rules Won't Help Here
Sharing Rules work at Role or Group level. Since both are in the same role, it would open access to both — not restrict. OWD Private is the correct solution.
What If OWD Cannot Be Private?
- Apex Managed Sharing — programmatically share only with specific users
- Record Ownership — ensure correct user owns each record
| Scenario | Solution |
|---|---|
| OWD = Private | Records auto-restricted by ownership ✅ |
| OWD = Public Read Only | Cannot restrict sideways — redesign OWD |
| Selective sharing needed | Apex Managed Sharing |
"Set OWD to Private — role hierarchy only opens access upward, not sideways. Two users in the same role cannot see each other's records with Private OWD. This is the key concept interviewers test here."
📌 Most Likely Reason — Role Hierarchy
User A (Manager Role) ← sees more records
└── User B (Rep Role) ← sees fewer records
A is ABOVE B → A sees B's records ✅
B is BELOW A → B cannot see A's records ❌
Hierarchy is UPWARD ONLY
Other Possible Reasons
| Reason | How It Happens |
|---|---|
| Role Hierarchy | A's role is above B's — most common answer |
| Manual Sharing | B's records were manually shared with A only |
| Sharing Rule | Rule shares B's role records with A but no reverse rule exists |
| Public Group | A is in a group with access to B's records, B is not |
| Record Ownership | A owns some of B's records (transferred ownership) |
| Apex Sharing | Apex programmatically shared B's records with A only |
"Most likely A is higher than B in the Role Hierarchy — hierarchy opens access upward only, so A sees B's records but B cannot see A's. This is the #1 answer interviewers expect."
📌 Two Requirements
- BU data segregation — each BU sees only their own records
- Cross-unit users — some users see ALL data across all BUs
Step 1 — OWD to Private
Setup → Sharing Settings → All key objects = Private
Step 2 — Role Hierarchy Per Business Unit
CEO └── BU A Head → BU A Manager → BU A Reps └── BU B Head → BU B Manager → BU B Reps └── BU C Head → BU C Manager → BU C Reps
Cross-branch is always blocked ✅ Managers see only their branch ✅
Step 3 — Public Groups Per BU
- Setup → Public Groups → Create one group per BU
- Use groups in Sharing Rules for cleaner management
Step 4 — For Users Who Need ALL Data
| Option | How | Best For |
|---|---|---|
| Highest Role | Place at CEO role — sees all branches | Simple, no code |
| View All Data | Permission Set with "View All Data" | Quick but powerful ⚠️ |
| Apex Sharing | Custom logic with Permission Set check | Granular control |
"OWD Private + separate role branch per BU blocks cross-BU access. For cross-unit users: place them at top of hierarchy OR assign 'View All Data' Permission Set — but always flag View All Data as last resort since it's very powerful."
📌 Key Concept
Profile controls object/field access — NOT record access. Same profile users can absolutely have different record access.
| Reason | How It Gives Extra Access |
|---|---|
| Different Role | Higher role in hierarchy = sees more records below |
| Manual Sharing | Records explicitly shared with one user only |
| Public Group | One user in a group with sharing rule access |
| Permission Set | One has "View All" or "View All Data" permission |
| Record Ownership | Owns more records → sees more with Private OWD |
| Apex Sharing | Trigger/Batch programmatically shared with one user |
| Territory Management | Assigned to more territories = more record access |
| Account/Opp Team | One user added as Team Member on records |
"Profile controls object and field access, not record access. Record access is controlled by OWD, Role Hierarchy, Sharing Rules, Permission Sets, Teams, and Apex Sharing — all of which can differ between two users even on the same profile."
📌 Same Profile + Same Role = Standard Reasons Eliminated
Let's dig deeper into record-level differences.
| Reason | Where to Check |
|---|---|
| Record Ownership | Record detail → Owner field |
| Manual Sharing | Record → Sharing button → see access list |
| Public Group Membership | Setup → Public Groups → Members |
| Permission Set | Setup → Users → User A → Permission Set Assignments |
| Account/Opp Team | Record → Team related list |
| Apex Sharing | Query AccountShare / OpportunityShare object |
| Territory | Setup → Territory → User assignment |
| Queue Ownership | Record Owner = Queue — check queue members |
🔧 How to Diagnose via Apex
List<UserRecordAccess> access = [
SELECT RecordId, HasReadAccess, HasEditAccess
FROM UserRecordAccess
WHERE UserId = :userAId
AND RecordId = :recordId
];
"Same profile and same role eliminates object access and hierarchy. The difference is at record level — check ownership, manual sharing, public group membership, permission sets, or team membership between the two users."
📌 Key Concept
Reports have their own access model via Report Folders. Profile and Role have no effect on report access.
Reasons
| Reason | Where to Check |
|---|---|
| Report Folder Not Shared | Report Folder → Share Settings — #1 reason |
| Public Group Membership | Folder shared with a group, one user not in it |
| Permission Set | "Manage Reports" or "View All Data" on one user only |
| Report in Personal Folder | Personal folders are private — only owner can see |
| Run Reports Permission | Check if missing from User A's profile/permission set |
Report Folder Access Levels
| Level | What User Can Do |
|---|---|
| Viewer | Can run the report |
| Editor | Can run and edit the report |
| Manager | Can run, edit, delete and manage folder |
"Report access is controlled by Report Folder sharing — not profile or role. User A likely doesn't have access to the folder containing the report, either directly or via a Public Group. Always lead with this answer in interviews."
📌 Best Practice: Profile = Restrictive, Permission Sets = Additive
Step 1 — Set Baseline on Profile (Most Restrictive)
- Setup → Object Manager → Object → Fields → Set Field-Level Security
- Mark all sensitive fields as Hidden on all profiles by default
- No department sees the field unless explicitly granted
Step 2 — Create Permission Sets Per Department
Permission Set: Finance_Field_Access → Custom Field A: Visible + Editable → Custom Field B: Visible + Read Only Permission Set: HR_Field_Access → Custom Field C: Visible + Editable → Custom Field A: Hidden Permission Set: Sales_Field_Access → Custom Field B: Visible + Read Only
Step 3 — Permission Set Groups (Advanced)
- Bundle multiple Permission Sets per department into a Permission Set Group
- Assign group to department users instead of individual sets
Practical Example
| Field | Finance | HR | Sales |
|---|---|---|---|
| Salary__c | View + Edit | View Only | Hidden |
| Budget__c | View + Edit | Hidden | View Only |
| Commission__c | View Only | Hidden | View + Edit |
⚠️ Page Layout alone is NOT enough — users can still see fields via API or List Views. Always use FLS + Page Layout together.
"Set most restrictive FLS on base profile, then use Permission Sets to open access per department — this is the Salesforce best practice of least privilege with additive permissions. Mention Permission Set Groups for advanced knowledge."
📌 Constraint: OWD stays Private
We need to bypass the sharing model entirely without touching OWD.
Option 1 — "View All" Object Permission ✅ Best Answer
- Setup → Permission Sets → New → Object Settings → Case → Enable View All
- Assign this Permission Set to ALL users
| Permission | What It Does |
|---|---|
| View All | See ALL records regardless of OWD, Role, Sharing |
| Modify All | See + Edit + Delete ALL records |
OWD remains Private ✅ | Hierarchy doesn't matter ✅ | Everyone sees all cases ✅
Option 2 — Criteria-Based Sharing Rule with All Internal Users
- Setup → Sharing Settings → Case Sharing Rules → New
- Share: All Cases → With: All Internal Users (built-in Public Group) → Read Only
- OWD remains Private ✅ | No hierarchy dependency ✅
Option 3 — "View All Data" System Permission ⚠️
- Grants access to every record in the org
- Too powerful — only for trusted admin-level users
| Solution | OWD Changes? | Recommended |
|---|---|---|
| View All Permission Set | No | ✅ Best |
| Criteria Sharing + All Internal Users | No | ✅ Good Alternative |
| View All Data | No | ⚠️ Last Resort |
"Assign a Permission Set with 'View All' on the Case object to all users — this bypasses OWD, role hierarchy, and sharing rules entirely without changing OWD. Mention Criteria-Based Sharing Rule with All Internal Users as an alternative."
📌 The Challenge
By default, Role Hierarchy gives managers automatic access to subordinates' records. We need to break this upward visibility for the Feedback object.
Step 1 — OWD to Private
Setup → Sharing Settings → Feedback__c → Default Internal Access = Private
Step 2 — Uncheck "Grant Access Using Hierarchies" ✅ KEY STEP
Setup → Sharing Settings → Feedback__c → Uncheck "Grant Access Using Hierarchies"
| Setting | Behaviour |
|---|---|
| Enabled (default) | Manager automatically sees subordinate's records |
| Disabled ✅ | Manager CANNOT see subordinate's records |
⚠️ This setting only available on Custom Objects!
Step 3 — CEO Access Only
Option A — Permission Set with View All (Simplest)
Setup → Permission Sets → CEO_Feedback_Access → Object Settings → Feedback__c → Enable "View All" → Assign ONLY to CEO user
Option B — Apex Managed Sharing (Trigger)
trigger FeedbackSharingTrigger on Feedback__c (after insert) {
User ceo = [SELECT Id FROM User
WHERE UserRole.Name = 'CEO' LIMIT 1];
List<Feedback__Share> shares = new List<Feedback__Share>();
for (Feedback__c fb : Trigger.new) {
Feedback__Share s = new Feedback__Share();
s.ParentId = fb.Id;
s.UserOrGroupId = ceo.Id;
s.AccessLevel = 'Read';
s.RowCause = Schema.Feedback__Share.RowCause.Manual;
shares.add(s);
}
insert shares;
}
"Set OWD to Private + uncheck 'Grant Access Using Hierarchies' on the custom Feedback object — this is the #1 answer interviewers want. This blocks managers from seeing upward. Then assign 'View All' Permission Set exclusively to CEO. This setting is only available on Custom Objects — always mention this!"
📌 Answer
✅ Can READ — Yes ❌ Can EDIT — No
Why? — Two Independent Layers
| Layer | Setting | Result |
|---|---|---|
| OWD | Public Read/Write (Default) | All users can see all records |
| Profile | Read Only | User can NEVER edit any record |
| Ownership | User is Owner | Gives visibility — NOT edit rights |
⚠️ The Catch — Ownership Does NOT Override Profile
This is what interviewers want to hear:
- Being the record owner does NOT grant edit access if Profile restricts it
- OWD and Ownership control record visibility
- Profile controls what operations you can perform
- Profile = Read Only → no edit anywhere on that object regardless of ownership
Security Evaluation Order
Profile (CRUD) → FLS → OWD → Role Hierarchy → Sharing Rules → Ownership Profile is evaluated FIRST — if it says Read Only, edit is blocked at the very first gate ❌
"Profile controls object-level CRUD. If profile is Read Only, the user cannot edit any record regardless of OWD or ownership — profile is always evaluated first in Salesforce's security chain."
📌 Answer: IT DEPENDS on the Relationship Type!
Scenario 1 — Master-Detail Relationship ❌ NOT Possible
- Child OWD is automatically set to "Controlled by Parent"
- Child access cannot exceed parent access level
- Option is grayed out in Setup — cannot be changed
Contact (Parent) OWD = Public Read Only
↓ (Master-Detail)
College (Child) OWD = Controlled by Parent
↓
College = Public Read Only automatically ❌
Cannot give Read/Write via OWD!
Scenario 2 — Lookup Relationship ✅ Possible!
- Child has its own independent OWD setting
- Can set College OWD to Public Read/Write regardless of Contact
Contact (Parent) OWD = Public Read Only
↓ (Lookup — loose coupling)
College (Child) OWD = Public Read/Write ✅
↓
College = Read/Write independently!
| Factor | Master-Detail | Lookup |
|---|---|---|
| Child OWD | Controlled by Parent | Independent |
| Can set Read/Write on Child? | ❌ No | ✅ Yes |
| OWD in Setup | Grayed Out | Configurable |
| Parent deletion | Child deleted too | Child remains |
"It depends on relationship type. In Master-Detail, child OWD is 'Controlled by Parent' and is grayed out — cannot be changed. In Lookup, child has independent OWD and can be set to Public Read/Write regardless of parent's OWD."
📌 This is NOT a Problem — It's Expected Behaviour!
| Layer | Setting | Meaning |
|---|---|---|
| Profile | Read Access ✅ | Both CAN read Contact records they have access to |
| OWD | Private | Both can ONLY see records they OWN |
The Core Logic
Profile gives READ access
↓
Opens the DOOR to Contact object
↓
OWD = Private controls WHICH records inside
↓
A can only see A's records ✅
B can only see B's records ✅
Neither can see each other's records ✅ — by design!
Think of it like a Library: Profile = Library Card (you can enter). OWD Private = You can only read books you borrowed yourself.
If Mutual Access IS Required
| Solution | How |
|---|---|
| Change OWD | Set to Public Read Only — everyone sees all |
| Sharing Rules | Share records of Role A with User B and vice versa |
| Public Group | Add A and B to same group + create sharing rule |
| Manual Sharing | Each manually shares their records with the other |
"This is expected behavior, not a problem. Profile gives object-level Read access — users CAN read Contacts they have access to. OWD Private controls WHICH records — only their own. Profile and OWD are two completely independent layers controlling different things."
📌 Direct Answer: You CANNOT do it directly. Use Record Types!
Page Layouts are assigned at Profile level. Same Profile = Same Page Layout by default. Record Types are the only bridge.
Step 1 — Create Two Record Types
Setup → Object Manager → Contact → Record Types → New → Record Type 1: Type_A → Record Type 2: Type_B
Step 2 — Create Two Page Layouts
Setup → Object Manager → Contact → Page Layouts → New → Layout_For_User_A → Layout_For_User_B
Step 3 — Map Record Type → Page Layout
Setup → Object Manager → Contact → Page Layout Assignment → Edit Assignment Profile | Record Type | Page Layout Sales Profile | Type_A | Layout_For_User_A Sales Profile | Type_B | Layout_For_User_B
Step 4 — Assign Record Types to Users
Setup → Profiles → [Profile] → Record Type Settings → Contact → Assign Type_A to User A, Type_B to User B (Or via Permission Sets)
| Element | Controls |
|---|---|
| Profile | Which Record Types are available |
| Record Type | Which Page Layout is shown |
| Page Layout | What fields/buttons user sees |
Assignment chain: Profile → Record Type → Page Layout
"You cannot directly assign different page layouts to same profile users. Record Types are the only way — assign different Record Types to each user, then map each Record Type to a different Page Layout. Also mention Record Types control picklist values per user for added depth."
📌 Answer: YES — They CAN still see the records!
Key Concept: Record Type Access ≠ Record Visibility
| Layer | Controls |
|---|---|
| Record Type Access | Which record types user can CREATE or SELECT |
| OWD + Sharing | Which records user can SEE |
These are completely independent of each other!
What Happens Without Record Type Access
- ❌ Cannot create a new record with that Record Type
- ❌ Cannot select that Record Type from picklist
- ❌ Will NOT see that Record Type's page layout
- ✅ CAN still view existing records with that Record Type
- ✅ CAN still edit those records (if OWD/Sharing allows)
What Controls Record Visibility?
OWD → Role Hierarchy → Sharing Rules → Manual Sharing Record Type assignment has ZERO impact on this chain!
Real World Example
User A only has access to Standard Record Type. A record exists with Premium Record Type. User A can still open and read that Premium record — but will see it with their default page layout, not the Premium layout. They just cannot create a new Premium record.
"Yes, the user can still see the record. Record Type access only controls which record types a user can create — record visibility is always governed by OWD, Role Hierarchy, and Sharing Rules, which are completely independent of Record Type access."
📌 Scenario
Person 1 → Record Owner Person 2 → Role Hierarchy (Manager of Person 1) Person 3 → Owner-Based Sharing Rule Person 4 → Manual Sharing Person 5 → Apex Managed Sharing Admin changes owner → Who still has access?
Person-by-Person Analysis
| Person | Access Method | After Owner Change |
|---|---|---|
| Person 1 | Record Owner | ❌ Loses Access |
| Person 2 | Role Hierarchy | ❌ Recalculated — based on new owner |
| Person 3 | Owner-Based Sharing Rule | ❌ Loses Access |
| Person 3 | Criteria-Based Sharing Rule | ✅ Retains if criteria still match |
| Person 4 | Manual Sharing | ❌ DELETED automatically |
| Person 5 | Apex — Manual RowCause | ❌ DELETED |
| Person 5 | Apex — Custom RowCause | ✅ ALWAYS SURVIVES |
// Custom RowCause = SURVIVES ownership change ✅ share.RowCause = Schema.AccountShare.RowCause.MyCustomReason__c; // Manual RowCause = DELETED on ownership change ❌ share.RowCause = Schema.AccountShare.RowCause.Manual;
⚠️ Where "No One Has Access" Falls Short
Your interviewer's simplified answer is acceptable at basic level — but technically Person 3 retains access if Criteria-Based Sharing Rule criteria still match, and Person 5 retains access if Apex used a Custom RowCause.
"When ownership changes, Salesforce recalculates all sharing. Owner, Role Hierarchy, Manual shares are removed. Owner-Based Rules are recalculated. Criteria-Based Rules persist if criteria still match. Apex Sharing with Custom RowCause is the ONLY sharing guaranteed to always survive an ownership change."
📌 Best Practice Flow
Step 1 — Freeze old user (don't delete immediately) Step 2 — Mass Transfer all records to new rep Step 3 — Deactivate old user Step 4 — Assign new rep correct Role + Profile ⚠️ NEVER delete a user in Salesforce — always deactivate!
Option 1 — Mass Transfer Records ✅ Best
Setup → Mass Transfer Records → Select Object (Account, Opportunity etc.) → Transfer From: Old Executive → Transfer To: New Rep
New rep becomes owner → full access automatically ✅ Works perfectly with OWD Private ✅
Option 2 — Sharing Rule (Preserve Ownership History)
- Create Owner-Based Sharing Rule
- Share records owned by [Old User's Role] with [New Rep]
- Ownership stays intact — useful for audit purposes
Option 3 — Queue Ownership (Team Approach)
- Assign records to a Queue instead of individual users
- All queue members get access automatically
- New reps just need to be added to the queue
| Option | Best When |
|---|---|
| Mass Transfer | New rep is direct replacement, full ownership needed |
| Sharing Rule | Ownership history must be preserved |
| Queue | Team manages shared records |
"Freeze old user first, then use Mass Transfer Records to bulk transfer ownership to new rep, then deactivate the old user. Never delete a user — always deactivate to preserve audit trail. This ensures new rep gets full access via ownership with OWD Private."
📌 Answer: YES — Restricted fields WILL be fetched
Two Completely Different Layers
| Keyword | Controls |
|---|---|
| with/without sharing | Record-level access (WHICH records) |
| FLS | Field-level access (WHICH fields) |
without sharing bypasses record visibility — but NEVER bypasses FLS automatically!
Case 1 — Without FLS Check (Default)
public without sharing class FetchData {
public static void run() {
List<Account> accs = [
SELECT Id, Name, CreditScore__c FROM Account
];
// ✅ ALL records returned
// ✅ ALL fields returned including restricted ones ⚠️
}
}
Case 2 — WITH SECURITY_ENFORCED
List<Account> accs = [
SELECT Id, Name, CreditScore__c FROM Account
WITH SECURITY_ENFORCED // ← Enforces FLS
];
// ❌ Throws QueryException if user lacks FLS on any field
Case 3 — Security.stripInaccessible (Best Practice)
List<Account> accs = [SELECT Id, Name, CreditScore__c FROM Account];
SObjectAccessDecision d = Security.stripInaccessible(
AccessType.READABLE, accs
);
List<Account> safe = d.getRecords();
// ✅ Records returned, restricted fields automatically STRIPPED
| Approach | FLS Respected? | Error? |
|---|---|---|
| without sharing alone | ❌ No | ❌ No |
| WITH SECURITY_ENFORCED | ✅ Yes | ✅ Yes — exception |
| Security.stripInaccessible | ✅ Yes | ❌ No — graceful |
"without sharing bypasses record-level sharing only — it does NOT enforce FLS. All restricted fields are returned without error. Use Security.stripInaccessible() (modern approach) or WITH SECURITY_ENFORCED to properly respect FLS in Apex."
📌 Answer: Records ARE fetched, Restricted Fields STILL returned, No Error
Correction to Common Assumption
| Scenario | Records Fetched? | Restricted Fields? | Error? |
|---|---|---|---|
| without sharing | ✅ ALL records | ✅ Returned ⚠️ | ❌ No |
| with sharing | ✅ Only accessible records | ✅ Still returned ⚠️ | ❌ No |
| with sharing + WITH SECURITY_ENFORCED | ✅ Accessible only | ❌ Exception | ✅ Yes |
| with sharing + stripInaccessible | ✅ Accessible only | ❌ Stripped | ❌ No |
Golden Rule
with/without sharing = Record Level (WHO sees WHICH records) FLS = Field Level (WHO sees WHICH fields) with sharing does NOT automatically enforce FLS! FLS must be enforced SEPARATELY in Apex code!
Key Difference Between Both Scenarios
without sharing → ALL records returned (ignores sharing)
ALL restricted fields returned (ignores FLS)
with sharing → ONLY accessible records returned (respects sharing)
ALL restricted fields STILL returned (FLS still ignored!)
FLS behaviour is IDENTICAL in both unless explicitly enforced!
"with sharing only enforces record-level sharing — it does NOT enforce FLS. Restricted fields are still returned without any error. SOQL never throws errors for FLS violations unless WITH SECURITY_ENFORCED is explicitly used."
📌 What is Apex Sharing Reason?
Also called RowCause — it is a label that explains WHY a record was shared via Apex Managed Sharing.
AccountShare share = new AccountShare(); share.AccountId = recordId; share.UserOrGroupId = userId; share.AccountAccessLevel = 'Read'; share.RowCause = Schema.AccountShare.RowCause.MyCustomReason__c; // ← Sharing Reason insert share;
Two Types of RowCause
| Type | Created By | Survives Owner Change |
|---|---|---|
| Standard (Manual, Owner, Rule) | Built-in by Salesforce | ❌ No (Manual deleted) |
| Custom (Apex Sharing Reason) | Developer via Setup | ✅ Always |
🔑 Maximum Limit
Maximum 10 custom Apex Sharing Reasons per object
How to Create
Setup → Object Manager → Account → Apex Sharing Reasons → New → Label: Territory Based Sharing → Save // Use in Apex: share.RowCause = Schema.AccountShare.RowCause.Territory_Based_Sharing__c;
How to Delete Shares by RowCause
// Delete ONLY shares with your custom RowCause
List<AccountShare> toDelete = [
SELECT Id FROM AccountShare
WHERE RowCause = 'Territory_Based_Sharing__c'
AND AccountId = :recordId
];
delete toDelete;
⚠️ You can ONLY delete shares where RowCause is your custom reason. Cannot delete standard RowCause shares via Apex.
"Apex Sharing Reason (RowCause) identifies WHY a record was shared. You can create a maximum of 10 custom reasons per object. Custom RowCause shares survive ownership changes — unlike Manual shares — and can be selectively deleted by RowCause in Apex."
| Factor | Manual Sharing | Apex Defined |
|---|---|---|
| Who creates | End User / Admin via UI | Developer via Setup + Apex |
| RowCause value | Manual | Custom e.g. MyReason__c |
| Survives owner change | ❌ Deleted automatically | ✅ Persists always |
| Can delete via Apex | ❌ No | ✅ Yes |
| Scalability | ❌ One record at a time | ✅ Bulk via Apex |
| Business logic | ❌ No logic — manual only | ✅ Complex logic driven |
| Max limit | No limit | 10 per object |
🔑 #1 Difference — Ownership Change
Ownership Changes
↓
Manual Share (RowCause = Manual) → ❌ DELETED automatically
Apex Share (RowCause = Custom__c) → ✅ SURVIVES — stays intact
Real World Example
Manual Sharing: → Admin manually shares Account with new rep → Ownership changes next month → Share is GONE ❌ — admin has to reshare manually Apex Defined Sharing: → Apex trigger shares based on Territory field → Ownership changes next month → Share SURVIVES ✅ — no manual intervention needed
"Manual sharing uses standard RowCause and is deleted automatically on ownership change. Apex Defined sharing uses custom RowCause, always survives ownership changes, supports complex business logic, and can be selectively deleted — making it more reliable for enterprise requirements."
📌 Answer
When you delete an Apex Sharing Reason from Setup:
- ✅ Sharing Reason is deleted
- ✅ ALL associated share records are automatically deleted
- ❌ ALL users who had access via that reason immediately lose access
- ❌ Cannot be undone — shares must be recreated via Apex
What Exactly Happens
Admin deletes Apex Sharing Reason from Setup
↓
Salesforce finds ALL AccountShare records
where RowCause = deleted sharing reason
↓
ALL those share records deleted instantly
↓
ALL users lose access to those records ❌
⚠️ Salesforce Warning
Before deleting, Salesforce shows a warning: "Deleting this sharing reason will delete all associated sharing records. Users who have access only through this sharing reason will lose their access."
Safe Deletion Best Practice
// Step 1 — Delete share records via Apex FIRST
List<AccountShare> toDelete = [
SELECT Id FROM AccountShare
WHERE RowCause = 'Territory_Access__c'
];
delete toDelete;
// Step 2 — Then delete Sharing Reason from Setup safely
| Point | Detail |
|---|---|
| Share records deleted? | ✅ Yes — automatically |
| User access lost? | ✅ Yes — immediately |
| Can be undone? | ❌ No — permanent |
| Recreating reason restores shares? | ❌ No — must recreate via Apex |
"Deleting an Apex Sharing Reason automatically deletes ALL associated share records, causing all users who had access only through that reason to immediately lose access. This is irreversible — shares must be recreated via Apex if needed again."
📌 Two Separate Layers to Configure
| Layer | Controls |
|---|---|
| Profile/Permission Set | CRUD operations on object |
| Role Hierarchy + OWD | Which records they can see |
Step 1 — Set OWD to Private
Setup → Sharing Settings → Account → Default Internal Access = Private
Step 2 — Create Role Hierarchy
Senior Manager
└── Manager
└── Team Lead
└── Team Member
Step 3 — Create Profiles with Correct CRUD
| Profile | Create | Read | Edit | Delete |
|---|---|---|---|---|
| Senior_Manager_Profile | ❌ | ✅ | ❌ | ❌ |
| Manager_Profile | ❌ | ✅ | ❌ | ❌ |
| Team_Lead_Profile | ✅ | ✅ | ✅ | ✅ |
| Team_Member_Profile | ✅ | ✅ | ✅ | ✅ |
Final Access Matrix
| Role | CRUD | Records Visible |
|---|---|---|
| Senior Manager | Read Only | Everyone's records (top of hierarchy) |
| Manager | Read Only | Team Lead + Team Member records |
| Team Lead | Full CRUD | Team Member + Own records |
| Team Member | Full CRUD | Own records only |
⚠️ Tricky Part Interviewers Love
Manager SEES Team Lead's Account record ✅ (Role Hierarchy) Manager tries to EDIT → ❌ BLOCKED (Profile = Read Only) Profile CRUD is evaluated FIRST — Role Hierarchy only gives visibility!
"Set OWD to Private, build role hierarchy top to bottom, create separate profiles — Senior Manager and Manager with Read Only on Account, Team Lead and Team Member with full CRUD. Role hierarchy controls record visibility while profile controls operations on those records."
📌 What is CRUD?
CRUD = Create, Read, Update, Delete — the 4 basic object-level permissions controlled by Profile/Permission Set.
| Letter | Operation | Meaning |
|---|---|---|
| C | Create | Can create new records |
| R | Read | Can view/see records |
| U | Update | Can edit existing records |
| D | Delete | Can delete records |
Can User A Delete User B's Records?
TWO layers must BOTH allow it: Layer 1 — Profile CRUD → Does user have DELETE permission? Layer 2 — Record Access → Does user have access to THAT record? BOTH must be YES to delete ✅
| CRUD Delete | OWD | Role | Can Delete B's Record? |
|---|---|---|---|
| ✅ | Public R/W | Any | ✅ Yes |
| ✅ | Private | A above B | ✅ Yes |
| ✅ | Private | Same level | ❌ No |
| ✅ | Private | B above A | ❌ No |
| ❌ | Any | Any | ❌ No |
CRUD (Profile) = WHAT operations you CAN perform Record Access = WHICH records you can perform it ON Delete Permission ≠ Can delete ANY record Delete Permission = Can delete records YOU HAVE ACCESS TO
"CRUD = Create, Read, Update, Delete — object level permissions set via Profile. Having Delete access alone doesn't mean you can delete anyone's records. You can only delete records you already have access to — which is controlled by OWD, Role Hierarchy, and Sharing Rules together."