Sharing Settings Interview Questions – Real Scenario Practice

Prepare for Salesforce sharing and security interviews with practical, scenario-based questions that test both sharing concepts and problem-solving skills.

1. Role Hierarchy + OWD for Hierarchical Access

Question: A user should see only their own records, but managers should see team records. How do you design role hierarchy + OWD?

Answer: Use Role Hierarchy with OWD set to Private:

Key Points:

2. Sharing Rules vs Manual Sharing

Question: You need to give access to a specific group for some records only. Do you use sharing rules or manual sharing?

Answer: Choose based on volume and pattern:

Aspect Sharing Rules Manual Sharing
Use Case Systematic, repeatable access (e.g., all Oppty for Region = "West" to West Sales group) One-off, ad-hoc access (e.g., share specific deal with executive)
Automation ✅ Automatic, rule-based ❌ Manual action per record
Rule Limit Max 300 rules per org No limit (but slow at scale)
Criteria Based on owner role, territory, or field value User/Group/Role individually

Best Practice: Use Sharing Rules for predictable patterns, Manual Sharing for exceptions.

3. Apex Sharing for Dynamic Access

Question: A record should be private, but certain users get access dynamically via logic. How do you implement Apex sharing?

Answer: Create sharing records programmatically:


// Apex: Share Opportunity with specific user if condition is met
public class OpportunityShareService {

    public static void shareWithApprover(List<Opportunity> opportunities) {
        List<OpportunityShare> shares = new List<OpportunityShare>();

        for (Opportunity opp : opportunities) {
            // Only share if amount > $100k and approver exists
            if (opp.Amount > 100000 && opp.ApproverIds__c != null) {
                List<Id> approverIds = opp.ApproverIds__c.split(';');

                for (Id approverId : approverIds) {
                    OpportunityShare share = new OpportunityShare();
                    share.OpportunityId = opp.Id;
                    share.UserOrGroupId = approverId;
                    share.OpportunityAccessLevel = 'Edit';  // Read or Edit
                    shares.add(share);
                }
            }
        }

        if (!shares.isEmpty()) {
            insert shares;
        }
    }

    // Remove access when condition no longer met
    public static void removeApproverShare(List<Opportunity> opportunities) {
        List<Id> oppIds = new List<Id>(new Map<Id, Opportunity>(opportunities).keySet());

        List<OpportunityShare> sharesToDelete = [
            SELECT Id
            FROM OpportunityShare
            WHERE OpportunityId IN :oppIds
            AND RowCause = 'Manual'
        ];

        if (!sharesToDelete.isEmpty()) {
            delete sharesToDelete;
        }
    }
}

// Trigger
trigger OpportunitySharingTrigger on Opportunity (after insert, after update) {
    if (Trigger.isAfter) {
        OpportunityShareService.shareWithApprover(Trigger.new);
        OpportunityShareService.removeApproverShare(Trigger.new);
    }
}
  

Key Points:

4. Profile vs Permission Set vs Sharing

Question: Difference between Profile vs Permission Set vs Sharing in real scenario?

Answer: Three different layers of access control:

Layer What It Controls Scope Example
Profile Object access, field access, system permissions User-wide Sales Profile can't access Finance Object
Permission Set Extends profile permissions (add, never remove) User-assigned Add "Manage Quotes" to Sales Rep temporarily
Sharing Record-level access control Per-record Sales Rep A can see Opp X but not Opp Y

Real Scenario:


Sales Profile (base permissions)
├─ Can access Account, Opportunity objects
├─ Cannot access Finance object
└─ Cannot see phone field

+ Permission Set (temporary)
├─ Add "Approve Quotes" ability
└─ Keep all other restrictions

+ Sharing (record control)
├─ Sally can see Account 123
├─ Sally cannot see Account 456
└─ Both accounts visible to Manager due to role hierarchy
  

5. Field-Level Security vs Object-Level vs Record-Level

Question: How do you handle field-level security vs object-level vs record-level access?

Answer: Three layers of restriction:

