Deloitte Salesforce Interview Questions 2026 — 3 Years and 6 Years Experience | SF Interview Pro
📅 Deloitte
🏢 Big 4 — Deloitte India
Deloitte Salesforce Interview Questions 2026
Real questions shared by candidates who interviewed at Deloitte India — separated by 3 years and 6 years experience.
60Questions
2 Tracks3 Yr and 6 Yr
RealFrom Candidates
✅ These questions are sourced from real candidates who interviewed at Deloitte India — shared via LinkedIn, community forums, and direct submissions to sfinterviewpro.com.
About Deloitte Salesforce Practice — India
What you need to know before your Deloitte interview
Interview Rounds
3 to 4 rounds
Round Types
Technical + Techno Managerial + HR
Tech Focus
LWC, Apex, Integration, Security
Key Skill
Client Communication + Technical
Hiring Cities
Hyderabad, Mumbai, Bangalore, Pune
Process Time
2 to 4 weeks
What Deloitte Specifically Tests
Based on real candidate feedback — Deloitte focuses heavily on: LWC live coding exercises, security model depth (OWD + Sharing + Profiles), real project scenarios you have worked on, and your ability to explain technical concepts to non-technical stakeholders. They also ask about batch class error handling and governor limits from real implementation experience.
3
Years Experience
Associate Consultant / Developer
6
Years Experience
Consultant / Senior Consultant
💰 Deloitte Salesforce Salary Guide India 2026
0-2 Years
6-10 LPA
Analyst
3-4 Years
12-18 LPA
Associate Consultant
5-7 Years
18-28 LPA
Consultant
8+ Years
28-45 LPA
Senior Consultant
👨💻
3 Years Experience — Technical + Techno Managerial Rounds
Real questions from Deloitte candidates with 3 years experience
Technical Interview Round
Q13 YrsSecurity
Brief on Security Models in Salesforce. Two users have the same Profile and Role — one can see 10 records, the other can see 100 records. Why?
Profiles and Roles are not the only access control mechanisms. The difference is due to Manual Shares, Sharing Rules, Teams (Account/Opportunity), or direct record ownership differences. Profiles control object-level access. Sharing controls record-level access.
| Layer | Controls |
|---|---|
| Profile / Permission Set | Object CRUD, Field FLS, App access |
| OWD (Org Wide Default) | Baseline record visibility |
| Role Hierarchy | Managers see subordinate records |
| Sharing Rules | Open access to groups of users |
| Manual Share | Record owner shares with specific user |
| Teams | Account/Opportunity Team members get access |
One-liner: "Same Profile + Role does not mean same record access. Sharing Rules, Manual Shares, Account/Opportunity Teams, and record ownership all independently grant record-level access on top of the Role Hierarchy."
Q23 YrsApex
In Apex method one SOQL query has 4 fields. 2 users are using the application — one user has access to all 4 fields, another has access to only 2 fields. What will happen when the second user runs the query?
It depends on the sharing mode of the Apex class. In WITH USER_MODE (Summer 26 default at API v67), a FieldAccessException is thrown if the user doesn't have FLS on queried fields. In WITHOUT SHARING or system mode, all 4 fields are returned regardless of FLS.
// WITH USER_MODE — throws FieldAccessException for restricted fields
List<Account> accs = [SELECT Id, Name, Salary__c, Revenue__c
FROM Account WITH USER_MODE];
// User 2 without FLS on Salary__c → FieldAccessException!
// System mode — returns all 4 fields for ALL users (bypasses FLS)
public without sharing class QueryClass {
public static List<Account> getAccounts() {
return [SELECT Id, Name, Salary__c, Revenue__c FROM Account];
}
}
// Safe approach — check FLS before querying
if(Schema.SObjectType.Account.fields.Salary__c.isAccessible()) {
// include Salary__c in query
}
One-liner: "In WITH USER_MODE — FieldAccessException for fields user can't access. In system mode — all fields returned regardless. Summer 26 API v67 defaults to USER_MODE so this behavior is now the standard."
Q33 YrsApex
How do you share a record with another user in Salesforce? How many ways can you share?
There are 6 ways to share records in Salesforce — Role Hierarchy, OWD, Sharing Rules, Manual Sharing, Apex Managed Sharing, and Teams. Each serves a different purpose and use case.
| # | Method | Who Controls | When to Use |
|---|---|---|---|
| 1 | Role Hierarchy | Automatic | Managers see subordinate records |
| 2 | OWD | Admin | Baseline access for all users |
| 3 | Sharing Rules | Admin | Group-based automated sharing |
| 4 | Manual Share | Record Owner | Ad-hoc sharing with specific user |
| 5 | Apex Managed Sharing | Developer | Complex programmatic sharing logic |
| 6 | Teams (Account/Opp) | Record Owner | Collaborative access on specific records |
One-liner: "6 ways to share: Role Hierarchy (automatic), OWD (baseline), Sharing Rules (group-based), Manual Share (ad-hoc), Apex Managed Sharing (programmatic complex logic), Teams (collaborative). OWD can only open access further — never restrict beyond OWD."
Q43 YrsApex
Batch class processed 4000 records. At 3000 records it failed, 1000 records left. How do you resolve this?
Check Database.executeBatch() with Database.Stateful to track which records failed. Use Database.update() with allOrNone=false so failed records don't roll back successful ones. Use the finish() method to re-process failed records.
// Use allOrNone=false — partial success instead of full rollback
public void execute(Database.BatchableContext bc, List<Account> scope) {
List<Database.SaveResult> results = Database.update(scope, false);
for(Database.SaveResult sr : results) {
if(!sr.isSuccess()) {
// Log failed records — don't throw exception
failedIds.add(sr.getId()); // failedIds in Database.Stateful
for(Database.Error err : sr.getErrors()) {
System.debug('Error: ' + err.getMessage());
}
}
}
}
// finish() — re-process or notify about failures
public void finish(Database.BatchableContext bc) {
if(!failedIds.isEmpty()) {
// Launch another batch for failed records only
Database.executeBatch(new RetryBatch(failedIds), 50);
}
}
Database.Stateful is Key Here
Without Database.Stateful, each execute() has fresh state — failedIds would be empty each time. Implement Database.Stateful on your Batch class to maintain the failed record list across all execute() calls.One-liner: "Use Database.update(scope, false) for partial success — failed records don't rollback successful ones. Implement Database.Stateful to track failures across chunks. Launch a retry batch in finish() for the failed records."
Q53 YrsLWC
How do you reuse LWC functions in different LWC components?
Create a shared utility JavaScript module (service component) — a JS file without HTML/CSS. Import and use it in any LWC component. This is the recommended pattern for shared logic in LWC.
// Shared utility module — utils/utils.js (no HTML file needed)
const formatCurrency = (amount, currency = 'INR') => {
return new Intl.NumberFormat('en-IN', {
style: 'currency', currency
}).format(amount);
};
const validateEmail = (email) => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
};
export { formatCurrency, validateEmail };
// In any LWC component — import and use
// import { formatCurrency, validateEmail } from 'c/utils';
// this.formattedAmount = formatCurrency(this.amount);
One-liner: "Create a service LWC with only a JS file (no HTML/CSS), export shared functions, and import them in any component. This is the standard LWC pattern for reusable utility logic."
Q63 YrsLWC
What are the possible reasons for an LWC component not displaying on the page?
Most common reasons: missing targets in meta XML, insufficient Profile/Permission Set access, JavaScript errors in browser console, Apex method not @AuraEnabled, missing @wire or imperative call, component not deployed, or Lightning page not saved after adding component.
Deloitte Debugging Checklist
1. Check browser console for JS errors first2. Verify meta XML has correct targets (lightning__AppPage, lightning__RecordPage etc.)
3. Check Profile has access to the LWC component (Lightning Component settings)
4. Check Apex method is @AuraEnabled and class is public
5. Check if component is in the right App/Page in Lightning App Builder
6. Verify the component is deployed (not just saved locally)
7. Check if @wire adapter has correct parameters — undefined params prevent data load
One-liner: "LWC not showing: check browser console first, then meta XML targets, then Profile Lightning Component access, then @AuraEnabled on Apex, then deployment status. 80% of cases are one of these five."
Q73 YrsLWC
Write LWC code — UI has 4 fields and one Update button. On click of the button the current record should update.
Use @wire with getRecord to load current values, track properties for two-way binding, call an @AuraEnabled Apex method on button click to perform the update.
// HTML template
// <lightning-input label="Name" value={name} onchange={handleName}></lightning-input>
// <lightning-input label="Phone" value={phone} onchange={handlePhone}></lightning-input>
// <lightning-button label="Update" onclick={handleUpdate}></lightning-button>
// JavaScript
import { LightningElement, api, wire, track } from 'lwc';
import { getRecord } from '@salesforce/uiRecordApi';
import updateRecord from '@salesforce/apex/AccountController.updateRecord';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class UpdateForm extends LightningElement {
@api recordId;
@track name; @track phone;
handleName(e) { this.name = e.target.value; }
handlePhone(e) { this.phone = e.target.value; }
handleUpdate() {
updateRecord({ recordId: this.recordId, name: this.name, phone: this.phone })
.then(() => {
this.dispatchEvent(new ShowToastEvent({
title: 'Success', message: 'Updated!', variant: 'success'
}));
})
.catch(error => console.error(error));
}
}
One-liner: "LWC update pattern: @api recordId, @track properties for form fields, onchange handlers to capture input, imperative Apex call on button click, ShowToastEvent for success/error feedback."
Q83 YrsLWC
What is the difference between lightning-record-form, lightning-record-edit-form, and lightning-record-view-form?
All three display Salesforce record data but offer different levels of customization and control.
| Component | Use Case | Customization |
|---|---|---|
| lightning-record-form | Simple view/edit form, auto layout | Low — Salesforce controls layout |
| lightning-record-edit-form | Custom edit form with specific fields | High — you control field placement |
| lightning-record-view-form | Read-only display of record fields | High — custom layout, no edit |
When to Use Each at Deloitte Projects
lightning-record-form = quick admin-like edit form with minimal codelightning-record-edit-form = custom create/edit page with specific field arrangement
lightning-record-view-form = display record data in custom layout without edit capability
One-liner: "record-form = auto layout simple form. record-edit-form = custom layout editable form. record-view-form = custom layout read-only. Use record-edit-form for most custom requirements as it gives full control."
Q93 YrsLWC
How do you get the recordId in an LWC component?
Use @api recordId — Salesforce automatically passes the current record's Id to any LWC component placed on a Lightning Record Page. For components in App pages or other contexts, pass it explicitly as a property.
// Automatic from Lightning Record Page
import { LightningElement, api } from 'lwc';
export default class MyComponent extends LightningElement {
@api recordId; // Salesforce passes this automatically on record pages
@api objectApiName; // Also available automatically
}
// Or from CurrentPageReference
import { CurrentPageReference } from 'lightning/navigation';
import { wire } from 'lwc';
@wire(CurrentPageReference)
pageRef;
// this.pageRef.state.recordId — for community or custom pages
One-liner: "@api recordId is automatically populated by Salesforce on Lightning Record Pages. For other contexts use CurrentPageReference. Always declare it as @api so the platform can pass it in."
Q103 YrsApex
How do you implement a future method in a trigger? What trigger events would you use?
@future methods cannot be called directly from before triggers if they need the record Id (not yet committed). Call from after insert/after update — the record Id is available. Pass primitive params only — no sObjects.
trigger AccountTrigger on Account (after insert, after update) {
Set<Id> ids = new Set<Id>();
for(Account a : Trigger.new) ids.add(a.Id);
// Call @future — pass Set of Ids (primitive collection)
AccountHelper.processAsync(ids);
}
public class AccountHelper {
@future
public static void processAsync(Set<Id> accountIds) {
// Runs in separate transaction — fresh governor limits
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :accountIds];
// Process accounts...
}
}
One-liner: "Call @future from after insert/after update triggers — not before insert (Id not available). Pass Set of Ids as parameter — never sObjects. @future gives fresh governor limits in a new transaction."
Q113 YrsLWC
How do you call an Apex class in Lightning Flow and pass a value to it?
Use @InvocableMethod annotation on the Apex method. Flow calls it as an Action element — pass input variables from Flow to Apex and get output variables back.
// Apex class with @InvocableMethod
public class FlowAccountUpdater {
@InvocableMethod(label='Update Account Industry' description='Updates industry field')
public static List<String> updateIndustry(List<FlowInput> inputs) {
List<String> results = new List<String>();
for(FlowInput input : inputs) {
// Process input.accountId and input.industry
results.add('Updated: ' + input.accountId);
}
return results;
}
public class FlowInput {
@InvocableVariable(required=true)
public Id accountId;
@InvocableVariable
public String industry;
}
}
One-liner: "@InvocableMethod makes Apex callable from Flow as an Action element. Use @InvocableVariable on inner class properties for input/output — Flow maps its variables to these properties."
Q123 YrsLWC
How do you write a Test.startTest() and Test.stopTest() for a @future method? Why are they important?
Test.startTest() and Test.stopTest() create an isolated governor limit context within the test. For @future methods, everything between them runs synchronously when stopTest() is called — ensuring async code completes before assertions.
@isTest
static void testFutureMethod() {
// Setup test data BEFORE startTest
Account a = new Account(Name = 'Test');
insert a;
Test.startTest(); // Fresh governor limits from here
// Call the code that triggers @future
AccountHelper.processAsync(new Set<Id>{a.Id});
Test.stopTest(); // @future executes HERE before moving to assertions
// Assert AFTER stopTest — @future has completed
Account updated = [SELECT Id, Description FROM Account WHERE Id = :a.Id];
System.assertNotEquals(null, updated.Description);
}
One-liner: "Test.startTest() resets governor limits. Test.stopTest() forces @future, Queueable, and Batch Apex to execute synchronously so assertions can verify their results. Always assert AFTER stopTest()."
Q133 YrsApex
You have Account object and a child object with field x. For each Account there can be multiple child records of x. Write a trigger to roll up the sum of x to the Account.
Trigger on the child object (after insert, after update, after delete, after undelete). Collect parent AccountIds, aggregate child amounts with SOQL GROUP BY, update parent Account with the sum.
trigger ChildTrigger on Child__c (after insert, after update, after delete, after undelete) {
Set<Id> accountIds = new Set<Id>();
List<Child__c> records = Trigger.isDelete ? Trigger.old : Trigger.new;
for(Child__c c : records) {
if(c.Account__c != null) accountIds.add(c.Account__c);
}
// Aggregate sum of Amount__c per Account
List<AggregateResult> totals = [
SELECT Account__c, SUM(Amount__c) total
FROM Child__c
WHERE Account__c IN :accountIds
GROUP BY Account__c
];
Map<Id, Decimal> sumMap = new Map<Id, Decimal>();
for(AggregateResult ar : totals) {
sumMap.put((Id) ar.get('Account__c'), (Decimal) ar.get('total'));
}
List<Account> toUpdate = new List<Account>();
for(Id accId : accountIds) {
toUpdate.add(new Account(
Id = accId,
Total_Amount__c = sumMap.containsKey(accId) ? sumMap.get(accId) : 0
));
}
if(!toUpdate.isEmpty()) update toUpdate;
}
One-liner: "Roll-up trigger on child: collect parent Ids → SOQL GROUP BY with SUM aggregate → Map parent Id to sum → update parent records. Handle delete trigger using Trigger.old, and cover all 4 events: insert, update, delete, undelete."
Q143 YrsLWC
How do you call an LWC component inside a Flow? How do you pass value to and from the LWC?
Add lightning__FlowScreen as a target in the meta XML. Use @api properties with @api get/set for input, and dispatch FlowAttributeChangeEvent for output values back to the Flow.
// meta XML — add FlowScreen target
// <targets><target>lightning__FlowScreen</target></targets>
// <targetConfigs>
// <targetConfig targets="lightning__FlowScreen">
// <property name="inputValue" type="String" />
// <property name="outputValue" type="String" role="outputOnly" />
// </targetConfig>
// </targetConfigs>
// JavaScript — receive input, send output back to Flow
import { FlowAttributeChangeEvent } from 'lightning/flowSupport';
@api inputValue; // Flow passes value in
@api outputValue; // Flow reads value out
handleChange(event) {
this.outputValue = event.target.value;
// Notify Flow of the changed value
this.dispatchEvent(new FlowAttributeChangeEvent('outputValue', this.outputValue));
}
One-liner: "LWC in Flow: add lightning__FlowScreen target in meta XML, define input/output properties in targetConfig, use @api for input, and FlowAttributeChangeEvent to pass output values back to the Flow."
Q153 YrsLWC
You have a parent component and a child component. How do you call a method in the child component from the parent?
Use template.querySelector() in the parent to get a reference to the child component, then call its @api method directly. The child method must be decorated with @api to be accessible from outside.
// Child component — expose method with @api
import { LightningElement, api } from 'lwc';
export default class ChildComponent extends LightningElement {
@api doSomething(value) {
console.log('Child received: ' + value);
// Do work
}
}
// Parent component — call child method
// <c-child-component></c-child-component>
// <lightning-button label="Call Child" onclick={callChild}></lightning-button>
import { LightningElement } from 'lwc';
export default class ParentComponent extends LightningElement {
callChild() {
const child = this.template.querySelector('c-child-component');
child.doSomething('Hello from parent');
}
}
One-liner: "Parent calls child method via template.querySelector('c-child-component').methodName(). The child method must be @api — without @api it's private and inaccessible from outside the component."
Techno Managerial Round — 3 Years
Q163 YrsScenario
Client scenario: Build a UI with a Service Center search field. User enters address — show only matching Service Centers. On selecting a new one, load it on Google Maps on the same page. No page reload.
Build an LWC with: a lightning-input for address search, @wire to Apex for filtered Service Centers, Google Maps API embedded in the component using a static resource or lightning-map, and event handlers to update the map when a center is selected.
Architecture Points Deloitte Wants to Hear
1. LWC with @track for reactive updates — no page reload needed2. Apex @AuraEnabled method with SOQL to filter service centers by address
3. Google Maps embedded via lightning-map component OR Google Maps JavaScript API loaded as Static Resource
4. Data storage: Custom Metadata (admin-configurable) vs Custom Object (dynamic) — explain trade-off
5. SLDS lightning-map for simple use case, Google Maps API for pins, clustering, custom markers
One-liner: "LWC reactive search with @wire to Apex for filtered results, lightning-map or Google Maps API for visualization, @track for reactive map updates without page reload. Store Service Centers in Custom Object for flexibility."
Q173 YrsScenario
Client wants to create a Case and set Status to In-Progress and Priority to Medium automatically. Assign it to a Queue. How would you do this?
Use a Record-Triggered Flow on Case (before save) to set Status and Priority field values. Use Assignment Rules to automatically assign the Case to the Queue — no code needed.
Two Approaches — Know Both
Declarative (preferred): Record-Triggered Flow (before save) → set Status = In-Progress, Priority = Medium. Assignment Rule → route to Queue based on criteria.Code approach: Before insert trigger → set field values → use Group object query to get Queue Id → assign OwnerId to Queue Id.
Always suggest declarative first at Deloitte — only go to code if Flow cannot handle the complexity.
One-liner: "Record-Triggered Flow (before save) for field defaults + Case Assignment Rule for Queue routing — fully declarative. Use Apex trigger only if assignment logic is too complex for Assignment Rules."
Q183 YrsScenario
Develop a functionality where there are 200 FAQs. User searches from a search input and only relevant FAQs show — without page reload.
LWC with client-side filtering using JavaScript Array.filter() — load all 200 FAQs once from Apex, then filter the array reactively as user types. No server call on each keystroke. Use @track for reactive rendering.
// Load all FAQs once
@wire(getFAQs) wiredFAQs({ data, error }) {
if(data) {
this.allFAQs = data;
this.filteredFAQs = data; // show all initially
}
}
// Filter client-side as user types — no Apex call per keystroke
handleSearch(event) {
const searchTerm = event.target.value.toLowerCase();
this.filteredFAQs = this.allFAQs.filter(faq =>
faq.Question__c.toLowerCase().includes(searchTerm) ||
faq.Answer__c.toLowerCase().includes(searchTerm)
);
}
Why Client-Side Filtering for 200 Records
200 records is small enough to load once and filter in memory. Server-side SOQL on each keystroke would hit governor limits and create poor UX with network latency. For 10,000+ FAQs, switch to server-side SOQL with debounce.One-liner: "200 FAQs — load once from Apex, filter client-side with Array.filter() on each input change. No page reload, no server call per keystroke. For large datasets (10K+) use server-side SOQL with debounce pattern."
Q193 YrsApex
What is Mixed DML error? How do you handle it?
Mixed DML error occurs when you perform DML on both Setup objects (User, Profile, PermissionSet) and non-Setup objects (Account, Contact) in the same transaction. Fix by separating Setup DML into a @future method.
// CAUSES Mixed DML Error
Account a = new Account(Name = 'Test');
insert a; // Non-setup DML
PermissionSetAssignment psa = new PermissionSetAssignment(
AssigneeId = userId, PermissionSetId = psId);
insert psa; // Setup DML → MIXED DML ERROR!
// FIX — separate setup DML into @future
@future
public static void assignPermSet(Id userId, Id psId) {
PermissionSetAssignment psa = new PermissionSetAssignment(
AssigneeId = userId, PermissionSetId = psId);
insert psa; // New transaction — no conflict
}
// In test class — use System.runAs() to avoid Mixed DML
System.runAs(new User(Id = UserInfo.getUserId())) {
insert psa; // runAs creates separate context
}
One-liner: "Mixed DML = Setup objects (User/Profile/PermSet) and non-Setup objects (Account/Contact) in same transaction. Fix with @future for setup DML. In test classes use System.runAs() to avoid it."
Q203 YrsApex
What is Recursion in Triggers? How do you prevent it?
Recursion happens when a trigger fires, performs DML, which fires the trigger again — creating an infinite loop that hits governor limits. Prevent using a static Boolean variable that is set to true on first run and checked before running.
// Recursion prevention with static Boolean
public class TriggerHelper {
public static Boolean hasRun = false;
}
trigger AccountTrigger on Account (after update) {
if(!TriggerHelper.hasRun) {
TriggerHelper.hasRun = true; // Set before DML
List<Account> toUpdate = new List<Account>();
for(Account a : Trigger.new) {
toUpdate.add(new Account(Id=a.Id, Description='Updated'));
}
update toUpdate; // This fires trigger again — but hasRun is true, so it exits
}
}
One-liner: "Recursion = trigger fires trigger. Prevent with static Boolean: set true before DML, check at trigger start. Static variables retain state within a transaction — reset when new transaction starts."
🎯 Practice Deloitte-Level Questions
2,244 MCQ questions across 22 premium quizzes — same topics Deloitte tests.
Apex, LWC, Admin, Flow, Security, Integration. Timed. Scored. Free to try.
Apex, LWC, Admin, Flow, Security, Integration. Timed. Scored. Free to try.
🏆
6 Years Experience — Consultant / Senior Consultant
Architecture, design, integration and leadership questions
Technical + Architecture Round
Q216 YrsArchitecture
A trigger is causing CPU timeout during large data loads. What do you check first?
CPU timeout = too much computation in a single transaction. Check for: SOQL/DML inside loops, string operations in loops, calling Apex methods that themselves have loops, and related triggers or Flows also firing in the same transaction.
Diagnosis Steps — Senior Consultant Approach
1. Enable Apex debug logs with CPU timing — identify which line consumes most CPU2. Check for SOQL or DML inside loops — most common cause
3. Check if Flow is also triggering on same DML — Flow CPU adds to trigger CPU
4. Check string manipulation in loops — string concatenation is CPU-heavy
5. Move heavy logic to Queueable or Batch Apex — fresh CPU limits per transaction
6. Use SOQL for loop instead of List to reduce heap which indirectly reduces CPU
One-liner: "CPU timeout: enable debug logs with timing, look for loops with SOQL/DML/string ops, check if Flow adds CPU in same transaction. Move heavy processing to Queueable/Batch for fresh CPU limits."
Q226 YrsArchitecture
Describe how you design a multi-org architecture for a global client rollout across India, US and Europe.
Evaluate Single Org vs Multi-Org based on: data residency requirements (GDPR Europe = data must stay in EU), business process commonality, reporting needs, and maintenance overhead. Prefer Single Org when possible.
| Factor | Single Org | Multi-Org |
|---|---|---|
| Unified Reporting | Yes — one report for all | No — manual consolidation |
| GDPR Data Residency | Challenging | EU org stays in EU region |
| Deployment | One pipeline | Multiple pipelines, more risk |
| License Cost | Lower | Higher — multiply per org |
| Process Differences | Handle via Record Types, BUs | Separate customization per org |
One-liner: "Default to Single Org — unified reporting, simpler governance, lower cost. Move to Multi-Org only when GDPR data residency or completely incompatible processes make single org impossible. Salesforce now offers EU data residency add-on for Single Org too."
Q236 YrsIntegration
What is the difference between REST and SOAP API in Salesforce? When would you use each?
REST uses JSON over HTTP — lightweight, modern, preferred. SOAP uses XML envelopes — heavier but more formal, used for enterprise/legacy systems. Salesforce supports both but REST is recommended for all new integrations.
| Feature | REST API | SOAP API |
|---|---|---|
| Format | JSON (lighter) | XML (heavier) |
| Protocol | HTTP verbs (GET/POST/PUT/DELETE) | WSDL-based |
| Performance | Faster — smaller payload | Slower — XML parsing overhead |
| Use case | Modern apps, mobile, LWC | Legacy enterprise systems, SAP |
| Auth | OAuth 2.0 | Username/password or OAuth |
| Salesforce Preference | Recommended for new integrations | Legacy only |
One-liner: "REST = JSON over HTTP, lightweight, preferred for all new Salesforce integrations. SOAP = XML, heavier, use only for legacy enterprise systems that require WSDL-based contracts. Never start a new integration with SOAP."
Q246 YrsScenario
An email arrives in Salesforce. A Case is created. The email field has the sender's email. Check if there is a Contact with that email — if yes link it to the Case, if no create a new Contact and link it.
Use an after insert trigger on Case or a Record-Triggered Flow. Query Contact by email — if found, update Case.ContactId. If not found, create new Contact and set Case.ContactId.
trigger CaseTrigger on Case (after insert) {
List<Case> toUpdate = new List<Case>();
for(Case c : Trigger.new) {
if(c.SuppliedEmail != null && c.ContactId == null) {
List<Contact> existing = [SELECT Id FROM Contact
WHERE Email = :c.SuppliedEmail LIMIT 1];
if(!existing.isEmpty()) {
toUpdate.add(new Case(Id = c.Id, ContactId = existing[0].Id));
} else {
// Create new Contact
Contact newCon = new Contact(
LastName = c.SuppliedName != null ? c.SuppliedName : 'Unknown',
Email = c.SuppliedEmail
);
insert newCon;
toUpdate.add(new Case(Id = c.Id, ContactId = newCon.Id));
}
}
}
if(!toUpdate.isEmpty()) update toUpdate;
}
Note for Senior Devs
Avoid SOQL inside a for loop here — bulk-safe version would collect all emails, query all Contacts in one SOQL using WHERE Email IN :emailSet, then match in Apex. The above is simplified for concept illustration.One-liner: "After insert trigger on Case — query Contact by SuppliedEmail, link if found, create and link if not. In production use bulk-safe pattern: collect all emails → one SOQL → Map by email → loop to match and update."
Q256 YrsArchitecture
What are the different types of Relationships in Salesforce? What is the difference between Lookup and Master-Detail?
Salesforce has 5 relationship types: Lookup, Master-Detail, Many-to-Many (Junction), Hierarchical (User only), and External Lookup (for External Objects). Lookup is loose coupling, Master-Detail is tight coupling with cascading delete.
| Feature | Lookup | Master-Detail |
|---|---|---|
| Required | Optional (can be blank) | Required — child needs parent |
| Delete parent | Child stays (lookup goes null) | Child is deleted (cascade) |
| Roll-Up Summary | Not supported | Supported on parent |
| OWD on child | Independent from parent | Controlled by parent OWD |
| Ownership | Child has own owner | Child inherits parent owner |
| Max per object | 25 | 2 |
One-liner: "Lookup = loose, optional, child survives parent deletion. Master-Detail = tight, required, child cascades on delete, enables Roll-Up Summary. Max 2 MD fields per object. OWD on MD child is controlled by parent — not independently settable."
Q266 YrsArchitecture
A user has a very complex sharing requirement that cannot be achieved by Sharing Rules. How do you implement this?
Use Apex Managed Sharing — programmatically create share records (ObjectName__Share) with specific access levels. This is the most flexible sharing mechanism and can implement any custom sharing logic.
// Apex Managed Sharing — create share record programmatically
public static void shareRecord(Id recordId, Id userId, String accessLevel) {
Account__Share share = new Account__Share();
share.AccountId = recordId; // Record to share
share.UserOrGroupId = userId; // User or Group to share with
share.AccountAccessLevel = accessLevel; // 'Read' or 'Edit'
share.RowCause = Schema.Account__Share.RowCause.Manual;
Database.SaveResult result = Database.insert(share, false);
if(!result.isSuccess()) {
System.debug('Share error: ' + result.getErrors());
}
}
// Custom RowCause for Apex Managed Sharing
// Create custom Sharing Reason on the object first
// share.RowCause = Schema.MyObject__Share.RowCause.MyCustomReason__c;
One-liner: "Apex Managed Sharing creates Share records programmatically using ObjectName__Share — most flexible sharing option. Create a custom Sharing Reason for Apex-managed shares so they persist through manual sharing recalculations."
Q276 YrsAsync Apex
What are different Sync and Async processes in Salesforce? When do you use each?
Synchronous processes run in the same transaction as the triggering action — user waits for completion. Asynchronous processes run in separate transactions — user doesn't wait, fresh governor limits apply.
| Type | Sync/Async | Best For |
|---|---|---|
| Apex Trigger | Sync | Immediate data validation/update |
| Flow (Record-Triggered) | Sync (before/after) | Declarative automation |
| @future | Async | Simple callouts, Mixed DML |
| Queueable Apex | Async | Complex logic, chaining, monitoring |
| Batch Apex | Async | Millions of records |
| Scheduled Apex | Async | Time-based recurring jobs |
| Platform Events | Async | Event-driven integration |
One-liner: "Sync = same transaction, user waits, shared governor limits. Async = separate transaction, fresh limits, user doesn't wait. Use Queueable over @future for complex async — better monitoring, chaining, and supports sObject parameters."
Q286 YrsArchitecture
How does Batch Apex work? Can you call a Batch from another Batch?
Batch Apex processes large data in chunks. start() defines scope, execute() processes each chunk (default 200 records), finish() runs after all chunks complete. You CAN call a batch from finish() — not from execute().
public class AccountBatch implements Database.Batchable<sObject> {
public Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator('SELECT Id, Name FROM Account');
// Supports 50 million records
}
public void execute(Database.BatchableContext bc, List<Account> scope) {
// Fresh governor limits per chunk
// DO NOT call executeBatch() here — nesting batches is not allowed
for(Account a : scope) { a.Description = 'Processed'; }
update scope;
}
public void finish(Database.BatchableContext bc) {
// CAN chain another batch from finish()
Database.executeBatch(new ContactBatch(), 200);
}
}
One-liner: "Batch: start() → chunks → execute() → finish(). Can chain batch from finish() only — NOT from execute(). Max 5 concurrent batches queued per org. Default chunk = 200 records, max = 2000."
Q296 YrsLWC
How do you identify a relationship field in SOQL for both standard and custom objects?
Standard objects have predefined relationship names (Account from Contact, Contacts from Account). Custom objects use the relationship name defined on the lookup field plus __r for traversal.
// Standard — Child-to-Parent (dot notation)
[SELECT Name, Account.Name, Account.Industry FROM Contact]
[SELECT Name, Owner.Name, Owner.Profile.Name FROM Account]
// Standard — Parent-to-Child (subquery, plural)
[SELECT Name, (SELECT Name FROM Contacts) FROM Account]
[SELECT Name, (SELECT Name FROM Opportunities) FROM Account]
// Custom — lookup field: Territory__c → relationship: Territory__r
[SELECT Name, Territory__r.Name FROM Account__c]
// Custom — Parent-to-Child: relationship name on child + __r plural
[SELECT Name, (SELECT Name FROM Accounts__r) FROM Territory__c]
// How to find relationship name: Object Manager → Field → Relationship Name
One-liner: "Standard: predefined names — Account.Name, (SELECT Name FROM Contacts). Custom: FieldName__r for parent traversal, RelationshipName__r for child subquery. Find the relationship name in Object Manager → field definition."
Q306 YrsScenario
Can you pass 1 parameter to both before and after trigger at the same time? Should you use before or after trigger to update a field on the same record?
Yes — both before and after triggers in the same trigger file share Trigger.new and Trigger.old — same data available. For updating a field on the SAME record, always use BEFORE trigger — modify Trigger.new directly without any DML needed.
trigger AccountTrigger on Account (before insert, after insert) {
// Same Trigger.new available in both contexts
if(Trigger.isBefore && Trigger.isInsert) {
for(Account a : Trigger.new) {
// Modify field directly — NO DML needed, saves extra query
a.Description = 'Set in before insert';
}
}
if(Trigger.isAfter && Trigger.isInsert) {
// Id is now available — use for related record operations
for(Account a : Trigger.new) {
System.debug('Account Id: ' + a.Id);
}
}
}
One-liner: "Before trigger: modify Trigger.new directly to update same record fields — no DML needed. After trigger: Id available, use for related record operations. Same Trigger.new is shared across before and after contexts."
Additional Technical Questions — 3 Years
Q313 YrsLWC
What is Shadow DOM in LWC and how does it affect CSS styling?
Shadow DOM encapsulates LWC component internals — CSS styles defined inside a component do not leak out, and external CSS cannot style elements inside the component. This is native browser encapsulation for web components.
/* Styles in child component CSS — scoped, don't leak out */
p { color: red; } /* Only affects <p> tags inside THIS component */
/* Parent CANNOT style child component internals */
/* c-child p { color: blue; } → WON'T WORK in parent CSS */
/* To style across shadow boundary — use CSS custom properties */
/* Child exposes: --text-color: var(--text-color, black); */
/* Parent sets: c-child { --text-color: blue; } */
One-liner: "Shadow DOM encapsulates LWC CSS — styles don't leak in or out. Parent cannot style child internals directly. Use CSS custom properties (CSS variables) to pass styling across the shadow boundary."
Q323 YrsApex
What is a Continuation class in Apex and when should you use it?
Continuation allows long-running callouts (up to 120 seconds) from Visualforce or Aura without blocking the Salesforce server thread. The callout runs asynchronously and a callback method processes the response.
public Object startRequest() {
Continuation con = new Continuation(60); // 60 second timeout
con.continuationMethod = 'processResponse';
HttpRequest req = new HttpRequest();
req.setEndpoint('https://slow-api.example.com/data');
req.setMethod('GET');
this.requestId = con.addHttpRequest(req);
return con; // Hand control back to Salesforce
}
public Object processResponse() {
HttpResponse res = Continuation.getResponse(this.requestId);
// Process response
return Page.myPage;
}
One-liner: "Continuation allows callouts up to 120 seconds without blocking the Salesforce server — the callout runs async and a callback handles the response. Use for slow external APIs where normal 10s limit is insufficient."
Q333 YrsLWC
How do you communicate between sibling LWC components that have no parent-child relationship?
Use Lightning Message Service (LMS) — a pub/sub system for cross-component communication regardless of DOM hierarchy. Define a Message Channel, publish from one component, subscribe in another.
// Publisher component
import { publish, MessageContext } from 'lightning/messageService';
import MY_CHANNEL from '@salesforce/messageChannel/MyChannel__c';
@wire(MessageContext) messageContext;
sendMessage() {
publish(this.messageContext, MY_CHANNEL, { payload: 'Hello sibling!' });
}
// Subscriber component
import { subscribe, MessageContext } from 'lightning/messageService';
import MY_CHANNEL from '@salesforce/messageChannel/MyChannel__c';
@wire(MessageContext) messageContext;
connectedCallback() {
this.subscription = subscribe(this.messageContext, MY_CHANNEL, (msg) => {
this.receivedMessage = msg.payload;
});
}
One-liner: "Lightning Message Service (LMS) enables pub/sub communication between any LWC components regardless of DOM position — sibling, unrelated, even across different regions of a Lightning page."
Q343 YrsApex
What are Slots in LWC and how do you use them?
Slots allow a parent component to inject HTML content into specific places inside a child component — like placeholders. Default slot accepts any content. Named slots accept content targeted by name.
// Child component HTML — defines slot placeholders
<template>
<div class="header">
<slot name="header">Default Header</slot>
</div>
<div class="body">
<slot></slot> <!-- Default (unnamed) slot -->
</div>
</template>
// Parent component — fills the slots
<c-child>
<span slot="header">My Custom Header</span>
<p>This goes into the default slot</p>
</c-child>
One-liner: "Slots are HTML injection points in child components — parent provides content, child defines where it goes. Named slots target specific positions. Default slot catches any unslotted content."
Q353 YrsScenario
How do you handle authentication for REST API callouts in Salesforce Apex?
Use Named Credentials — store endpoint URL and authentication details in Setup, not in code. This avoids hardcoding credentials, supports OAuth automatically, and makes callouts simpler and more secure.
// WITHOUT Named Credential — credentials in code (bad practice!)
HttpRequest req = new HttpRequest();
req.setEndpoint('https://api.example.com/data');
req.setHeader('Authorization', 'Bearer hardcoded_token'); // NEVER do this!
// WITH Named Credential — credentials in Setup, secure
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:MyNamedCredential/endpoint'); // Named Credential name
req.setMethod('GET');
// Auth header added automatically by Salesforce!
Http h = new Http();
HttpResponse res = h.send(req);
One-liner: "Named Credentials store endpoint + auth in Setup — use callout:NamedCredentialName in Apex. Supports OAuth 2.0, Basic Auth, JWT. Never hardcode tokens in Apex code — Named Credentials are the standard at Deloitte implementations."
Q363 YrsTesting
How do you write a Test class for Batch Apex? What methods do you use specifically?
Use Test.startTest() and Test.stopTest() — Database.executeBatch() inside them runs synchronously in tests. Create test data before startTest, assert after stopTest once batch completes.
@isTest
private class AccountBatchTest {
@isTest
static void testBatch() {
// Create test data
List<Account> accounts = new List<Account>();
for(Integer i = 0; i < 200; i++) {
accounts.add(new Account(Name = 'Test ' + i));
}
insert accounts;
Test.startTest();
AccountBatch batch = new AccountBatch();
Database.executeBatch(batch, 200); // Runs synchronously in test
Test.stopTest(); // Batch completes here
// Assert after stopTest
List<Account> updated = [SELECT Id, Description FROM Account];
System.assertEquals('Processed', updated[0].Description);
}
}
One-liner: "Batch test: create data → Test.startTest() → Database.executeBatch() → Test.stopTest() → assert. The batch runs synchronously between startTest and stopTest. Always test with 200 records to verify bulk handling."
Q373 YrsFlows
What are the different types of Flows in Salesforce?
Salesforce has 5 Flow types — each serves a different use case and is triggered differently.
| Flow Type | Triggered By | Use Case |
|---|---|---|
| Record-Triggered Flow | Record create/update/delete | Replace triggers and workflows |
| Screen Flow | User clicks button | Guided user input process |
| Scheduled Flow | Time/date schedule | Nightly jobs, reminders |
| Autolaunched Flow | Apex, Process, REST API | Called from code or other flows |
| Platform Event-Triggered | Platform Event published | Event-driven automation |
One-liner: "5 Flow types: Record-Triggered (replaces triggers/workflows), Screen (guided UI), Scheduled (time-based), Autolaunched (called from code), Platform Event-Triggered (event-driven). Record-Triggered is the most commonly used."
Q383 YrsApex
Can you call a Queueable from inside another Queueable? What is the limit?
Yes — you can chain Queueables by calling System.enqueueJob() from inside execute(). In production, you can chain up to 50 levels deep. In test context, only 1 additional enqueue is allowed per test.
public class Step1Queueable implements Queueable {
public void execute(QueueableContext ctx) {
// Do Step 1 work
List<Account> accounts = [SELECT Id FROM Account LIMIT 200];
for(Account a : accounts) a.Description = 'Step 1';
update accounts;
// Chain Step 2 — only 1 enqueue per execute() allowed
System.enqueueJob(new Step2Queueable());
}
}
public class Step2Queueable implements Queueable {
public void execute(QueueableContext ctx) {
// Step 2 runs after Step 1 completes
}
}
One-liner: "Queueable chaining: call System.enqueueJob() from execute() to chain next step. Max 1 enqueue per execute(). Production limit: 50 chain levels. Test limit: 1 chain. Use chaining for sequential async processing."
Q393 YrsApex
What is HTTPCalloutMock and how do you use it in test classes?
HTTPCalloutMock simulates HTTP callout responses in test classes — real callouts are not allowed in tests. Implement HttpCalloutMock interface, set the mock response, then call the method that makes the callout.
// Mock class
@isTest
public class MockCallout implements HttpCalloutMock {
public HTTPResponse respond(HTTPRequest req) {
HttpResponse res = new HttpResponse();
res.setStatusCode(200);
res.setBody('{"success": true, "data": "test"}');
return res;
}
}
// Test class using mock
@isTest
static void testCallout() {
Test.setMock(HttpCalloutMock.class, new MockCallout());
Test.startTest();
MyCalloutClass.makeCallout(); // Mock intercepts the callout
Test.stopTest();
// Assert based on mock response
}
One-liner: "HttpCalloutMock simulates callout responses in tests — real HTTP calls not allowed. Implement the interface, return fake response, set with Test.setMock() before calling the method under test."
Q403 YrsScenario
How do you create a list view button that opens an LWC component?
Create a Lightning Quick Action of type LWC — add it to the List View button section in Object Manager. The LWC gets selectedRecordIds from the action context for the selected records.
// meta XML — expose for list view action
<targets>
<target>lightning__ListView</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__ListView">
<property name="selectedRecordIds" type="String[]" />
</targetConfig>
</targetConfigs>
// JavaScript — receive selected record Ids
@api selectedRecordIds; // Array of selected record Ids from list view
connectedCallback() {
console.log('Selected: ', this.selectedRecordIds);
// Process selected records
}
One-liner: "Add lightning__ListView as target in meta XML. Set as Quick Action on the object. LWC receives selectedRecordIds via @api property containing the Ids of checked records in the list view."
Q413 YrsApex
What is the difference between a before insert and after insert trigger context? When is the record Id available?
Before insert: record not yet committed to database — Id is NULL. Modify Trigger.new fields directly. After insert: record committed — Id is populated. Use for creating related records or callouts.
| Context | Record Id | Trigger.old | Use For |
|---|---|---|---|
| before insert | NULL | Not available | Validate/set field values |
| after insert | Available | Not available | Create related records |
| before update | Available | Available | Validate/modify before save |
| after update | Available | Available | Update related records |
| before delete | Available | Trigger.old only | Prevent deletion |
| after delete | Available | Trigger.old only | Clean up related records |
One-liner: "Before insert: Id is null — modify Trigger.new directly, no DML needed. After insert: Id available — use to create child records. Trigger.old only available on update and delete contexts."
Q423 YrsScenario
What are different events in Apex Triggers? Which ones have Trigger.old and Trigger.new?
7 trigger events: before/after insert, before/after update, before/after delete, after undelete. Trigger.new = new record state. Trigger.old = previous record state. Both available only in update context.
| Event | Trigger.new | Trigger.old |
|---|---|---|
| before insert | Yes (no Id) | No |
| after insert | Yes (with Id) | No |
| before update | Yes | Yes |
| after update | Yes | Yes |
| before delete | No | Yes |
| after delete | No | Yes |
| after undelete | Yes | No |
One-liner: "Both Trigger.new AND Trigger.old available only in before/after update. Delete events: only Trigger.old. Insert/undelete: only Trigger.new. Compare old vs new values in update to detect field changes."
Q433 YrsFlows
What are different actions performed on Record-Triggered Flows?
Record-Triggered Flows can perform: Create Records, Update Records, Delete Records, Get Records, Send Email, Create Chatter Post, Send Custom Notification, Call Apex (Invocable Method), call Subflows, and run Scheduled Paths.
Record-Triggered Flow — Key Options
Trigger: A record is created / updated / deletedWhen to Run: Before Save (fast, no DML) or After Save (can query other objects)
Scheduled Paths: Run hours/days after the trigger condition — e.g., send reminder 3 days after Case created if still open
Entry Conditions: Only run when specific field conditions are met — prevents unnecessary execution
One-liner: "Record-Triggered Flow: Before Save for field updates on same record (no DML), After Save for related record operations and callouts. Scheduled Paths enable time-delayed automation from the same trigger."
Q443 YrsApex
If I need third-party JavaScript in my LWC component, how do I include it?
Upload the JavaScript library as a Static Resource in Salesforce, then load it in LWC using loadScript() from the platformResourceLoader module. Load in connectedCallback() and use after the promise resolves.
import { LightningElement } from 'lwc';
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import CHART_JS from '@salesforce/resourceUrl/ChartJS';
export default class MyChart extends LightningElement {
connectedCallback() {
loadScript(this, CHART_JS)
.then(() => {
// ChartJS now available — initialize chart
const ctx = this.template.querySelector('canvas');
new Chart(ctx, { type: 'bar', data: {} });
})
.catch(error => console.error(error));
}
}
One-liner: "Third-party JS in LWC: upload as Static Resource → import resource URL → loadScript() in connectedCallback() → use library only inside the .then() callback after it loads."
Q453 YrsScenario
How does data flow from parent to child and child to parent in LWC?
Parent to Child: use @api properties — parent sets them as HTML attributes. Child to Parent: fire a custom event with CustomEvent() in the child, parent listens with an event handler.
// Parent → Child (via @api property)
// Parent HTML: <c-child message={parentMessage}></c-child>
// Child JS receives it
@api message; // Parent sets this as attribute
// Child → Parent (via Custom Event)
// Child fires event
handleClick() {
this.dispatchEvent(new CustomEvent('childaction', {
detail: { value: 'data from child' }
}));
}
// Parent listens
// <c-child onchildaction={handleChildAction}></c-child>
handleChildAction(event) {
this.receivedData = event.detail.value;
}
One-liner: "Parent to Child = @api property set as HTML attribute. Child to Parent = CustomEvent dispatched from child, parent listens with onEventName handler. One-way data binding in both directions."
Additional Questions — 6 Years Experience
Q466 YrsArchitecture
How do you troubleshoot a failed Scheduled Batch job in production?
Check AsyncApexJob in Setup → Apex Jobs. Query AsyncApexJob for status, ExtendedStatus (error message), and NumberOfErrors. Check debug logs if enabled, review Apex exception emails, and check if the schedule is still active.
// Query job status
List<AsyncApexJob> jobs = [
SELECT Id, Status, ExtendedStatus, NumberOfErrors,
JobItemsProcessed, TotalJobItems, CreatedDate
FROM AsyncApexJob
WHERE ApexClass.Name = 'MyBatchClass'
ORDER BY CreatedDate DESC
LIMIT 5
];
// Common failure causes:
// 1. ExtendedStatus shows the exception
// 2. Governor limits hit in execute()
// 3. Null pointer exception in start() query
// 4. Schedule aborted by another admin
// 5. Org maintenance window killed running jobs
One-liner: "Query AsyncApexJob for ExtendedStatus — it contains the exact error message. Check Apex Jobs in Setup UI. Common causes: governor limits, null pointer in start(), org maintenance. Resend Apex exception emails in Setup."
Q476 YrsArchitecture
A validation rule is blocking a critical business update in production. How do you handle it without disabling the rule?
Add a bypass condition to the validation rule using a custom checkbox field or Custom Permission. This allows specific users or processes to bypass the rule without disabling it for everyone.
// Option 1 — Custom field bypass
// Add checkbox field: Bypass_Validation__c on the object
// Validation rule condition:
// AND(NOT(Bypass_Validation__c), ISBLANK(Required_Field__c))
// Set Bypass_Validation__c = true via Flow/Apex for specific scenarios
// Option 2 — Custom Permission bypass (better for profile-based bypass)
// Create Custom Permission: Bypass_Validation_Rules
// Assign to specific Profile or Permission Set
// Validation rule condition:
// AND(NOT($Permission.Bypass_Validation_Rules), ISBLANK(Required_Field__c))
One-liner: "Add bypass using Custom Permission in validation rule formula — NOT($Permission.Bypass_Validation_Rules). Assign the permission to integration user or admin profile. Never disable validation rules in production without a bypass pattern."
Q486 YrsArchitecture
What is Platform Events in Salesforce? How does it differ from Custom Objects for integration?
Platform Events are messages published to an event bus — they enable event-driven architecture. Unlike Custom Objects, Platform Events are not stored in the database (unless High Volume) and support real-time pub/sub with a 3-day replay window.
| Feature | Platform Events | Custom Objects |
|---|---|---|
| Storage | Not stored (Standard) / Stored (HV) | Always stored in database |
| Real-time | Yes — immediate delivery | No — polling required |
| Replay | 3-day replay window | Always queryable |
| Use case | Integration notifications | Persistent data storage |
| External subscribe | CometD / gRPC streaming | REST API polling |
One-liner: "Platform Events = event bus messages, real-time delivery, 3-day replay, not stored in DB. Custom Objects = persistent storage, queryable anytime. Use Platform Events for integration notifications, Custom Objects for data that needs to be queried later."
Q496 YrsLeadership
How do you handle a situation where a client insists on a technical approach you know is wrong?
This is a core Deloitte consultant skill — influence without authority. Present data and risk, offer alternatives, document your recommendation, but ultimately respect client decision if they proceed.
Deloitte Expected Answer Framework
Step 1 — Understand: "First I ask why they want this approach — there may be a business constraint I'm not aware of."Step 2 — Present risk: "I document the risks — performance impact, maintenance overhead, governor limit concerns — in writing."
Step 3 — Offer alternatives: "I present 2-3 alternative approaches that meet their business goal with lower risk."
Step 4 — Document: "If they still proceed, I get sign-off on a risk acceptance document — this protects both parties."
Step 5 — Execute: "I implement their decision professionally, monitoring for the risks I flagged."
One-liner: "Present risk in writing, offer alternatives, get sign-off if they proceed anyway. A consultant's job is to advise, not overrule. Document everything — protect yourself and the client."
Q506 YrsArchitecture
How do you design a solution for sending 50,000 emails from Salesforce in a single day without hitting limits?
Salesforce has a 5,000 single email limit per day per org in standard licenses. For 50,000 emails use: Marketing Cloud (MCAE/Pardot) for mass email campaigns, or Batch Apex with Messaging.sendEmail() distributed across multiple days/batches.
Senior Consultant Architecture Options
Option 1 — Marketing Cloud: If it's a marketing campaign, use Marketing Cloud Account Engagement (Pardot) — no Salesforce email limits applyOption 2 — Batch Apex: Split 50K emails into batches. Per-org limit is 5K/day. With Professional+ licenses, limit increases. Check Limit.getSingleEmailLimits()
Option 3 — External Email Service: Use SendGrid, AWS SES, or similar via REST API from Apex — bypasses all Salesforce email limits
Option 4 — Mass Email API: Uses different limit pool than SingleEmail
One-liner: "50K emails/day exceeds standard Salesforce limits. Use Marketing Cloud for campaigns, external email service (SendGrid/SES) via REST API for transactional volume, or distribute across multiple days with Batch Apex."
Q516 YrsArchitecture
What is the Trigger Order of Execution in Salesforce? Name all steps.
Salesforce processes a save in a specific sequence — understanding this is critical for diagnosing unexpected behavior in complex orgs.
| # | Step |
|---|---|
| 1 | Load original record (for updates) |
| 2 | Apply new field values from UI/API |
| 3 | System Validations (required fields, max length, data type) |
| 4 | Before Triggers |
| 5 | Custom Validations (Validation Rules) |
| 6 | Duplicate Rules |
| 7 | Save to database (not committed yet) |
| 8 | After Triggers |
| 9 | Assignment Rules |
| 10 | Auto-Response Rules |
| 11 | Workflow Rules → Field Updates (re-runs triggers) |
| 12 | Escalation Rules |
| 13 | Record-Triggered Flows |
| 14 | Entitlement Rules |
| 15 | Roll-Up Summary recalculation |
| 16 | Criteria-based Sharing evaluation |
| 17 | Commit to database |
| 18 | Post-Commit: Send emails, async jobs |
One-liner: "Order: System validation → Before Trigger → Validation Rules → Duplicate Rules → Save (not committed) → After Trigger → Workflow → Flows → Commit → Post-commit emails. Workflow field updates re-fire triggers once."
Q526 YrsArchitecture
What is the difference between Custom Metadata Types and Custom Settings? When would you use each?
Custom Metadata is deployable and packageable — values move with deployments. Custom Settings are org-specific data — not deployed with metadata. Use Custom Metadata for configuration, Custom Settings for org-specific runtime values.
| Feature | Custom Metadata | Custom Settings |
|---|---|---|
| Deployable | Yes — moves with metadata | No — org specific |
| Packageable | Yes | Hierarchy type: No. List type: Yes |
| SOQL | Yes | Yes (or getInstance()) |
| Governor limits | No SOQL limit | Counts toward SOQL limit |
| Use case | Config values, mappings, rules | Org-specific keys, feature flags |
One-liner: "Custom Metadata moves with deployments — use for configuration that should be consistent across environments. Custom Settings are org-specific — use for values that differ between sandbox and production like API keys."
Q536 YrsArchitecture
How would you migrate 10 million records from a legacy system into Salesforce?
Use Bulk API 2.0 — designed for millions of records, processes asynchronously, no governor limits. Split into 10M/10K = 1,000 jobs of 10K records each. Run in parallel for speed. Validate data before migration, archive originals.
Migration Strategy — Senior Consultant Approach
Phase 1 — Prepare: Data cleansing, deduplication, mapping legacy fields to Salesforce fields, set External IDs for upsertPhase 2 — Pilot: Migrate 1% (100K records) first — validate data quality, performance, business rule behavior
Phase 3 — Full Migration: Bulk API 2.0 in parallel batches — disable triggers, validation rules, sharing recalculation during migration window
Phase 4 — Validate: Record counts, spot-check data quality, re-enable automation
Tools: Salesforce Data Loader, MuleSoft, Informatica, or custom Bulk API 2.0 integration
One-liner: "10M record migration: Bulk API 2.0, External IDs for upsert matching, disable triggers/validations during migration window, pilot with 1% first, validate counts after. Always keep originals until validation complete."
Q546 YrsLeadership
What is your approach to estimating effort for a Salesforce implementation at Deloitte?
Break down requirements into user stories. Estimate each story in story points or hours. Add buffer for integration complexity, data migration, testing, and UAT. Include non-development time — meetings, documentation, training.
Deloitte Estimation Framework
Discovery (10-15%): Requirements gathering, process mapping, gap analysisDesign (15-20%): Data model, integration design, security model, wireframes
Build (40-50%): Configuration, development, integration, unit testing
Test (15-20%): System Integration Testing, UAT, performance testing
Deploy (5-10%): Data migration, training, go-live support
Add 20% buffer for unknowns. Get sign-off on estimates before committing.
One-liner: "Estimate by phase: Discovery 15%, Design 20%, Build 45%, Test 15%, Deploy 5%. Add 20% buffer. Never give estimates without requirements — 'it depends' is a valid answer until you have full scope."
Q556 YrsArchitecture
How do you implement a solution where the same Salesforce trigger should NOT fire when an integration user updates records?
Use a Custom Permission or a dedicated Integration User Profile. Check in the trigger using UserInfo.getUserId() or $Permission in a formula, and bypass trigger logic for the integration user.
// Option 1 — Check by User Profile name
trigger AccountTrigger on Account (before insert) {
User currentUser = [SELECT Profile.Name FROM User
WHERE Id = :UserInfo.getUserId()];
if(currentUser.Profile.Name == 'Integration User Profile') {
return; // Skip all logic for integration user
}
// Normal trigger logic
}
// Option 2 — Custom Permission (better practice)
if(FeatureManagement.checkPermission('Bypass_Account_Trigger')) {
return; // Integration user has this permission, skip trigger
}
One-liner: "Use Custom Permission to bypass trigger — assign it to integration user's Permission Set. Check FeatureManagement.checkPermission() in trigger to skip logic. Cleaner than Profile name checks which break when Profile is renamed."
Q566 YrsArchitecture
What is Change Data Capture (CDC) in Salesforce and when would you use it at Deloitte?
Change Data Capture streams real-time change events (create, update, delete, undelete) to external systems via the event bus. It generates ChangeEvent records for subscribed objects — external systems subscribe via CometD or gRPC.
CDC vs Platform Events — Key Difference
Platform Events: you define what to publish and whenChange Data Capture: Salesforce automatically generates events for all changes to subscribed objects — no code needed to publish
Use CDC at Deloitte when: external system (ERP, Data Warehouse) needs to stay in sync with Salesforce in real-time without polling
One-liner: "CDC automatically streams all create/update/delete changes for subscribed objects to external systems. No publish code needed — Salesforce generates the events. Use for real-time ERP/data warehouse sync without polling."
Q576 YrsArchitecture
A client at Deloitte has 500 active Flows and performance is degrading. How do you optimize?
500 Flows on one org is a governance failure — too many automations with overlapping triggers. Audit and consolidate: merge multiple Record-Triggered Flows on the same object into one Flow with decision branches.
Flow Optimization Strategy
1. Audit: Use Flow Trigger Explorer to see all automations per object2. Consolidate: Merge multiple flows on same object trigger into one Flow with Decision elements
3. Entry conditions: Ensure all Flows have tight entry conditions so they don't run unnecessarily
4. Deactivate: Find inactive or redundant Flows — deactivate and archive
5. Before vs After: Move field-only updates to Before Save — much faster
6. Avoid Get Records in loops: Same governor limit rule as Apex
One-liner: "500 Flows = governance problem. Use Flow Trigger Explorer to audit. Merge multiple per-object flows into one with Decision branches. Add tight entry conditions. Use Before Save for field-only updates. Deactivate unused flows."
Q586 YrsArchitecture
How do you implement a complete CI/CD pipeline for a Salesforce project at Deloitte?
Use SFDX (Salesforce DX) with Git branching strategy, automated testing, and tools like GitHub Actions or Jenkins for pipeline automation. DevOps Center is Salesforce's native CI/CD tool.
Deloitte CI/CD Pipeline
Source Control: Git (GitHub/Azure DevOps/Bitbucket) — one branch per featureOrgs: Dev Sandbox → Integration Sandbox → QA → UAT → Production
Pipeline Steps:
1. Developer pushes to feature branch
2. PR triggers automated deployment to Integration sandbox
3. Run Apex test classes (75%+ coverage gate)
4. Code review + approval
5. Merge to main → deploy to QA automatically
6. After UAT sign-off → deploy to Production via change set or SFDX
Tools: SFDX CLI, GitHub Actions/Jenkins, PMD for static analysis, Copado/Gearset for managed deployments
One-liner: "CI/CD: Git feature branches → PR triggers auto-deploy to Integration → run Apex tests (75% gate) → code review → merge to main → auto-deploy to QA → manual UAT sign-off → production deployment. PMD for static analysis."
Q596 YrsLeadership
How do you onboard a new Salesforce developer to a complex existing Deloitte project?
Structured onboarding reduces ramp-up time and prevents mistakes. Start with understanding business context before touching code. Pair programming for first 2 weeks before independent tasks.
30-60-90 Day Onboarding Plan
Day 1-7: Org access setup, codebase tour, architecture overview, intro to client businessWeek 2: Shadow developer, review existing Apex classes and triggers, understand data model
Week 3-4: Pair programming on small tasks, read existing test classes to understand patterns
Month 2: Independent small features with code review, attend client calls as listener
Month 3: Own a full feature end-to-end including test class and documentation
One-liner: "Onboard with business context first, not code. Week 1: shadow and understand. Week 2-4: pair programming. Month 2: independent work with review. Month 3: own a feature. Never let a new developer touch production without review in first 30 days."
Q606 YrsArchitecture
What is the difference between Queueable Apex and Batch Apex? When do you choose Queueable over Batch at Deloitte?
Queueable is for complex async processing with chaining and monitoring — no size constraint. Batch is for processing millions of records in chunks. Use Queueable for complex logic on a moderate dataset, Batch for LDV processing.
| Feature | Queueable | Batch |
|---|---|---|
| Max records | 50,000 per transaction | 50,000,000 (QueryLocator) |
| Chaining | Yes — enqueueJob in execute() | Yes — from finish() only |
| sObject params | Yes | Via QueryLocator |
| Monitoring | Yes (AsyncApexJob) | Yes (AsyncApexJob) |
| Callouts | Yes (implement Database.AllowsCallouts) | Yes (Database.AllowsCallouts) |
| Best for | Complex logic, chaining, moderate data | Millions of records, LDV |
One-liner: "Use Queueable when: complex async logic, chaining multiple steps, passing sObjects between jobs. Use Batch when: processing 50K+ records, need per-chunk fresh limits. At Deloitte: Queueable is preferred for integration jobs, Batch for data processing."
SF
By SF Interview Pro
Salesforce Interview Prep Team
Practical Q&A by working Salesforce professionals · LWC, Apex, Data Cloud & AI
☕
☕ Enjoyed this article?
SF Interview Pro is 100% free and maintained by a Salesforce professional. No ads, no paywalls, and no signup required. If this guide helped you prepare for an interview, earn a certification, or grow your Salesforce career, consider buying me a coffee! ☕💜
🇮🇳 UPI (India)
Pay by QR
GPay · PhonePe · Paytm · BHIM
Keep Preparing
New interview questions every week
Follow for fresh Salesforce Q&A, free courses, and real interview experiences — straight from the trenches.
Follow Us ↗