Level Controls Set By Example
Object-Level Can user access object at all? Profile / Permission Set Sales user cannot access Finance object
Field-Level Can user see/edit specific fields? Profile / Permission Set Sales user can see Amount but not Cost__c
Record-Level Can user access specific records? OWD / Sharing Rules / Apex Sharing Sales user can see their own Oppty, not competitor's

Access Check Order (top to bottom):


1. Can user access the Object? (OWD)
2. Can user access the Field? (FLS in Profile/PermSet)
3. Can user see this Record? (Sharing Rules / Apex Sharing)
4. Can user Edit this Field? (FLS for Edit + Record Access)

If ANY layer says NO, access is DENIED.
  

Example:


Sales user:
✅ Can access Opportunity object (OWD = Private)
✅ Can see Amount field (FLS allows read)
❌ Cannot see this specific Opp (not owner, not shared, manager)
❌ Result: User cannot see record

---

Manager:
✅ Can access Opportunity object
✅ Can see Amount field
✅ Can see Opp because below Sales Rep in hierarchy
✅ Can edit Amount (FLS allows edit)
✅ Result: Manager can view and edit
  

6. User Can See Record But Not Edit

Question: A user can see a record but not edit it. What could be the reasons?

Answer: Check in this order:

  1. Field-Level Security (FLS) – Field hidden or read-only in Profile
    • Check: Setup → Users → Profiles → Profile Name → Field Permissions
    • Fix: Grant Read/Write on field
  2. Record-Level Access – User has Read-only sharing, not Edit
    • Check: Record Sharing Details → see user's access level
    • Fix: Change OpportunityShare.AccessLevel = 'Edit'
  3. Object-Level Permissions – Profile can't edit object type
    • Check: Profile → Object Permissions → Edit access
    • Fix: Enable Edit on object
  4. Org-Level Restrictions – Org-wide defaults prevent edit
    • Check: Setup → Sharing Settings → OWD for object
    • Fix: Change to Read/Write or controlled by sharing rules
  5. Record Locked/Approval – Record in approved stage
    • Check: Approval process locked record
    • Fix: Unlock record or use bypass sharing

7. Public Groups vs Roles

Question: When would you use Public Groups vs Roles?

Answer: Different purposes and behaviors:

Aspect Roles Public Groups
Hierarchy Hierarchical (tree structure) Flat (just membership)
Inheritance Parent sees child records No inheritance
Use Case Org structure, manager visibility Cross-functional teams, skill-based groups
Example CEO → Manager → Sales Rep "Finance Approvers", "East Coast Team", "Data Analysts"
Sharing Rules Can use "Based on Role" Can't (must use "Based on Role" for hierarchy)

Scenario: Share records to "Finance Team" (includes people from Sales, Ops, Legal)


Solution: Use Public Group "Finance Team" with members from different roles.
Sharing Rule: IF Approver_Group__c = 'Finance', share with PublicGroup.FinanceTeam
  

8. Community User Related Records Access

Question: A community user needs limited access to related records. How do you design external sharing model?

Answer: Use Account-based sharing:


// Community User sees:
// ✅ Their own User record
// ✅ Their Account (customer's account)
// ✅ Contacts related to that Account (via relationship)
// ✅ Cases they created or are related to Account
// ❌ Sensitive opportunity details

// Setup:
Setup → Sharing Settings:
- Account OWD: Controlled by Parent
- Contact OWD: Controlled by Parent (Account)
- Case OWD: Public Read/Write or Controlled by Parent
- Opportunity OWD: Private (not visible to community)

// Apex: Ensure portal user only sees their Account data
public with sharing class ContractController {
    @AuraEnabled(cacheable=true)
    public static List<Contract__c> getMyContracts() {
        // with sharing automatically filters to user's accessible records
        return [
            SELECT Id, Name, Amount__c, Status__c
            FROM Contract__c
            WHERE Account__c IN (SELECT AccountId FROM User WHERE Id = :UserInfo.getUserId())
        ];
    }
}
  

9. OWD Public Read/Write But Still Want Restrictions

Question: What happens when OWD is Public Read/Write but you still want restrictions?

Answer: Use Anti-Sharing Rules or custom logic:


// If OWD = Public Read/Write, everyone sees all records.
// To restrict: Use anti-sharing or custom validation.

// Option 1: Validation Rule (prevents edit, not view)
IF(
    AND(
        $Profile.Name != 'System Administrator',
        Sensitivity__c = 'Confidential'
    ),
    TRUE
)
Error: "Non-admins cannot edit sensitive records"
// Note: Users can still SEE sensitive records, just can't edit

// Option 2: Custom Logic in Apex (deny read/write)
public with sharing class AccountController {
    @AuraEnabled
    public static Account getAccount(Id accountId) {
        Account acc = [SELECT Id, Name, SensitiveData__c FROM Account WHERE Id = :accountId];

        // Deny access if sensitive and not admin
        if (acc.Sensitivity__c = 'Confidential' && !isCurrentUserAdmin()) {
            throw new AuraHandledException('Access Denied');
        }
        return acc;
    }
}

// Option 3: Remove OWD = Public Read/Write, use Sharing Rules instead
// ✅ Better approach: Set OWD to Private/Read Only, grant access via Sharing Rules
  

10. Troubleshooting "User Can't See Record"

Question: How do you troubleshoot "user can't see record" issue step by step?

Answer: Use this checklist:


STEP 1: Check Org-Wide Defaults (OWD)
├─ Go to Setup → Sharing Settings → [Object]
├─ Read: "The account level is set to Private"
├─ Q: Can user see ANY records of this type? 
│   └─ If NO → Likely OWD or Field-Level Security issue

STEP 2: Check Record Sharing
├─ Open the record → sharing icon (bottom right)
├─ Q: Is user listed with 'Read' or 'Edit' access?
│   └─ If NO → Add via sharing rule or manual share
│   └─ If YES → Drill down to field level

STEP 3: Check Profile/Permission Set
├─ Setup → Users → User Details → [User]
├─ Check Profile → Object Permissions
├─ Q: Does profile have READ access to object?
│   └─ If NO → Add to profile or permission set
│   └─ If YES → Check field access next

STEP 4: Check Field-Level Security
├─ Profile → Field Permissions → [Object]
├─ Q: Is the FIELD (or key fields) readable?
│   └─ If NO → Enable field in Profile/PermSet
│   └─ If YES → Check record ownership/hierarchy

STEP 5: Check Role Hierarchy
├─ Is user owner of record? YES → can see
├─ Is user above owner in hierarchy? YES → can see
├─ Is record shared? Check sharing rules/manual share
├─ Is sharing rule matching? YES → can see

STEP 6: Check Apex Sharing
├─ Query ShareWith__c or custom sharing logic
├─ Is record dynamically shared? Check trigger logs

STEP 7: Last Resort – Check System
├─ Clear browser cache (Ctrl+Shift+Del)
├─ Login as different user and test
├─ Check org debug logs for AuraEnabled exceptions
└─ Run: SOSL search to see if record appears
  

11. Sales Reps See Only Their Records, Managers See Team Data

Question: Sales reps should see only their records, managers should see team data. How do you design access?

Answer: Combine OWD + Role Hierarchy:


// Org Structure:
VP Sales
├─ Regional Manager - West
│  ├─ Sales Rep 1
│  └─ Sales Rep 2
└─ Regional Manager - East
   └─ Sales Rep 3

// Configuration:
1. OWD for Opportunity = PRIVATE
2. Sales Rep owns their own Opportunities
3. Role Hierarchy grants manager visibility

// Results:
Sales Rep 1:
- Sees their own Oppty (as owner)
- Cannot see Oppty owned by Sales Rep 2
- Cannot see Regional Manager's Oppty

Regional Manager - West:
- Sees all Oppty owned by Sales Rep 1 & 2 (below in hierarchy)
- Sees their own Oppty (as owner)
- Cannot see Regional Manager - East's Oppty

VP Sales:
- Sees all Oppty (everyone below in hierarchy)

// Adding Apex Sharing for exceptions:
If an Oppty needs review by Finance team:

public class OpportunityShareService {
    public static void shareWithFinanceForReview(List<Opportunity> opportunities) {
        List<OpportunityShare> shares = new List<OpportunityShare>();

        for (Opportunity opp : opportunities) {
            if (opp.FinanceReview__c == true) {
                OpportunityShare share = new OpportunityShare();
                share.OpportunityId = opp.Id;
                share.UserOrGroupId = PublicGroupId.Finance;  // Public Group ID
                share.OpportunityAccessLevel = 'Read';
                shares.add(share);
            }
        }
        insert shares;
    }
}
  

12. Finance Read-Only Access Across Regions

Question: Finance needs read-only access to all Opportunities across regions. What approach will you use?

Answer: Use Sharing Rule with Public Group:


// Step 1: Create Public Group for Finance
Setup → Users → Public Groups → New
Name: Finance Team
Add members: All Finance users, Finance manager

// Step 2: Create Sharing Rule
Setup → Sharing Settings → Opportunity → New
Rule Name: Finance View All Oppty
Type: Opportunity
Share With: Public Group (Finance Team)
Access Level: Read

Criteria:
- All Opportunities (no filters)
OR
- RecordType = "Enterprise"
OR
- StageName IN ('Proposal', 'Negotiation')

// Result:
- Every Finance user can READ all Oppty
- Finance users cannot EDIT Oppty
- Finance users cannot delete Oppty
- Access applies globally across regions

// Apex: Verify access
public with sharing class FinanceReporting {
    @AuraEnabled(cacheable=true)
    public static List<Opportunity> getAllOpportunitiesForReporting() {
        // with sharing respects OWD and sharing rules
        return [
            SELECT Id, Name, Amount, StageName, OwnerId, Account.Name
            FROM Opportunity
            ORDER BY Amount DESC
        ];
    }
}
  

13. Temporary Access to a Record

Question: You need to give temporary access of a record to a user. How would you handle it?

Answer: Use scheduled Apex or workflow to auto-revoke access:


import { LightningElement, api, track } from 'lwc';
import grantTemporaryAccess from '@salesforce/apex/SharingService.grantTemporaryAccess';

export default class TemporaryAccessComponent extends LightningElement {
    @api recordId;
    @track accessDays = 7;
    @track selectedUser;

    async handleGrantAccess() {
        try {
            await grantTemporaryAccess({
                recordId: this.recordId,
                userId: this.selectedUser,
                days: this.accessDays
            });
            alert('Access granted until ' + new Date(Date.now() + this.accessDays * 24 * 60 * 60 * 1000));
        } catch (error) {
            alert('Error: ' + error.body.message);
        }
    }
}

// Apex: Grant and auto-revoke access
public class SharingService {

    @AuraEnabled
    public static void grantTemporaryAccess(Id recordId, Id userId, Integer days) {
        // Insert share record
        OpportunityShare share = new OpportunityShare();
        share.OpportunityId = recordId;
        share.UserOrGroupId = userId;
        share.OpportunityAccessLevel = 'Edit';
        share.RowCause = 'Manual';
        insert share;

        // Create scheduled action to remove access
        scheduleAccessRevoke(recordId, userId, days);
    }

    private static void scheduleAccessRevoke(Id recordId, Id userId, Integer days) {
        RevokeAccessJob job = new RevokeAccessJob(recordId, userId);
        String cronExpression = getCronExpression(days);
        System.schedule('Revoke_' + recordId, cronExpression, job);
    }
}

// Scheduled Job: Revoke access
public class RevokeAccessJob implements Schedulable {
    private Id recordId;
    private Id userId;

    public RevokeAccessJob(Id recordId, Id userId) {
        this.recordId = recordId;
        this.userId = userId;
    }

    public void execute(SchedulableContext ctx) {
        List<OpportunityShare> shares = [
            SELECT Id
            FROM OpportunityShare
            WHERE OpportunityId = :recordId
            AND UserOrGroupId = :userId
            AND RowCause = 'Manual'
        ];

        if (!shares.isEmpty()) {
            delete shares;
        }
    }
}
  

14. Dynamic Sharing Based on Field Value

Question: A record should be shared dynamically based on a field value. What solution will you use?

Answer: Use Apex trigger with field-based logic:


// Scenario: Share Opportunity with Account team when Status = "Reviewing"

trigger OpportunityFieldBasedSharing on Opportunity (after insert, after update) {
    if (Trigger.isAfter) {
        OpportunityFieldSharingService.shareBasedOnStatus(Trigger.new, Trigger.oldMap);
    }
}

public class OpportunityFieldSharingService {

    public static void shareBasedOnStatus(List<Opportunity> newOppty, Map<Id, Opportunity> oldMap) {
        List<OpportunityShare> sharesToAdd = new List<OpportunityShare>();
        List<Id> opptyToUnshare = new List<Id>();

        for (Opportunity opp : newOppty) {
            Opportunity oldOpp = oldMap.get(opp.Id);
            String newStatus = opp.StageName;
            String oldStatus = oldOpp.StageName;

            // If changed to "Reviewing", share with Account team
            if (newStatus == 'Reviewing' && oldStatus != 'Reviewing') {
                // Get account team members
                List<AccountTeamMember> teamMembers = [
                    SELECT UserId FROM AccountTeamMember
                    WHERE AccountId = :opp.AccountId
                ];

                for (AccountTeamMember member : teamMembers) {
                    OpportunityShare share = new OpportunityShare();
                    share.OpportunityId = opp.Id;
                    share.UserOrGroupId = member.UserId;
                    share.OpportunityAccessLevel = 'Read';
                    sharesToAdd.add(share);
                }
            }

            // If changed from "Reviewing" to other status, revoke access
            if (oldStatus == 'Reviewing' && newStatus != 'Reviewing') {
                opptyToUnshare.add(opp.Id);
            }
        }

        // Insert new shares
        if (!sharesToAdd.isEmpty()) {
            insert sharesToAdd;
        }

        // Remove old shares
        if (!opptyToUnshare.isEmpty()) {
            List<OpportunityShare> sharesToDelete = [
                SELECT Id FROM OpportunityShare
                WHERE OpportunityId IN :opptyToUnshare
                AND RowCause = 'Manual'
            ];
            delete sharesToDelete;
        }
    }
}
  

15. Users Can See Records But Cannot Edit Certain Fields

Question: Users can see records but cannot edit certain fields. How do you control this?

Answer: Combine Field-Level Security + Validation Rules + Lightning Components:


// Approach 1: Field-Level Security (simplest)
Setup → Profiles → [Profile] → Field Permissions
- Amount field: Read enabled, Edit disabled
- Result: User sees Amount but input field is read-only

// Approach 2: Validation Rule (more control)
IF(
    AND(
        NOT($Profile.Name = 'System Administrator'),
        CHANGED(Renewal_Date__c),
        Renewal_Date__c > TODAY()
    ),
    TRUE
)
Error: "Non-admins cannot edit future renewal dates"

// Approach 3: Lightning Component (most control)
import { LightningElement, api, track, wire } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import getFieldEditability from '@salesforce/apex/FieldSecurityService.getFieldEditability';

export default class RestrictedFieldForm extends LightningElement {
    @api recordId;
    @track fieldPermissions = {};

    @wire(getFieldEditability, { recordId: '$recordId' })
    wiredPermissions({ data, error }) {
        if (data) {
            this.fieldPermissions = data;
        }
    }

    get canEditAmount() {
        return this.fieldPermissions.Amount?.editable;
    }

    handleSave() {
        // Validate field edits before sending to Apex
        if (!this.canEditAmount) {
            alert('You cannot edit Amount field');
            return;
        }
        // Proceed with save
    }
}

// Apex: Check FLS before allowing edit
public with sharing class FieldSecurityService {

    @AuraEnabled
    public static Map<String, FieldPermission> getFieldEditability(Id recordId) {
        Map<String, FieldPermission> fieldAccess = new Map<String, FieldPermission>();

        Schema.DescribeSobjectResult describe = recordId.getSObjectType().getDescribe();
        Map<String, Schema.SObjectField> fields = describe.fields.getMap();

        for (String fieldName : new List<String>{ 'Amount', 'RenewalDate', 'Description' }) {
            Schema.SObjectField field = fields.get(fieldName.toLowerCase());
            Schema.DescribeFieldResult fieldDesc = field.getDescribe();

            FieldPermission perm = new FieldPermission();
            perm.readable = fieldDesc.isAccessible();
            perm.editable = fieldDesc.isUpdateable();

            fieldAccess.put(fieldName, perm);
        }

        return fieldAccess;
    }

    public class FieldPermission {
        @AuraEnabled public Boolean readable { get; set; }
        @AuraEnabled public Boolean editable { get; set; }
    }
}
  

16. External (Community) Users See Only Their Related Records

Question: External (community) users should only see their own related records. How do you design it?

Answer: Use Account-based sharing + Apex with sharing:


// Community user setup:
1. Portal User linked to Account (customer account)
2. OWD settings for Customer Portal:
   - Account: Controlled by Parent (Account)
   - Contact: Controlled by Parent (Account)
   - Case: Public Read/Write (so they can see all cases)
   - Opportunity: Private

3. Portal User sees:
   ✅ Their Account (the company they work for)
   ✅ Contacts on their Account
   ✅ Cases on their Account
   ❌ Other companies' data

// Apex: Enforce access in code
public with sharing class CommunityDataService {

    @AuraEnabled(cacheable=true)
    public static List<Contact> getAccountContacts() {
        // with sharing automatically filters to user's account
        User portalUser = [SELECT AccountId FROM User WHERE Id = :UserInfo.getUserId()];

        return [
            SELECT Id, Name, Email, Phone
            FROM Contact
            WHERE AccountId = :portalUser.AccountId
        ];
    }

    @AuraEnabled(cacheable=true)
    public static List<Case> getMyAccountCases() {
        User portalUser = [SELECT AccountId FROM User WHERE Id = :UserInfo.getUserId()];

        return [
            SELECT Id, CaseNumber, Subject, Status
            FROM Case
            WHERE AccountId = :portalUser.AccountId
        ];
    }
}

// LWC: Display community data
import { LightningElement, track, wire } from 'lwc';
import getAccountContacts from '@salesforce/apex/CommunityDataService.getAccountContacts';

export default class CommunityView extends LightningElement {
    @track contacts = [];

    @wire(getAccountContacts)
    wiredContacts({ data, error }) {
        if (data) {
            this.contacts = data;
        }
    }
}
  

17. Troubleshooting: User Cannot See Record Step by Step

Question: A user cannot see a record they should have access to. How do you troubleshoot step by step?

Answer: Use this systematic troubleshooting approach:


// Step 1: Verify user can see records at all (OWD baseline)
SELECT Id, Name FROM Opportunity LIMIT 10
// If query returns records → OWD allows some visibility
// If query returns 0 records → Check OWD (likely Private) or FLS

// Step 2: Check if user owns the record
SELECT OwnerId FROM Opportunity WHERE Id = :recordId
IF (OwnerId = UserInfo.getUserId()) {
    CONCLUSION: User is owner, should have full access
    CHECK: Profile edit permissions
}

// Step 3: Check role hierarchy
User u = [SELECT UserRoleId FROM User WHERE Id = :UserInfo.getUserId()];
UserRole r = [SELECT Id, ParentRoleId FROM UserRole WHERE Id = :u.UserRoleId];

IF (record.OwnerId's role is below current user's role hierarchy) {
    CONCLUSION: User should see record via role hierarchy
    CHECK: OWD is not blocking it (set to Private at minimum)
}

// Step 4: Check if record is shared explicitly
List<OpportunityShare> shares = [
    SELECT Id, AccessLevel
    FROM OpportunityShare
    WHERE OpportunityId = :recordId
    AND (UserOrGroupId = :userId OR UserOrGroupId IN (public groups user belongs to))
];

IF (shares.size() > 0) {
    CONCLUSION: Record is explicitly shared
    IF (AccessLevel = 'Read') → User can READ but not EDIT
}

// Step 5: Check Field-Level Security
Schema.DescribeFieldResult fieldDesc = Opportunity.Amount.getDescribe();
IF (!fieldDesc.isAccessible()) {
    PROBLEM: User cannot read Amount field
    FIX: Enable field in Profile/PermSet
} ELSE IF (!fieldDesc.isUpdateable()) {
    PROBLEM: User can read but cannot edit Amount
    FIX: User has Read access to record but not edit to this field
}

// Step 6: Check for API filters
// Some record types may be hidden by validation rules or SOSL
SELECT COUNT() FROM Opportunity WHERE Id = :recordId
IF (count = 0) {
    PROBLEM: Record doesn't match user's query filters
    CHECK: Are you using with sharing clause?
    FIX: Ensure Apex uses with sharing for correct filtering
}

// Step 7: Verify record actually exists and isn't deleted
SELECT Id FROM Opportunity WHERE Id = :recordId ALL ROWS
IF (IsDeleted = true) {
    CONCLUSION: Record is in recycle bin
    FIX: Restore from recycle bin if needed
}

// Debug Query to get complete picture:
List<Opportunity> opps = [
    SELECT
        Id, Name, OwnerId, Owner.Name, Amount,
        (SELECT Id, UserOrGroupId, OpportunityAccessLevel FROM OpportunityShares)
    FROM Opportunity
    WHERE Id = :recordId
];
  

18. Restrict Access When OWD is Public Read/Write

Question: You need to restrict access even when OWD is Public Read/Write. What can you do?

Answer: OWD = Public Read/Write gives global access; use alternatives:


// PROBLEM: If OWD = Public Read/Write, EVERYONE can see ALL records
// SOLUTION OPTIONS:

// Option 1: Change OWD to Private or Read Only ✅ BEST
Setup → Sharing Settings → Opportunity
Change: Public Read/Write → Private
Then grant access via Sharing Rules/Apex Sharing

// Option 2: Use Validation Rules (prevents EDIT, not VIEW)
IF(
    AND(
        $Profile.Name != 'System Administrator',
        Confidentiality__c = 'Sensitive'
    ),
    TRUE
)
Error: "Non-admins cannot edit sensitive records"
// Note: Users can still SEE sensitive records, just can't edit

// Option 3: Use Apex with custom restrictions
public with sharing class OpportunitySecurity {
    @AuraEnabled(cacheable=true)
    public static List<Opportunity> getAccessibleOpportunities() {
        // Apex with sharing respects OWD and Sharing Rules
        List<Opportunity> opps = [
            SELECT Id, Name, Amount
            FROM Opportunity
        ];

        // Add custom filter to deny based on field
        List<Opportunity> filtered = new List<Opportunity>();
        for (Opportunity opp : opps) {
            if (opp.Confidentiality__c != 'TopSecret' || isCurrentUserAdmin()) {
                filtered.add(opp);
            }
        }
        return filtered;
    }
}

// Option 4: Use record-level access control in LWC
// Even if record is visible via SOQL, hide in LWC based on field
import { LightningElement, track } from 'lwc';

export default class OpportunitiesList extends LightningElement {
    @track visibleOpportunities = [];

    handleDataLoaded(event) {
        const allOpps = event.detail;

        // Filter in UI if Confidentiality = TopSecret
        this.visibleOpportunities = allOpps.filter(
            opp => opp.Confidentiality__c !== 'TopSecret'
        );
    }
}

// RECOMMENDATION:
// Change OWD from Public Read/Write to Private
// Grant access explicitly via Sharing Rules or Apex
// This is the proper security model
  

19. Multiple Teams Need Access – Roles or Public Groups?

Question: Multiple teams need access to the same set of records. Do you use Roles or Public Groups? Why?

Answer: Depends on team structure:

Scenario Use Roles Use Public Groups
Sales team hierarchy (VP → Manager → Rep) ✅ YES – hierarchy matters ❌ No – not hierarchical
Cross-functional teams (Sales + Finance + Legal) ❌ No – not structural ✅ YES – skill-based, not org hierarchy
Finance needs all Oppty (across all regions) ❌ No – they're not in sales org ✅ YES – create "Finance Approvers" group
Multiple account teams for same account ❌ No – not structural ✅ YES – "West Region Team", "East Region Team"

Real Example:


Scenario: Three teams need access to "Enterprise Opportunities"
- Sales team (hierarchical)
- Finance review team (cross-functional)
- Legal review team (ad-hoc)

Solution:
1. Create Sharing Rule for "Sales" based on Roles
   Share with role "Sales" + subordinates

2. Create Public Group "Finance Approvers"
   Add: Finance VP, Finance Manager, Auditor role

3. Create Public Group "Legal Team"
   Add: Legal counsel, in-house attorney, paralegal

4. Create two Sharing Rules:
   Rule 1: IF Requires_Finance_Review__c = true
           Share with Public Group "Finance Approvers"

   Rule 2: IF Requires_Legal_Review__c = true
           Share with Public Group "Legal Team"

Result:
- Sales sees records in their role
- Finance sees only records needing finance review
- Legal sees only records needing legal review
  

20. Complex Sharing Logic – Sharing Rules or Apex?

Question: You need to share records from a custom object based on complex logic. Would you use Sharing Rules or Apex Sharing? Why?

Answer: Choose based on complexity:

Scenario Sharing Rules Apex Sharing
Share if field = "X" ✅ Simple and fast ❌ Overkill
Share if field = "X" AND role = "Y" ✅ Good (use role-based rule) ❌ Sharing rules handle this
Share based on lookup field value (Account region) ❌ Can't do cross-object logic ✅ YES – query parent record, evaluate
Share if related Contact matches criteria ❌ No cross-object logic ✅ YES – query related records
Complex: Share to users on same team as record owner ❌ Can't query team membership ✅ YES – use Apex for complex joins

Example: Complex Sharing Rule


Requirement: Share Project record with all users in same Department as owner

// Sharing Rules can't do this (no cross-object logic)
// Solution: Use Apex

trigger ProjectSharingTrigger on Project__c (after insert, after update) {
    ProjectSharingService.shareWithDepartment(Trigger.new);
}

public class ProjectSharingService {

    public static void shareWithDepartment(List<Project__c> projects) {
        // Get owner departments
        Set<Id> ownerIds = new Set<Id>();
        for (Project__c proj : projects) {
            ownerIds.add(proj.OwnerId);
        }

        // Query owners and their department
        Map<Id, String> ownerDepartments = new Map<Id, String>();
        for (User u : [SELECT Id, Department FROM User WHERE Id IN :ownerIds]) {
            ownerDepartments.put(u.Id, u.Department);
        }

        // Find all users in same departments
        Set<String> departments = new Set<String>(ownerDepartments.values());
        List<User> departmentUsers = [
            SELECT Id FROM User
            WHERE Department IN :departments
        ];

        // Create shares
        List<Project__Share> shares = new List<Project__Share>();
        for (Project__c proj : projects) {
            String ownerDept = ownerDepartments.get(proj.OwnerId);

            for (User u : departmentUsers) {
                if (u.Department == ownerDept) {
                    Project__Share share = new Project__Share();
                    share.ParentId = proj.Id;
                    share.UserOrGroupId = u.Id;
                    share.AccessLevel = 'Edit';
                    share.RowCause = 'Manual';
                    shares.add(share);
                }
            }
        }

        insert shares;
    }
}

// DECISION RULE:
// If: Simple field-based rule → Use Sharing Rules ✅
// If: Complex, cross-object, queryable logic → Use Apex ✅
// If: Mix of both → Sharing Rules + Apex (don't duplicate logic)
  

Interview Tips