100 Salesforce Developer Interview Questions and Answers for 6+ Years Experience (2026)

100 Salesforce Developer Interview Questions 6+ Years Experience 2026 | SF Interview Pro
Senior Level — 6+ Years Experience

100 Salesforce Developer
Interview Questions

Senior-level questions on Apex design patterns, async architecture, SOQL at scale, LWC, integration, security and DevOps — with answers you can actually say in the room.

100
Questions
9
Categories
6+ Yrs
Experience
Free
No Paywall

Preparing for a senior Salesforce developer interview with 6+ years of experience? These 100 questions cover what interviewers actually ask at that level — advanced Apex design patterns, governor limits and async processing, SOQL performance at large data volumes, Lightning Web Components, integration architecture, security, DevOps, and real production scenarios. Every answer follows a 7-step format ending with a one-line answer you can say directly in the interview. All questions are free, with no signup required.

Advanced Apex & Design Patterns
Enterprise patterns, decoupling, and code architecture at senior level
Q1–Q12
Q1How would you design a trigger framework for an org with 30+ objects and multiple teams deploying independently? Advanced
🏗One trigger per object that delegates to a handler class extending a shared base. The base handles context routing, recursion control, and a Custom Metadata bypass switch so any handler can be disabled in production without a deployment.
trigger OrderTrigger on Order__c (before insert, before update, after insert, after update) { TriggerDispatcher.run(new OrderTriggerHandler()); } public virtual class TriggerHandler { public void run() { if(TriggerBypass.isDisabled(getName())) return; if(Trigger.isBefore && Trigger.isInsert) beforeInsert(); if(Trigger.isAfter && Trigger.isInsert) afterInsert(); } protected virtual void beforeInsert() {} protected virtual void afterInsert() {} }
✅ What the Framework Controls
  • Execution order: One trigger means no ambiguity about which fires first
  • Bypass switches: Custom Metadata flag disables a handler during data migrations
  • Testability: Handler methods are unit-testable without DML overhead
  • It does not bypass governor limits — each handler still needs bulkified logic
🏭 Real World — XYZ Company

XYZ Company had 4 separate triggers on Account written by 3 different teams over 5 years. Consolidating into one dispatcher plus handler classes cut CPU time by 40% and made it possible to disable a specific handler during a 2 million record migration without touching code.

🎯 Key Points for Interviewer
  • One trigger per object, logic lives entirely in the handler
  • Custom Metadata bypass avoids emergency deployments
  • Static Set of processed IDs for recursion, not a Boolean
  • Service classes hold business logic so handlers stay thin
Say This in Interview
"One trigger per object delegating to a handler class, with the base dispatcher owning context routing, recursion control, and a Custom Metadata bypass switch so we can disable automation during migrations without a deployment."
Q2Why is a static Boolean a poor way to prevent trigger recursion, and what should you use instead? Advanced
A static Boolean blocks the entire remaining transaction, including legitimate processing of different records in the same batch. A static Set of already-processed record IDs blocks only the records that have already run.
// Naive: kills bulkification for the whole transaction if(TriggerHelper.isRunning) return; TriggerHelper.isRunning = true; // Correct: per-record recursion guard List<Order__c> toProcess = new List<Order__c>(); for(Order__c o : Trigger.new) { if(!TriggerHelper.processedIds.contains(o.Id)) { toProcess.add(o); TriggerHelper.processedIds.add(o.Id); } }
🏭 Real World — XYZ Company

XYZ Company had a rollup that double-counted revenue because a workflow field update re-fired the after-update trigger. A static Boolean fixed the double count but silently skipped 60% of records in bulk loads. Switching to a static Set of processed IDs fixed both problems.

Say This in Interview
"A static Boolean stops all records for the rest of the transaction and quietly breaks bulk processing. I track processed record IDs in a static Set so only records that already ran are skipped."
Q3Explain dependency injection in Apex and how it changes the way you write tests. Advanced
🔧Dependency injection means passing collaborators in through the constructor as interfaces instead of instantiating them inside the class. That lets tests inject a mock and verify behaviour without callouts or DML.
public interface IPaymentService { Boolean charge(Id orderId, Decimal amt); } public class OrderProcessor { private IPaymentService payments; public OrderProcessor(IPaymentService svc) { this.payments = svc; } public Boolean process(Id orderId, Decimal amt) { return payments.charge(orderId, amt); } } // Test injects a mock, no real gateway call public class MockPayments implements IPaymentService { public Boolean wasCalled = false; public Boolean charge(Id orderId, Decimal amt) { wasCalled = true; return true; } }
Say This in Interview
"Dependency injection passes collaborators in as interfaces rather than creating them inside the class, so tests can inject mocks and assert behaviour in isolation without hitting real callouts or DML."
Q4What is the Strategy pattern and where does it genuinely earn its place in a Salesforce org? Advanced
Strategy encapsulates interchangeable algorithms behind one interface, selected at runtime by a factory. It earns its place when the same business operation has several variants that change independently — pricing tiers, discount models, or tax calculation by country.
🔍 When It Helps vs When It Is Overkill
SituationStrategy?Why
3+ pricing models, new ones added yearly✅ YesNew class, zero edits to existing code
Two branches that never change❌ NoA simple if-else is clearer
Country-specific tax rules✅ YesEach country is independently testable
🏭 Real World — XYZ Company

XYZ Company prices quotes using Simple, Tiered, and Volume models depending on account tier. A factory returns the right strategy per account, so adding a new Distributor tier meant writing one class and one factory line instead of editing a 400-line pricing method.

Say This in Interview
"Strategy puts each algorithm variant in its own class behind a shared interface, chosen by a factory at runtime, so adding a new pricing model means adding a class rather than editing existing tested code."
Q5What is the difference between the insert DML statement and Database.insert()? Medium
💾The insert statement is all-or-nothing and throws a DmlException on any failure. Database.insert(records, false) allows partial success and returns a SaveResult array telling you exactly which records failed and why.
AspectinsertDatabase.insert()
On errorThrows DmlExceptionReturns SaveResult
Partial success❌ No✅ Yes (allOrNone=false)
Best forAtomic business operationsBulk loads and integrations
🏭 Real World — XYZ Company

XYZ Company imports 5,000 leads nightly from a partner feed. With allOrNone=false, 4,980 load successfully and the 20 malformed rows are written to an Integration_Error__c object for review, instead of the whole night failing on one bad email address.

Say This in Interview
"insert is all-or-nothing and throws; Database.insert with allOrNone false commits the good records and returns a SaveResult array so I can log each failure individually."
Q6What is the difference between with sharing, without sharing, and inherited sharing? Medium
🔒These keywords control whether the class enforces the running user's record-level sharing. They do NOT enforce field-level or object-level security — that needs WITH USER_MODE or Security.stripInaccessible.
KeywordSharing enforced?Use when
with sharing✅ YesControllers exposed to users
without sharing❌ NoDeliberate system-level operations
inherited sharingDepends on callerShared utility and service classes
🎯 Key Points for Interviewer
  • Sharing keywords affect records only, never fields
  • inherited sharing is the secure default for utilities
  • A class with no keyword runs without sharing in most entry points
  • Inner classes inherit the outer class sharing setting
Say This in Interview
"They control record-level sharing only. inherited sharing is the secure default for shared utilities because it respects whatever context called it, and none of them enforce field-level security."
Q7When would you choose a Custom Metadata Type over a Custom Setting in 2026? Medium
Default to Custom Metadata Types. Their records deploy with code, consume no data storage, and are cached so queries do not count against SOQL limits. Use Hierarchy Custom Settings only when you genuinely need per-user or per-profile overrides.
FactorCustom MetadataCustom Setting
Deploys with code✅ Yes❌ Separate data load
Counts toward SOQL limit❌ No (cached)Depends on access method
Per-user override❌ No✅ Hierarchy type
Data storage cost❌ None✅ Counts
🏭 Real World — XYZ Company

XYZ Company moved its ERP integration endpoints from Custom Settings to Custom Metadata. Every sandbox refresh previously required a manual data load step that was regularly forgotten; now the endpoints deploy automatically with the code.

Say This in Interview
"Custom Metadata for anything that should travel with the code and is cached SOQL-free. Hierarchy Custom Settings only when I need genuine per-user or per-profile overrides."
Q8What are virtual, abstract, and override in Apex, and how do they combine into the Template Method pattern? Medium
🏗abstract forces a subclass to implement a method with no default. virtual provides a default that a subclass may override. override marks the replacing method. Together they let a base class own the workflow while subclasses fill in the variable steps.
public abstract class BaseIntegrationHandler { public abstract HttpRequest buildRequest(String endpoint); public virtual Integer getTimeout() { return 30000; } // Template method: fixed workflow, variable steps public HttpResponse execute(String endpoint) { HttpRequest req = buildRequest(endpoint); req.setTimeout(getTimeout()); return new Http().send(req); } }
Say This in Interview
"abstract forces implementation, virtual gives an overridable default, and override marks the replacement. A base class using them defines a fixed workflow while subclasses supply only the parts that vary."
Q9How do you create and use custom exception classes, and why not just catch generic Exception? Medium
🚨Extend Exception and give the class a domain name. Distinct exception types let callers catch each failure mode separately and respond differently, instead of catching everything and guessing what went wrong.
public class OrderValidationException extends Exception {} public class IntegrationException extends Exception {} try { processOrder(order); } catch(OrderValidationException e) { addFieldError(e.getMessage()); // user fixes the data } catch(IntegrationException e) { logAndRetryLater(e); // system problem, retry }
Say This in Interview
"Custom exceptions extend Exception and must end in Exception. Naming them by domain lets callers catch validation failures and integration failures separately and respond to each appropriately."
Q10What is a wrapper class and when is it the right answer? Easy
🧩A wrapper class bundles records plus derived values into a single custom type. It is the right answer when the UI needs data that does not exist as a field — a selection checkbox, a computed total, or records from unrelated objects returned together.
public class QuoteWrapper { @AuraEnabled public Quote record; @AuraEnabled public List<QuoteLineItem> lines; @AuraEnabled public Decimal grandTotal; // computed, not stored @AuraEnabled public Boolean isSelected; // UI state only }
Say This in Interview
"A wrapper combines records and computed values into one type for the UI. I mark members AuraEnabled so LWC can consume it, and it is never stored in the database."
Q11What is the Safe Navigation Operator and where does it not help? Easy
🛡The Safe Navigation Operator (?.) short-circuits a chained expression and returns null instead of throwing a NullPointerException when any link is null. It does not help when the null itself is the bug — it just hides the symptom further downstream.
// Verbose null checks String owner = null; if(c.Account != null && c.Account.Owner != null) owner = c.Account.Owner.Name; // Same result, one line String owner = c.Account?.Owner?.Name;
Say This in Interview
"The safe navigation operator returns null instead of throwing when a link in a chain is null. It removes nested null checks, but I still validate where a null actually indicates a data problem."
Q12What is Database.setSavepoint() and what are its real limitations? Advanced
💾A savepoint marks a rollback point so you can undo part of a transaction and continue. The critical limitation is that rollback does NOT reset governor limit counters, and it cannot undo callouts or already-published platform events.
Savepoint sp = Database.setSavepoint(); try { insert riskyRecords; } catch(DmlException e) { Database.rollback(sp); // undo only this part logFailure(e); } update safeRecords; // unaffected by the rollback
⚠ Limitations to Mention
  • Each savepoint consumes one DML statement from the limit
  • Rollback does not reset SOQL or DML counters already consumed
  • Cannot undo a callout or a published platform event
  • Savepoints do not cross async boundaries
Say This in Interview
"Savepoints give partial rollback inside one transaction, but they cost a DML statement, do not reset governor counters, and cannot undo callouts or published platform events."
Governor Limits, Batch & Async Apex
Scale, chaining, failure handling and async architecture decisions
Q13–Q26
Q13How do you choose between Future, Queueable, Batch and Scheduled Apex? Medium
🧮Queueable is the modern default. Future is legacy and only accepts primitives. Batch is for volumes that will not fit in a single transaction. Scheduled is about timing, not processing model — it usually just launches a Batch or Queueable.
TypeBest forKey constraint
FutureSimple fire-and-forget calloutsPrimitives only, no chaining, no monitoring
QueueableChaining, sObject params, Finalizers50 enqueued per sync transaction
BatchMillions of records5 concurrent batch jobs per org
ScheduledTime-based triggering100 scheduled jobs per org
🏭 Real World — XYZ Company

XYZ Company runs a nightly Batch to recalculate 2 million account scores, a Queueable chain to charge payment then send confirmation in order, and Future for a fire-and-forget audit log callout where failure does not matter.

Say This in Interview
"Queueable by default for its chaining, object parameters and Finalizer support. Batch when volume exceeds one transaction, Scheduled purely for timing, and Future only in legacy code."
Q14What is a Transaction Finalizer and what problem did it solve? Advanced
🏁A Finalizer implements System.Finalizer and runs after a Queueable completes, including when it fails with an unhandled exception. Before Finalizers there was no reliable in-platform way to detect and respond to Queueable failures.
public class SyncFinalizer implements System.Finalizer { public void execute(System.FinalizerContext ctx) { if(ctx.getResult() == ParentJobResult.UNHANDLED_EXCEPTION) { insert new Integration_Error__c( Message__c = ctx.getException().getMessage(), Job_Id__c = ctx.getAsyncApexJobId() ); System.enqueueJob(new SyncJob()); // one retry allowed } } }
Say This in Interview
"A Finalizer runs after a Queueable regardless of outcome, so it is the built-in hook for logging failures and re-enqueuing a retry. Before Finalizers, a failed Queueable simply vanished."
Q15How do you implement a Dead Letter Queue for failed async jobs in Salesforce? Advanced
📬Salesforce has no native DLQ. You build one with a custom object storing the payload, error, and retry count, populated by a Finalizer, drained by a scheduled retry job, and escalated to a human after a maximum retry count.
🏗 Implementation Steps
  • 1
    Create Failed_Job__c with Payload__c, Error__c, Retry_Count__c, Status__c
  • 2
    Finalizer writes a record on UNHANDLED_EXCEPTION with full serialised context
  • 3
    Scheduled job queries Status = New and Retry_Count < 3 each hour
  • 4
    Deserialise payload, re-enqueue the job, increment Retry_Count
  • 5
    After 3 attempts set Status = Dead and alert the integration owner
🏭 Real World — XYZ Company

XYZ Company lost roughly 40 order syncs a month to silent Queueable failures. After adding a DLQ with hourly retries and escalation after three attempts, silent data loss dropped to zero and the integration team gets a single daily digest instead of surprise customer complaints.

Say This in Interview
"A Finalizer writes failed jobs with their payload to a custom object, a scheduled job retries them with backoff up to three times, then marks them Dead and alerts a human. That gives us zero silent data loss."
Q16Can you call Database.executeBatch() from inside a Batch execute() method? Advanced
No. You cannot start a batch from within execute(). You CAN start one from finish(). If you need to trigger downstream work mid-batch, enqueue a Queueable from execute() instead.
global void execute(Database.BatchableContext bc, List<Account> scope) { // Database.executeBatch(new NextBatch()); // FAILS System.enqueueJob(new IntermediateQueueable(scope)); // allowed } global void finish(Database.BatchableContext bc) { Database.executeBatch(new NextBatch(), 200); // allowed }
Say This in Interview
"Batch chaining happens in finish, never in execute. If I need async work mid-batch I enqueue a Queueable from execute, which is permitted."
Q17What is Database.Stateful and when should you avoid it? Medium
📦Database.Stateful preserves instance variables across execute() calls so you can accumulate totals and error lists for the finish() summary. Avoid it when the collection could grow large, because that state counts against heap on every chunk.
⚠ When Not to Use It
  • Accumulating thousands of records or IDs will eventually blow heap
  • If you only need counts, query AsyncApexJob in finish() instead
  • Good use: running totals, error message lists, small ID sets
Say This in Interview
"Stateful keeps instance variables alive across batch chunks for running totals and error logs. I keep that state small because it counts against heap in every chunk, and I use AsyncApexJob for simple counts."
Q18Why can you not make a callout after a DML in the same transaction, and how do you work around it? Medium
🚫Uncommitted DML holds database locks open. Allowing a slow callout while locks are held would block other transactions, so Salesforce blocks it. The fix is to defer the callout to an async context that runs after commit.
// Fails: DML then callout in one transaction insert order; Http h = new Http(); h.send(req); // CalloutException // Works: commit first, call out asynchronously insert order; System.enqueueJob(new ERPSyncJob(order.Id)); // Also fine: callout BEFORE any DML HttpResponse res = h.send(req); insert order;
Say This in Interview
"Uncommitted DML holds row locks, so Salesforce blocks callouts afterwards. Callout-then-DML is fine; DML-then-callout needs the callout deferred to a Queueable that runs after commit."
Q19How do you handle a Mixed DML error? Medium
🔄Salesforce forbids DML on setup objects (User, Group, PermissionSetAssignment) in the same transaction as business objects. Split them by moving one side into a Future method, or use System.runAs in tests.
// Fails insert new Account(Name='XYZ'); insert new User(...); // MIXED_DML_OPERATION // Works: setup DML in a separate transaction @future public static void createUserAsync(String json) { insert (User)JSON.deserialize(json, User.class); }
Say This in Interview
"Setup and non-setup objects cannot share a transaction. I move the setup DML into a Future method so it commits separately, and use System.runAs in tests."
Q20You are hitting CPU timeout in a trigger. How do you diagnose and fix it? Advanced
Profile with Limits.getCpuTime() at checkpoints to find the hot section, then eliminate the usual causes: nested loops, SOQL inside loops, string concatenation in loops, and heavy regex. Callout wait time does not count toward CPU.
🔍 Common CPU Hogs
  • String concatenation in a loop: creates a new object each pass — use String.join on a List
  • List.contains() in a loop: O(n) per call — use a Set for O(1)
  • Nested loops over collections: replace the inner loop with a Map lookup
  • Regex on long strings: Pattern matching is expensive; precompile or avoid
🏭 Real World — XYZ Company

XYZ Company had an Opportunity trigger doing List.contains() against a 5,000 item list for each of 200 records — a million comparisons. Converting the list to a Set<Id> dropped CPU time from 9.8 seconds to under 400 milliseconds.

Say This in Interview
"I profile with Limits.getCpuTime at checkpoints to find the hot block, then replace nested loops with Map lookups, List.contains with Set.contains, and string concatenation with String.join."
Q21What causes a HeapException and how do you design around it? Advanced
💾Heap is 6MB synchronous and 12MB asynchronous. It blows when you hold too many records or too much string data in memory at once. The fix is to stream rather than accumulate.
✅ Heap Reduction Techniques
  • Select only the fields you actually use — every extra field costs heap per row
  • Use a SOQL for-loop so records stream in chunks of 200
  • Use Database.getQueryLocator in Batch so Salesforce paginates server-side
  • Null out large collections once you are done with them
  • Mark Visualforce controller variables transient when not needed across postbacks
Say This in Interview
"Heap blows when I hold data instead of streaming it. I select only needed fields, use SOQL for-loops or a QueryLocator so records arrive in chunks, and release large collections as soon as they are consumed."
Q22How do you process 500,000 records reliably when a batch may fail halfway? Advanced
🔁Make the job resumable. Stamp a processing status on each record and have the batch query only unprocessed rows, so a restart picks up exactly where it stopped instead of redoing everything.
global Database.QueryLocator start(Database.BatchableContext bc) { return Database.getQueryLocator( 'SELECT Id FROM Order__c WHERE Batch_Status__c = \'Pending\'' ); } global void execute(Database.BatchableContext bc, List<Order__c> scope) { for(Order__c o : scope) { try { process(o); o.Batch_Status__c = 'Processed'; } catch(Exception e) { o.Batch_Status__c = 'Failed'; o.Batch_Error__c = e.getMessage(); } } update scope; }
Say This in Interview
"I stamp a per-record status so the batch queries only Pending rows. A failure mid-run is then just a restart that resumes where it stopped, and per-record try-catch means one bad row cannot kill the chunk."
Q23What is the 24-hour asynchronous execution limit and how does it change your architecture? Advanced
📅An org gets 250,000 async executions per rolling 24 hours, or licenses multiplied by 200, whichever is greater. This is shared across Future, Queueable, Batch and Scheduled, so enqueueing one job per record will exhaust it.
🏭 Real World — XYZ Company

XYZ Company enqueued one Queueable per order line during a bulk import and consumed 180,000 async executions in a single afternoon, blocking every scheduled job in the org. Refactoring to batch the lines into groups of 200 reduced it to under 1,000 executions.

Say This in Interview
"250,000 async executions per 24 hours are shared across all async types, so I never enqueue one job per record. I group records into chunks and process a batch per job."
Q24How do you make callouts from Batch Apex, and how do you size the batch? Medium
📞Implement Database.AllowsCallouts on the batch class. Size the batch to match the callout limit and the external API rate limit — if you make one callout per record, a scope size of 100 keeps you inside the 100-callout limit per chunk.
⚠ Rules Worth Stating
  • Each execute() chunk gets a fresh 100-callout allowance
  • No DML before a callout in the same execute() invocation
  • Reduce scope size when the external API is slow or rate limited
Say This in Interview
"Implement Database.AllowsCallouts and size the scope to the callout budget. One callout per record means a scope of 100 or less, and callouts must come before any DML in that execute call."
Q25How do you monitor and abort a running async job programmatically? Medium
📊Query AsyncApexJob for status and progress, and call System.abortJob(jobId) to cancel. ExtendedStatus carries the failure message for jobs that ended in error.
AsyncApexJob job = [ SELECT Id, Status, JobItemsProcessed, TotalJobItems, NumberOfErrors, ExtendedStatus FROM AsyncApexJob WHERE ApexClass.Name = 'OrderSyncBatch' ORDER BY CreatedDate DESC LIMIT 1 ]; Decimal pct = job.TotalJobItems > 0 ? (job.JobItemsProcessed / (Decimal)job.TotalJobItems) * 100 : 0; if(job.NumberOfErrors > 50) System.abortJob(job.Id);
Say This in Interview
"AsyncApexJob gives status, items processed versus total for a progress percentage, error count and ExtendedStatus for the failure message. System.abortJob cancels a stuck or misbehaving job."
Q26A user reports a scheduled job silently stopped after last week's release. What happened? Advanced
Deploying a class that is referenced by a scheduled job aborts that job. Salesforce does not re-schedule it automatically, so the job simply stops running with no error and no notification.
✅ Prevention
  • 1
    Add a post-deployment step that queries CronTrigger for expected job names
  • 2
    Re-schedule any missing job from an anonymous Apex script in the release runbook
  • 3
    Store the cron expression in Custom Metadata so schedule changes need no deployment
  • 4
    Add a monitoring job that alerts if an expected schedule is absent
Say This in Interview
"Deployments abort scheduled jobs silently. My release runbook queries CronTrigger afterwards and re-schedules anything missing, with the cron expression stored in Custom Metadata."
🔍
SOQL, Query Performance & Data Architecture
Selectivity, indexes, skew and modelling at large data volumes
Q27–Q40
Q27What makes a SOQL query non-selective and why does it matter at 5 million records? Advanced
A query is non-selective when its filters are not backed by an index or return too large a fraction of the table. Below roughly a million records nobody notices. Above that, the optimiser falls back to a full table scan and the query times out.
🔍 What Kills Selectivity
  • Formula field in WHERE: formula fields cannot be indexed, ever
  • Leading wildcard: LIKE '%term' cannot use an index; 'term%' can
  • Filtering on null: WHERE Field__c = null on a non-indexed field
  • Negative operators: NOT IN and != generally defeat the index
  • Fix: filter on Id, Name, External ID, lookup fields, or a requested custom index
🏭 Real World — XYZ Company

XYZ Company filtered orders on a currency-conversion formula field. At 500,000 rows the report simply timed out. Replacing the formula with a stored field populated by a trigger, then indexing it, took the query from timeout to under one second.

Say This in Interview
"Non-selective means the filter is not index-backed or returns too much of the table, so the optimiser does a full scan. I filter on indexed fields, never on formula fields or leading wildcards, and request custom indexes where needed."
Q28What is data skew and which three types should you watch for? Advanced
Skew is an uneven distribution where one parent, owner, or lookup target has an unusually large number of related records. Past roughly 10,000 children it causes lock contention and slow sharing recalculation.
Skew typeSymptomFix
Ownership skewOne user owns 100k+ records; sharing recalculation crawlsDistribute ownership, or place the user outside the role hierarchy
Parent-child skewUNABLE_TO_LOCK_ROW on concurrent child updatesSpread children across multiple parent records
Lookup skewMany records point to one lookup targetReduce concurrency, stagger updates
🏭 Real World — XYZ Company

XYZ Company had 200,000 contacts parked under a single placeholder account. Any bulk contact update produced lock errors. Distributing them across bucket accounts of 5,000 each eliminated the contention entirely.

Say This in Interview
"Skew is too many children under one parent or owner. It shows up as lock errors and slow sharing recalculation, and the fix is distributing records so no single parent holds more than about 10,000."
Q29How do you resolve UNABLE_TO_LOCK_ROW in a production bulk load? Advanced
🔒Reduce concurrency and make lock acquisition order consistent. Switch Bulk API to serial mode, sort records by parent Id before DML so every transaction locks in the same order, and address any underlying skew.
✅ Resolution Order
  • 1
    Sort records by parent Id before DML so lock order is deterministic
  • 2
    Switch Bulk API jobs to serial mode to remove parallel contention
  • 3
    Reduce batch scope size so each transaction holds fewer locks
  • 4
    Check for ownership or parent-child skew as the root cause
  • 5
    Add retry with backoff for the residual transient failures
Say This in Interview
"Lock errors come from concurrent transactions competing for the same parent rows. I sort by parent Id for consistent lock ordering, run Bulk API in serial mode, shrink batch size, and fix the underlying data skew."
Q30What is the N+1 query problem and how does it appear in Salesforce? Medium
📈N+1 is one query to get parents plus one query per parent to get children. In Apex it appears as SOQL inside a loop. The fix is a single relationship query or a pre-built Map.
// N+1: 1 + 200 queries for(Account a : accounts) { List<Contact> cons = [SELECT Id FROM Contact WHERE AccountId = :a.Id]; } // One query, grouped in memory Map<Id, List<Contact>> byAccount = new Map<Id, List<Contact>>(); for(Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]) { if(!byAccount.containsKey(c.AccountId)) byAccount.put(c.AccountId, new List<Contact>()); byAccount.get(c.AccountId).add(c); }
Say This in Interview
"N+1 is a query per parent inside a loop. I replace it with one bulk query filtered by an Id set, grouped into a Map, or a parent-to-child subquery."
Q31How do you prevent SOQL injection in dynamic queries? Medium
🚨Never concatenate raw user input into a query string. Use bind variables as the primary defence, and String.escapeSingleQuotes only where a value genuinely cannot be bound.
// Vulnerable: attacker enters x' OR Name != ' String q = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\''; // Safe: bind variable String q = 'SELECT Id FROM Account WHERE Name = :userInput'; List<Account> rows = Database.query(q); // Safe fallback where binding is impossible String safe = String.escapeSingleQuotes(userInput);
Say This in Interview
"Bind variables first, because they are type-safe and injection-proof. escapeSingleQuotes is the fallback for values that cannot be bound, such as dynamic field or object names which I validate against a describe allowlist."
Q32What is the difference between WITH SECURITY_ENFORCED, WITH USER_MODE, and Security.stripInaccessible()? Advanced
🔐All three enforce security that Apex bypasses by default, but they differ in scope and failure behaviour. WITH USER_MODE is the most complete because it also enforces record sharing.
EnforcementSECURITY_ENFORCEDUSER_MODEstripInaccessible
Field-level security
Object permissions
Record sharing
Works on DML
On violationThrowsThrowsSilently strips
Say This in Interview
"WITH USER_MODE is the most complete because it enforces sharing as well as FLS. SECURITY_ENFORCED covers FLS on reads and throws. stripInaccessible silently removes inaccessible fields and works on DML too, and tells you what it removed."
Q33What are the depth and count limits on SOQL relationship queries? Medium
🔗Child-to-parent traversal allows 5 levels using dot notation. Parent-to-child subqueries cannot be nested — only one level deep. A single query allows 20 relationship references in total.
// 5 levels up: allowed [SELECT Contact.Account.Owner.Profile.UserRole.Name FROM Contact] // Multiple subqueries: allowed [SELECT Id, (SELECT Id FROM Contacts), (SELECT Id FROM Opportunities) FROM Account] // Nested subquery: NOT allowed
Say This in Interview
"Five levels up with dot notation, one level down with subqueries which cannot nest, and twenty relationship references per query. Beyond that I split into multiple queries and join with Maps in Apex."
Q34How do you query more than 50,000 records? Medium
📦Use Batch Apex with Database.getQueryLocator, which streams up to 50 million records server-side. A synchronous SOQL query is capped at 50,000 rows returned per transaction.
ApproachCeilingContext
Database.query()50,000 rowsSynchronous
SOQL for-loop50,000 rows, streamed in 200sSynchronous, heap-friendly
Database.getQueryLocator50 million rowsBatch start() only
Bulk API / Async SOQLEffectively unlimitedExternal or platform-level
Say This in Interview
"Synchronous SOQL caps at 50,000 rows. For more I use Batch Apex with getQueryLocator, which streams up to 50 million records and paginates server-side so heap stays flat."
Q35What are aggregate queries good for, and what are their limitations? Medium
📊Aggregates push summarisation into the database instead of looping in Apex, saving CPU. Their key limits are that you cannot GROUP BY a formula field and grouped results cap at 2,000 rows.
List<AggregateResult> rows = [ SELECT Region__c region, SUM(Amount) total, COUNT(Id) cnt FROM Opportunity WHERE CloseDate = THIS_FISCAL_QUARTER GROUP BY Region__c HAVING SUM(Amount) > 1000000 ]; for(AggregateResult ar : rows) { String region = (String)ar.get('region'); Decimal total = (Decimal)ar.get('total'); }
Say This in Interview
"Aggregates summarise server-side and save CPU. You access results with ar.get on the alias, you cannot group by a formula field, and grouped results are capped at 2,000 rows."
Q36What is an External ID and why does it matter for integration reliability? Medium
🔑An External ID is a unique indexed field holding an identifier from another system. It lets you upsert without knowing the Salesforce Id, which makes an integration idempotent — a retry updates rather than duplicates.
Order__c o = new Order__c( ERP_Order_No__c = 'ERP-2026-4471', // External ID, unique Amount__c = 250000 ); Database.upsert(o, Order__c.ERP_Order_No__c, false); // Inserts if new, updates if the key already exists
🏭 Real World — XYZ Company

XYZ Company was creating roughly 400 duplicate accounts a week because a retrying nightly job re-inserted rows. Marking the ERP customer number as a unique External ID and switching insert to upsert eliminated duplicates completely.

Say This in Interview
"An External ID is a unique indexed key from the source system. Upserting on it makes the integration idempotent, so a retry updates the existing record instead of creating a duplicate."
Q37Roll-Up Summary field versus an Apex rollup — how do you decide? Medium
🔢Use a Roll-Up Summary whenever the relationship is master-detail and the function is standard — it is free, maintained by the platform, and always accurate. Apex rollups are for lookup relationships or logic the platform cannot express.
⚠ What Apex Rollups Must Handle
  • Insert, update, delete AND undelete contexts
  • Reparenting — when a child moves to a different parent, both parents need recalculation
  • Bulkification — one aggregate query, one parent update
  • Recursion control, since updating the parent may fire other automation
Say This in Interview
"Roll-Up Summary whenever it is master-detail and the function is standard. Apex only for lookups or custom logic, and then I must handle delete, undelete and reparenting, which is exactly where hand-written rollups usually go wrong."
Q38What is a skinny table and when would you request one? Advanced
A skinny table is a Salesforce-internal narrow table containing only your most-queried fields, so queries scan far less data. You request it through Support, and only after custom indexes have failed to solve the problem.
⚠ Constraints
  • Requested via Salesforce Support with performance evidence — not self-service
  • Cannot include fields from related objects
  • Adds a small write overhead since both tables must stay in sync
  • Does not copy to sandboxes automatically — must be re-requested
Say This in Interview
"A skinny table narrows the scanned row width for a hot object. It is a last resort after custom indexes, requested through Support with performance data, and it does not survive a sandbox refresh."
Q39When would you use a Big Object instead of a custom object? Advanced
🗃Big Objects store billions of rows for archival and audit data. The trade-off is severe: no triggers, no standard reports, no delete, and SOQL filtering only on the defined composite index fields.
FeatureCustom ObjectBig Object
CapacityMillionsBillions
Triggers
Standard reports
Delete records❌ Append-only
SOQL filtersAny fieldIndex fields, in order
Say This in Interview
"Big Objects are for billion-row archives such as audit logs. You give up triggers, reports and delete, and you can only filter on the index fields in the order they were defined, so the index design is the whole design."
Q40Reports on an 8 million row object are timing out. Walk through your remediation. Advanced
📈Attack it in three layers: make the filters selective, shrink the working set through archiving, then pre-aggregate so the report reads summaries rather than raw rows.
📋 Remediation Order
  • 1
    Add date-bounded filters so reports never scan full history by default
  • 2
    Replace formula fields used in filters with stored indexed fields
  • 3
    Request custom indexes on the fields actually used in report filters
  • 4
    Archive records older than the retention window to a Big Object
  • 5
    Build a nightly summary object so dashboards read pre-aggregated rows
🏭 Real World — XYZ Company

XYZ Company archived pre-2023 orders to a Big Object and added a nightly summary object holding revenue by region and month. Leadership dashboards went from a 90 second load with frequent timeouts to roughly 3 seconds.

Say This in Interview
"Make filters index-backed, archive cold data out of the working set, and pre-aggregate into a summary object so dashboards read hundreds of rows rather than millions."
💻
Lightning Web Components & Front End
Reactivity, composition, performance and testing at senior level
Q41–Q54
Q41When do you use @wire versus an imperative Apex call? Medium
@wire for read-only data that should refresh reactively when a parameter changes. Imperative for anything user-triggered or that performs DML, because cacheable methods cannot write.
Aspect@wireImperative
TriggerAutomatic on param changeManual call
Client cache✅ Yes❌ No
Can do DML❌ No✅ Yes
Loading state controlLimited✅ Full
Say This in Interview
"@wire for reactive cached reads that should refresh when a property changes. Imperative for user actions and DML where I need explicit loading and error state, followed by refreshApex to invalidate the wire cache."
Q42How do you refresh wired data after a DML operation? Medium
🔄Store the entire provisioned wire result, not just its data, then pass it to refreshApex after the save. Without the stored reference there is nothing to refresh.
import { refreshApex } from '@salesforce/apex'; wiredResult; // keep the whole result @wire(getOrders, { accountId: '$recordId' }) wiredOrders(result) { this.wiredResult = result; // store for refresh if(result.data) this.orders = result.data; } async handleSave() { await saveOrder({ payload: this.draft }); await refreshApex(this.wiredResult); // bust the cache }
Say This in Interview
"I capture the full wire result in a function-style wire handler and pass that object to refreshApex after the save. Storing only result.data leaves nothing refreshable."
Q43Explain the LWC lifecycle hooks and the classic mistake in renderedCallback. Medium
🔃constructor, connectedCallback, render, renderedCallback, then disconnectedCallback on removal. The classic mistake is mutating reactive state inside renderedCallback, which triggers another render and loops infinitely.
connectedCallback() { this.subscription = subscribe(...); // once, on insert } renderedCallback() { if(this.hasRendered) return; // guard is mandatory this.hasRendered = true; initChart(this.template.querySelector('.chart')); } disconnectedCallback() { unsubscribe(this.subscription); // prevent leaks }
Say This in Interview
"connectedCallback for one-time setup, renderedCallback for DOM work behind a guard flag, disconnectedCallback for cleanup. Mutating reactive state in renderedCallback without a guard causes an infinite render loop."
Q44How do unrelated components on the same page communicate? Medium
📻Lightning Message Service. Custom events only travel up the DOM tree, so two siblings with no common ancestor handler cannot use them. LMS also bridges LWC, Aura and Visualforce.
import { publish, subscribe, unsubscribe, MessageContext } from 'lightning/messageService'; import ORDER_SELECTED from '@salesforce/messageChannel/OrderSelected__c'; @wire(MessageContext) messageContext; connectedCallback() { this.sub = subscribe(this.messageContext, ORDER_SELECTED, (msg) => this.handleSelection(msg.orderId)); } disconnectedCallback() { unsubscribe(this.sub); } // always
Say This in Interview
"Custom events bubble up only, so unrelated components use Lightning Message Service with a Message Channel. I always unsubscribe in disconnectedCallback or the subscription leaks."
Q45How do you make a table of 10,000 records usable in LWC? Advanced
Never render them all. Page the data server-side, load incrementally with infinite scroll, debounce filter inputs, and keep getters cheap because they re-evaluate on every render.
✅ Optimisation Checklist
  • lightning-datatable with enable-infinite-loading and an onloadmore handler
  • Cursor-based server paging rather than OFFSET, which caps at 2,000
  • Debounce search inputs by roughly 300ms so typing does not fire per keystroke
  • Set key-field so the framework can diff rows efficiently
  • No heavy computation inside getters — they run on every render
🏭 Real World — XYZ Company

XYZ Company built a work report dashboard that originally loaded all rows at once and froze the browser. Switching to cursor-based paging of 50 rows with infinite scroll made it load in under a second regardless of total volume.

Say This in Interview
"Page server-side and load 50 rows at a time with infinite scroll, debounce filters, set key-field, and keep getters cheap. The goal is a small DOM and few Apex round trips."
Q46Why can't parent CSS style a child component's internals, and what do you do instead? Advanced
🎨Shadow DOM encapsulates each component's markup and styles, so selectors cannot cross the boundary. Pass styling through with CSS custom properties, use :host for the component root, or expose an @api class property.
/* Parent defines a variable that crosses the boundary */ :host { --brand-color: #6C63FF; } /* Child consumes it */ .btn { background: var(--brand-color); } /* Styling the component root itself */ :host { display: block; } :host(.compact) { padding: 4px; }
Say This in Interview
"Shadow DOM blocks CSS from crossing component boundaries. I theme with CSS custom properties which do cross, use :host for the component root, and SLDS styling hooks for base components."
Q47What are slots and how do they change component design? Medium
🧩Slots let a parent inject markup into named positions inside a child's template. They turn a rigid component into a reusable layout shell, which is the difference between a component you use once and one the whole team reuses.
<!-- base-card.html --> <div class="card"> <header><slot name="header">Default title</slot></header> <div class="body"><slot></slot></div> <footer><slot name="footer"></slot></footer> </div> <!-- consumer --> <c-base-card> <span slot="header">Order Summary</span> <p>Body content</p> </c-base-card>
Say This in Interview
"Slots let the parent supply content into named positions, so one layout component serves many contexts. It is composition instead of building a near-identical component for every variation."
Q48How do you test LWC with Jest and mock Apex? Medium
🧪Use sfdx-lwc-jest. Mock the imported Apex method with jest.fn, emit test data through the wire adapter, flush promises, then assert against the shadow DOM. No org required.
jest.mock( '@salesforce/apex/OrderController.getOrders', () => ({ default: jest.fn() }), { virtual: true } ); it('renders rows', async () => { getOrders.mockResolvedValue([{ Id: '1', Name: 'ORD-1' }]); const el = createElement('c-order-list', { is: OrderList }); document.body.appendChild(el); await Promise.resolve(); // flush expect(el.shadowRoot.querySelectorAll('tr').length).toBe(1); });
Say This in Interview
"sfdx-lwc-jest runs client-side tests with no org. I mock the Apex import, resolve test data, flush promises, then query the shadow root to assert what actually rendered."
Q49How do you load a third-party JavaScript library in LWC? Medium
🔒Content Security Policy blocks external script URLs. Upload the library as a Static Resource and load it with loadScript from platformResourceLoader, guarded so it loads only once.
import { loadScript } from 'lightning/platformResourceLoader'; import LEAFLET from '@salesforce/resourceUrl/Leaflet'; renderedCallback() { if(this.libLoaded) return; this.libLoaded = true; loadScript(this, LEAFLET + '/leaflet.js') .then(() => this.initMap()) .catch(e => this.error = e); }
Say This in Interview
"CSP blocks CDN scripts, so the library goes into a Static Resource and loads via loadScript in renderedCallback behind a guard flag. Some libraries also need checking against Lightning Web Security."
Q50When should you use Lightning Data Service instead of custom Apex? Medium
📋Use LDS for single-record CRUD. It enforces FLS automatically, shares a cache across every component on the page, and needs no Apex or test class. Use Apex for multi-record queries and complex logic.
NeedUse
Read or edit one recordLDS: getRecord, updateRecord, record forms
List of records with filtersApex with cacheable=true
Multi-object transactionApex imperative
Aggregation or complex logicApex
Say This in Interview
"LDS for single-record CRUD because it is FLS-safe, shares a page-level cache and needs no Apex or test coverage. Apex once I need multiple records, filters or transactional logic."
Q51How do you surface Apex errors to the user properly in LWC? Easy
🚨Wrap imperative calls in try-catch-finally, read error.body.message, and show it with ShowToastEvent. The finally block is critical so the spinner always clears even on failure.
async handleSave() { this.isLoading = true; try { await saveOrder({ payload: this.draft }); this.toast('Success', 'Order saved', 'success'); } catch(err) { this.toast('Error', err?.body?.message ?? 'Unexpected error', 'error'); } finally { this.isLoading = false; // always clears the spinner } }
Say This in Interview
"try-catch-finally around the imperative call, error.body.message in an error toast, and the spinner reset in finally so a failure never leaves the component stuck loading."
Q52How do you build a form whose fields are driven by configuration rather than hardcoded? Advanced
🔄Store the field definitions in Custom Metadata — API name, type, label, required flag, display order — return them from Apex, and render conditionally per type. Admins then change the form with no deployment.
<template for:each={fields} for:item="f"> <template lwc:if={f.isText}> <lightning-input key={f.api} label={f.label} required={f.required} data-field={f.api} onchange={handleChange}></lightning-input> </template> <template lwc:elseif={f.isPicklist}> <lightning-combobox key={f.api} label={f.label} options={f.options} data-field={f.api} onchange={handleChange}></lightning-combobox> </template> </template>
Say This in Interview
"Field configuration lives in Custom Metadata and the component renders the right input per type. Adding or reordering a field becomes an admin change rather than a release."
Q53What does cacheable=true actually change, and what does it forbid? Medium
💾It enables client-side caching and makes the method usable with @wire. In exchange the method must be read-only — any DML inside it throws at runtime.
✅ Practical Effects
  • Repeat calls with the same parameters are served from the client cache
  • Required for @wire — a non-cacheable method cannot be wired
  • No DML inside a cacheable method
  • refreshApex is the way to invalidate that cache after a write elsewhere
Say This in Interview
"cacheable=true turns on client caching and is required for @wire, but it makes the method read-only. Writes go through separate imperative non-cacheable methods, then refreshApex invalidates the cache."
Q54When would you build an LWC instead of a Screen Flow, and can you combine them? Medium
🤔Flow when an admin should own and change the process. LWC when the interaction needs custom UX, heavy client logic, or performance tuning. You can combine them by embedding an LWC as a Flow screen component.
🏭 Real World — XYZ Company

XYZ Company runs its returns process as a Screen Flow so the support lead can edit the questions, but embeds a custom LWC inside one screen for the product picker, which needed search-as-you-type and image thumbnails that Flow could not deliver.

Say This in Interview
"Flow when an admin should own the process, LWC when the UX or logic demands code. The best answer is often both — a Flow for the journey with an LWC embedded for the one screen that needs custom interaction."
🔗
Integration Architecture & APIs
Patterns, auth flows, resilience and enterprise integration design
Q55–Q68
Q55What are the core Salesforce integration patterns and how do you pick one? Medium
🔗Request-Reply, Fire-and-Forget, Batch Data Sync, Remote Call-In, and Data Virtualization. You choose based on three questions: does the user need the answer now, how much data moves, and how tightly coupled can the systems be?
PatternUse whenTypical implementation
Request-ReplyUser waits for the answerSynchronous callout from LWC or Apex
Fire-and-ForgetResult not needed immediatelyPlatform Event or Queueable
Batch Data SyncHigh volume, tolerant of delayBulk API, scheduled Batch Apex
Remote Call-InExternal system initiatesApex REST or standard API
Data VirtualizationData must stay in sourceSalesforce Connect, External Objects
Say This in Interview
"I pick based on latency tolerance, volume and coupling. If the user waits it is Request-Reply; if not, Fire-and-Forget via Platform Events; high volume goes to Bulk API; and data that legally cannot move gets virtualized."
Q56Which OAuth flow do you use for an unattended server-to-server integration? Advanced
🔑JWT Bearer flow. The external system signs an assertion with a private key and exchanges it for an access token — no user, no consent screen, no stored password.
FlowUser involved?Use for
Authorization Code + PKCE✅ YesWeb and mobile apps
JWT Bearer❌ Pre-authorisedServer-to-server automation
Client Credentials❌ NoMachine-to-machine, integration user
Username-Password✅ YesAvoid — credentials exposed
Say This in Interview
"JWT Bearer for unattended server-to-server, because a certificate-signed assertion replaces stored credentials and needs no consent screen. Client Credentials where there is genuinely no user context at all."
Q57Why are Named Credentials considered mandatory best practice? Medium
🔑They keep the endpoint and credentials out of code, refresh OAuth tokens automatically, and create the remote site entry for you. Apex just calls callout:MyService and never touches a secret.
// With Named Credential: no secret in code req.setEndpoint('callout:ERP_Integration/api/orders'); // Without: token lifecycle becomes your problem req.setEndpoint('https://erp.example.com/api/orders'); req.setHeader('Authorization', 'Bearer ' + getAccessToken());
⚠ Operational Note
  • OAuth tokens do not survive a sandbox refresh — re-authenticate every Named Credential afterwards
  • The newer External Credential model separates auth config from the endpoint definition
Say This in Interview
"Named Credentials keep secrets out of code and refresh tokens automatically, so Apex just calls callout colon the credential name. The only recurring task is re-authenticating after a sandbox refresh."
Q58Design a retry strategy for a flaky external API. Advanced
🔄Classify the error first. Retry 429 and 5xx with exponential backoff up to a small cap; never retry 4xx client errors because they will fail identically forever. Fall back to a dead letter queue after the cap.
StatusMeaningRetry?
400 / 422Bad request or validation❌ Log for human fix
401 / 403Auth failure❌ Re-authenticate, then alert
404Not found❌ Data problem
429Rate limited✅ Honour Retry-After header
500 / 502 / 503Server side✅ Backoff, cap at 3
TimeoutNo response✅ But require idempotency
🏭 Real World — XYZ Company

XYZ Company retries ERP order sync at 1, 4 and 9 seconds, honours Retry-After on 429, and routes anything still failing to a dead letter object. Transient failures now self-heal and only genuine data problems reach a human.

Say This in Interview
"Classify before retrying. 429 and 5xx get exponential backoff capped at three attempts, 4xx goes straight to a human because retrying cannot help, and anything past the cap lands in a dead letter queue."
Q59What is idempotency and why does it matter more than retry logic? Advanced
Idempotency means the same request produces the same result whether it runs once or five times. Without it, retry logic actively causes damage — a timed-out payment that actually succeeded gets charged again.
✅ How to Achieve It
  • Send a stable idempotency key — the Salesforce record Id works well
  • Reuse the same key on retry, never generate a fresh one
  • On the inbound side, upsert on External ID rather than insert
  • Store the external reference back on the record so you can detect prior success
Say This in Interview
"Retry without idempotency creates duplicates. I send a stable idempotency key so the external system recognises a repeat, and inbound I upsert on External ID so a replayed message updates rather than duplicates."
Q60What is the Circuit Breaker pattern and how do you implement it in Salesforce? Advanced
After a threshold of consecutive failures the circuit opens and calls fail fast with a fallback instead of waiting for timeouts. After a cooldown, one probe call decides whether to close it again.
StateBehaviourTransition
ClosedCalls pass through normallyFailure threshold reached, opens
OpenFail fast, return fallbackCooldown elapses, half-opens
Half-openOne probe call allowedSuccess closes, failure re-opens
🏭 Real World — XYZ Company

When the ERP went down for 40 minutes, XYZ Company's sync jobs each waited the full timeout and burned through CPU limits. A circuit breaker storing state in a Custom Setting now opens after five failures and skips calls entirely for 15 minutes.

Say This in Interview
"Circuit breaker tracks consecutive failures in a Custom Setting. Past the threshold it opens and calls fail fast with a fallback instead of burning timeouts, then a single probe after cooldown decides whether to close."
Q61How do you expose Apex as a REST endpoint securely? Medium
🌐Annotate a global class with @RestResource and its static methods with @HttpGet or @HttpPost. Security comes from declaring with sharing, enforcing FLS, validating every input, and scoping the Connected App tightly.
@RestResource(urlMapping='/orders/*') global with sharing class OrderRestService { @HttpGet global static Order__c getOrder() { String id = RestContext.request.requestURI.substringAfterLast('/'); return [SELECT Id, Status__c FROM Order__c WHERE Id = :id WITH USER_MODE]; } }
🎯 Key Points for Interviewer
  • with sharing so the caller's record access applies
  • WITH USER_MODE or stripInaccessible for field-level security
  • Validate every value from requestBody — never trust the caller
  • Minimum OAuth scope and IP restrictions on the Connected App
  • Version the URL path so consumers are not broken by changes
Say This in Interview
"@RestResource on a global class with sharing, WITH USER_MODE on queries, strict input validation, and a Connected App scoped to the minimum permissions with IP restrictions."
Q62Platform Events versus Change Data Capture — when do you use each? Medium
📡CDC publishes automatically whenever a record changes and carries the changed fields — ideal for replication. Platform Events are custom payloads you publish deliberately — ideal for business signals.
AspectChange Data CapturePlatform Events
Published byPlatform, automaticallyYour code, deliberately
PayloadFixed — changed fieldsCustom — you define it
Best forData replication to a warehouseBusiness events like Order Approved
Say This in Interview
"CDC answers what data changed and is right for replication. Platform Events answer what happened in the business and carry a payload I design. Different questions, different tools."
Q63What is the difference between publish immediately and publish after commit for Platform Events? Advanced
Publish immediately fires even if the transaction later rolls back, which can notify subscribers about data that no longer exists. Publish after commit fires only on a successful commit.
🏭 Real World — XYZ Company

XYZ Company originally used publish immediately for order confirmations. A validation failure later in the transaction rolled back the order, but the customer had already received a confirmation email. Switching to publish after commit fixed it permanently.

Say This in Interview
"Publish after commit for anything customer-visible, so a rollback never leaves a notification about data that does not exist. Publish immediately only for audit logging that must survive a rollback."
Q64How do you design a bi-directional sync without creating an infinite loop? Advanced
🔄Record the origin of every change. When the inbound integration writes a record it stamps the source as External, and the outbound trigger skips any record whose last change came from outside.
for(Order__c o : Trigger.new) { // Skip if this update came FROM the external system if(o.Sync_Source__c == 'External') continue; if(!hasRelevantChange(o, Trigger.oldMap.get(o.Id))) continue; toSync.add(o.Id); } if(!toSync.isEmpty()) System.enqueueJob(new ERPSyncJob(toSync));
✅ Also Required
  • Compare old and new values so no-op updates do not trigger a sync
  • Define a conflict rule — usually last-write-wins by timestamp
  • Sync only the fields each system owns, not the whole record
Say This in Interview
"A sync source field records where each change originated, so the outbound trigger skips inbound-caused updates. I also compare old and new values so no-op saves do not bounce, and define a clear conflict rule."
Q65What is the Composite API and when does it save you? Medium
🔀Composite bundles up to 25 REST subrequests into one call and lets later requests reference earlier results. It saves round trips and API allocation when creating related records that need the parent Id.
{ "allOrNone": true, "compositeRequest": [ { "method":"POST", "url":"/services/data/v60.0/sobjects/Account", "referenceId":"newAcct", "body":{ "Name":"XYZ Company" } }, { "method":"POST", "url":"/services/data/v60.0/sobjects/Contact", "referenceId":"newCon", "body":{ "LastName":"Sharma", "AccountId":"@{newAcct.id}" } } ] }
Say This in Interview
"Composite bundles up to 25 subrequests in one call with result chaining via the reference syntax, and allOrNone gives transactional behaviour across them. It turns a parent-then-children sequence into a single round trip."
Q66How do you receive and secure an inbound webhook? Advanced
🔔Expose an Apex REST endpoint, verify the sender's HMAC signature before doing anything, acknowledge fast, and push heavy processing into an async job so the sender never times out.
✅ Webhook Receiver Checklist
  • 1
    Verify the HMAC signature header against a shared secret before parsing
  • 2
    Return 200 quickly — senders usually time out in a few seconds
  • 3
    Store the raw payload, then process it in a Queueable
  • 4
    Deduplicate on the sender's event Id so replays are harmless
  • 5
    Log every received event for reconciliation
Say This in Interview
"Verify the HMAC signature first, acknowledge immediately, then process asynchronously. I deduplicate on the sender's event Id so a replayed webhook cannot create duplicate records."
Q67When would you use Salesforce Connect and External Objects? Advanced
🔗When data must stay in the source system for compliance or volume reasons but users still need to see it. External Objects query live over OData, so nothing is copied into Salesforce.
⚠ Real Constraints
  • No triggers, no roll-up summaries, no standard reporting on External Objects
  • No aggregate SOQL, and relationship support is limited
  • Every page view hits the external API — its latency becomes your latency
  • Right call when the data is huge, cold, or legally cannot be copied
Say This in Interview
"Salesforce Connect virtualizes data that must stay in the source system. Users see it live without storage cost, but every view is an API call and you lose triggers, rollups and standard reporting."
Q68What is a correlation ID and why does it matter in distributed integrations? Advanced
🔗A correlation ID is a unique value generated at the start of a business transaction and passed in every downstream call. It is what makes a failure traceable across Salesforce, middleware and the target system.
🏭 Real World — XYZ Company

XYZ Company stamps every ERP call with a correlation ID header. When the ERP team reports a failure they quote that ID, and both sides find the exact matching log entry in seconds instead of comparing timestamps across systems.

Say This in Interview
"A correlation ID passed as a header through every hop lets both teams find the same transaction in their own logs instantly. Without it, debugging a distributed failure is timestamp archaeology."
🔐
Security, Sharing & Access Control
Record access, FLS enforcement in code, and permission architecture
Q69–Q78
Q69Walk through every way a user can gain access to a record. Medium
🔓Access is additive. Salesforce evaluates every path and grants the highest level found, which is why OWD must be the most restrictive setting you will ever need.
PathGrants
Record ownershipFull access
Role hierarchy above the ownerRead or Edit, if OWD allows
Organisation-Wide DefaultThe baseline for everyone
Sharing rulesRead or Edit to groups or roles
Manual sharingRead or Edit to specific users
Account or Opportunity teamsConfigurable per member
Apex managed sharingRead, Edit or Full via __Share rows
Implicit sharingParent account access grants child access
Say This in Interview
"Sharing is purely additive — Salesforce takes the highest access from any path, so nothing downstream can take access away. That is why OWD has to be the most restrictive baseline."
Q70When do you need Apex managed sharing, and what is the Sharing Reason trap? Advanced
🔐You need it when sharing depends on runtime conditions declarative rules cannot express. The trap: on custom objects you must define a custom Sharing Reason, otherwise the share is treated as Manual and gets deleted when ownership changes.
Project__Share s = new Project__Share(); s.ParentId = projectId; s.UserOrGroupId = memberId; s.AccessLevel = 'Edit'; // Custom reason survives owner change; Manual does not s.RowCause = Schema.Project__Share.RowCause.Team_Member__c; Database.insert(s, false);
Say This in Interview
"Apex managed sharing for logic declarative rules cannot express, and always with a custom Sharing Reason on custom objects — Manual RowCause shares are silently deleted when the record owner changes."
Q71Why does Apex bypass FLS by default and what is your enforcement strategy? Advanced
🔐Apex runs in system mode so platform operations work regardless of the running user. That means every controller you expose is a potential data leak unless you explicitly enforce FLS.
// Reads: enforce sharing and FLS together List<Contact> rows = [SELECT Id, Name, Salary__c FROM Contact WITH USER_MODE]; // DML: strip fields the user cannot write SObjectAccessDecision d = Security.stripInaccessible( AccessType.UPDATABLE, incomingRecords); update d.getRecords(); System.debug(d.getRemovedFields()); // audit what was stripped
Say This in Interview
"Apex runs in system mode, so I enforce explicitly — WITH USER_MODE on queries and stripInaccessible on inbound DML, which also reports which fields it removed for auditing."
Q72What is the modern approach to profiles and permission sets? Medium
🔑Minimal profiles for the baseline and login restrictions, with Permission Set Groups delivering job-role permissions. Muting Permission Sets remove specific permissions inside a group without breaking it apart.
🏭 Real World — XYZ Company

XYZ Company had 14 near-identical profiles that had drifted apart over years. Collapsing them into 3 minimal profiles plus Permission Set Groups per job function made access reviews possible for the first time and cut onboarding setup from an hour to minutes.

Say This in Interview
"Minimal profiles for the baseline, Permission Set Groups per job role, and Muting Permission Sets for exceptions. Profile proliferation is the thing that makes access reviews impossible."
Q73How do you secure an integration user? Medium
🛡A dedicated user, never a shared admin account. Least-privilege permission set, IP range restrictions, certificate-based auth instead of a password, and its own audit trail so integration activity is distinguishable from human activity.
✅ Hardening Checklist
  • Dedicated user per integration so a compromise is contained and traceable
  • Permission set granting only the objects and fields actually needed
  • Login IP ranges locked to the calling system
  • Certificate-based JWT auth rather than a stored password
  • Never grant Modify All Data because it was easier than scoping
Say This in Interview
"A dedicated least-privilege user per integration with IP restrictions and certificate-based auth. Sharing an admin account across integrations means a single compromise exposes everything and nothing is traceable."
Q74What is Shield Platform Encryption and what is the main trade-off? Advanced
🔐Shield encrypts field values at rest with AES-256. The trade-off is functional: probabilistically encrypted fields cannot be used in WHERE clauses, ORDER BY, formula fields, or roll-up summaries.
🏭 Real World — XYZ Company

XYZ Company encrypts customer tax identifiers with Shield but keeps a separate unencrypted status field for reporting filters, because the encrypted field cannot be filtered or sorted in reports.

Say This in Interview
"Shield encrypts at rest, but encrypted fields lose filtering, sorting, formulas and rollups. I plan around it by keeping a separate non-sensitive field for anything that must be reportable."
Q75How do you prevent XSS in Visualforce and LWC? Medium
🛡Rely on default escaping. Never set escape="false" on user-supplied content in Visualforce, and never inject unsanitised HTML via lwc:dom="manual" in LWC.
✅ Rules
  • LWC escapes text bindings by default — keep it that way
  • escape="false" on any field a user can edit is a live XSS hole
  • Use JSENCODE and HTMLENCODE when writing values into script or markup contexts
  • Lightning Web Security and CSP add defence in depth, not a substitute
Say This in Interview
"Default escaping handles most of it. The risk comes from deliberately disabling it — escape false in Visualforce or manual DOM injection in LWC — on content a user controls."
Q76How do you troubleshoot Insufficient Privileges when the profile looks correct? Advanced
🔍Work down the layers in order, because the message is identical for six different causes. Object permission, then field permission, then record sharing, then Apex class access, then the action or layout, then dependent system permissions.
🔍 Diagnostic Order
  • 1
    Object-level CRUD on the profile and every assigned permission set
  • 2
    Field-level security on any required field in the operation
  • 3
    Record sharing — who owns it, what is the OWD, do the rules apply
  • 4
    Apex class access for AuraEnabled controllers
  • 5
    Login As the user to reproduce, with debug logs enabled
Say This in Interview
"The message is the same for six causes, so I check layers in order — object, field, record sharing, Apex class access — and the fastest route is Login As the user with debug logs running."
Q77What does Event Monitoring give you that Setup Audit Trail does not? Medium
📊Audit Trail records configuration changes and is free. Event Monitoring records user behaviour — logins, report exports, record views, API calls — and requires Shield. One answers who changed the org, the other answers what people did with the data.
Say This in Interview
"Audit Trail is free and tracks setup changes. Event Monitoring needs Shield and tracks user behaviour like mass exports and record views, which is what you need for insider-threat detection."
Q78A community guest user can see records they should not. Where do you look? Advanced
Almost always a without-sharing Apex controller or a sharing rule that grants access to the guest user profile. Guest user access is the single most common Salesforce security incident.
🔍 Check in This Order
  • 🔍Any Apex controller the community calls that is without sharing
  • 🔍Sharing rules or manual shares granting access to the guest user
  • 🔍Guest user profile object permissions — should be read-only and minimal
  • 🔍Missing FLS enforcement in controllers returning sObjects to the front end
  • Enable the guest user security policy that forces all guest access through sharing rules
Say This in Interview
"Guest user exposure is almost always a without-sharing controller or an over-broad sharing rule. I enforce with sharing plus USER_MODE on every community-facing controller and keep the guest profile read-only and minimal."
🧪
Testing & Code Quality
Meaningful tests, mocking, and what coverage does not tell you
Q79–Q86
Q79Why is 75% code coverage a poor measure of test quality? Medium
Coverage only proves a line executed, not that anything was verified. Delete every assertion from a test suite and coverage stays identical while the tests now verify nothing at all.
✅ What Actually Signals Quality
  • Every test method contains meaningful assertions on outcomes
  • Negative tests that assert the correct error is thrown
  • Bulk tests with 200 records, not a single happy-path record
  • Boundary cases — nulls, empty lists, limit edges
Say This in Interview
"Coverage proves a line ran, not that it was checked. Strip every assertion and coverage is unchanged. I judge a suite by assertions, negative cases and whether it tests 200 records rather than one."
Q80What do Test.startTest() and Test.stopTest() actually do? Medium
They give the code between them a fresh set of governor limits, and force queued async work to complete at stopTest so you can assert on its results.
Test.startTest(); // fresh limits from here Database.executeBatch(new OrderBatch(), 200); Test.stopTest(); // batch finishes before this returns // Now safe to assert on batch results System.assertEquals(50, [SELECT COUNT() FROM Order__c WHERE Status__c = 'Processed']);
Say This in Interview
"They reset governor limits so setup DML does not consume the budget I am testing, and force async jobs to complete at stopTest so I can assert on the results."
Q81Why is SeeAllData=true an anti-pattern? Medium
It couples the test to whatever data happens to exist in that org. The test passes in your sandbox, fails in a scratch org, and fails in production validation when someone deletes the record it silently depended on.
Say This in Interview
"SeeAllData makes tests depend on org data, so they break in scratch orgs and in production validation. I use a test data factory and @testSetup instead, with Test.getStandardPricebookId for the genuine exception."
Q82How does a test data factory improve maintainability? Medium
🏭It centralises record creation with sensible defaults, so a test specifies only the fields it actually cares about. When a new required field appears, you change one factory instead of fifty test classes.
@isTest public class TestDataFactory { public static Account account(Map<String,Object> overrides) { Account a = new Account(Name='Test Co', Industry='Manufacturing'); for(String f : overrides.keySet()) a.put(f, overrides.get(f)); return a; } } // Test states only what matters to it Account a = TestDataFactory.account(new Map<String,Object>{'Industry'=>'Pharma'});
Say This in Interview
"A factory gives defaults so each test declares only what it cares about. When a new required field lands, I update one factory method instead of every test class that touches that object."
Q83How do you test code that makes HTTP callouts, including failure paths? Medium
🧪Implement HttpCalloutMock and register it with Test.setMock. Crucially, test the failure paths too — mock a 500 and a timeout to prove your retry and fallback logic actually works.
public class FailingMock implements HttpCalloutMock { public HttpResponse respond(HttpRequest req) { HttpResponse r = new HttpResponse(); r.setStatusCode(500); r.setBody('{"error":"internal"}'); return r; } } Test.setMock(HttpCalloutMock.class, new FailingMock()); // Assert the retry ran and a DLQ record was created
Say This in Interview
"HttpCalloutMock with Test.setMock, and I always mock failures as well as success. A test suite that only covers the happy path leaves the retry and fallback logic completely unverified."
Q84What is the Apex Stub API and when do you reach for it? Advanced
🧩Test.createStub with System.StubProvider generates a mock implementation at runtime, letting you test a class in true isolation with no DML and no callouts. It pairs naturally with dependency injection.
Say This in Interview
"The Stub API creates runtime mocks so I can unit test a class without touching the database. It only works if the class takes its dependencies as injected interfaces, so it pushes you toward better design."
Q85A test passes in sandbox but fails in production validation. What are the usual causes? Advanced
🔍Environment assumptions. The test depends on something present in sandbox but absent or different in production.
CauseFix
Hardcoded record type or profile IdQuery by DeveloperName at runtime
SeeAllData dependencyCreate all data in @testSetup
Production validation rule the sandbox lacksMake test data satisfy production rules
Different Custom Metadata valuesDo not branch on org-specific config in tests
Required field added in production onlySync metadata; use a factory
Say This in Interview
"It is nearly always an environment assumption — a hardcoded Id, a SeeAllData dependency, or a production validation rule the sandbox does not have. I run the full suite in a Full sandbox before any production release."
Q86How do you test code whose behaviour depends on the running user? Medium
👤Create a test user with the specific profile and run the logic inside System.runAs. It is the only reliable way to verify sharing, FLS and profile-dependent branches, and it also sidesteps mixed DML.
User u = TestDataFactory.userWithProfile('Sales Rep'); insert u; System.runAs(u) { // Runs with that user's sharing and FLS List<Opportunity> visible = OpportunityService.getMine(); System.assertEquals(0, visible.size(), 'Rep should not see other territory opportunities'); }
Say This in Interview
"System.runAs with a purpose-built test user is the only way to genuinely verify sharing and FLS. Testing security as an admin proves nothing, because an admin sees everything anyway."
🚀
DevOps, Deployment & Release Management
SFDX, packaging, environments and safe production releases
Q87–Q94
Q87How do you design a branching and release strategy for a Salesforce team? Advanced
🌐Map branches to environments. Feature branches per developer sandbox, merged into an integration branch tied to a QA sandbox, then a release branch to UAT and production, with automated test runs on every pull request.
📋 Branch to Environment Mapping
  • 1
    feature/* branches map to developer sandboxes or scratch orgs
  • 2
    Pull request triggers a validation-only deploy plus test run in CI
  • 3
    develop branch deploys automatically to the integration sandbox
  • 4
    release branch deploys to UAT for business sign-off
  • 5
    main deploys to production behind an approval gate, tagged for rollback
Say This in Interview
"Branches map one to one with environments, every pull request runs a validation deploy and the test suite, and production sits behind an approval gate with a tagged release so rollback is a known state rather than a scramble."
Q88How do you delete metadata from production, and why can't a change set do it? Medium
🗑You need destructiveChanges.xml deployed through SFDX or the Metadata API. Change sets can only add and update — they have no delete capability at all.
<?xml version="1.0" encoding="UTF-8"?> <Package xmlns="http://soap.sforce.com/2006/04/metadata"> <types> <members>LegacyOrderService</members> <name>ApexClass</name> </types> <version>60.0</version> </Package> # Post-destructive so replacements deploy first sf project deploy start --manifest package.xml \ --post-destructive-changes destructiveChanges.xml
⚠ Before You Delete
  • Remove every reference first — Apex, Flows, reports, layouts, formulas
  • Use post-destructive so the replacement deploys before the old is removed
  • Deleted custom fields sit in the recycle bin for 15 days and still count toward field limits
Say This in Interview
"Change sets cannot delete, so removal needs destructiveChanges.xml via SFDX. I use post-destructive so the replacement deploys first, and I clear every reference beforehand or the delete fails."
Q89What is Quick Deploy and how does it change your release window? Medium
A successful validation within the last 10 days lets you deploy using those cached test results, turning a 45-minute deployment into seconds. That is what makes a short production maintenance window realistic.
🏭 Real World — XYZ Company

XYZ Company validates the release package overnight when the org is quiet, then runs Quick Deploy at 7am. The production change window went from 50 minutes of running tests to under two minutes.

Say This in Interview
"Validate off-hours to warm the cache, then Quick Deploy inside the maintenance window. It turns a 45-minute test run into a near-instant deployment, which is what makes short change windows achievable."
Q90Compare unlocked, managed and unmanaged packages. Medium
📦Unlocked packages modularise an internal org with real versioning. Managed packages protect intellectual property for AppExchange distribution. Unmanaged is a one-time copy with no upgrade path.
TypeVersionedEditable after installUse for
UnlockedInternal modular development
ManagedAppExchange distribution
UnmanagedOne-time templates
Say This in Interview
"Unlocked packages let separate teams version and release their domain independently inside one org. Managed is for AppExchange where code must stay protected. Unmanaged has no upgrade path so it is templates only."
Q91What is metadata drift and how do you detect and prevent it? Advanced
📈Drift is when someone changes production directly and version control no longer reflects reality. The next deployment then either overwrites their fix or fails on an unexpected conflict.
✅ Detect and Prevent
  • 🔍Run a scheduled retrieve and diff against the repository to surface untracked changes
  • 🔍Query LastModifiedById on ApexClass and Flow to find direct production edits
  • Treat production as read-only for configuration — changes go through the pipeline
  • Where emergency hotfixes are unavoidable, require same-day back-merge to the repo
Say This in Interview
"Drift means version control no longer matches production, so the next release either overwrites a hotfix or fails unexpectedly. I run a scheduled retrieve-and-diff and require every emergency fix to be back-merged the same day."
Q92What breaks after a sandbox refresh, and what is on your runbook? Advanced
A refreshed sandbox is a new org with a new Id, so OAuth tokens are invalidated, scheduled jobs are gone, and Custom Settings arrive pointing at production endpoints.
📋 Post-Refresh Runbook
  • 1
    Set email deliverability to System Only before anything else runs
  • 2
    Re-authenticate every Named Credential using OAuth
  • 3
    Repoint Custom Settings and Custom Metadata endpoints to sandbox systems
  • 4
    Re-schedule all scheduled jobs
  • 5
    Deactivate or scramble production user email addresses
  • 6
    Re-request any skinny tables and custom indexes if performance testing
🏭 Real World — XYZ Company

XYZ Company once refreshed a Full sandbox and a scheduled job emailed 3,000 real customers from the sandbox copy. Email deliverability is now step one of the runbook and is set before any other post-refresh task.

Say This in Interview
"Email deliverability to System Only first, then re-authenticate Named Credentials, repoint integration endpoints to sandbox systems, and re-schedule jobs. Skipping step one is how sandboxes email real customers."
Q93How do you release a risky feature safely without a blue-green environment? Advanced
🔵Feature flags. Deploy the code inactive behind a Custom Metadata switch or a permission set, enable it for a pilot group, then widen it. Rollback becomes flipping a flag rather than an emergency deployment.
if(FeatureFlag.isEnabled('NewPricingEngine')) { return new PricingEngineV2().calculate(quote); } return new PricingEngineV1().calculate(quote); // Rollback = flip Custom Metadata, no deployment needed
Say This in Interview
"Salesforce has one production org, so I approximate blue-green with feature flags in Custom Metadata. Code ships inactive, a pilot group enables it, and rollback is flipping a flag instead of an emergency deploy."
Q94How do you debug a production issue you cannot reproduce anywhere else? Advanced
🔍Persistent logging beats debug logs, because debug logs are capped, truncate the interesting part, and require you to catch the issue live. A custom error log object captures every exception with context permanently.
🔍 Approach
  • 1
    Set a trace flag on the affected user to capture the next occurrence
  • 2
    Add a catch that writes exception type, message, stack trace and record Id to a log object
  • 3
    Look for patterns — same profile, same time window, same data shape
  • 4
    Check AsyncApexJob ExtendedStatus if any async work is involved
  • 5
    Never log PII into the error object
Say This in Interview
"Debug logs truncate and require catching it live, so I write exceptions to a custom log object with full context. Patterns by profile, time or data shape usually reveal the cause faster than reading logs line by line."
🏗
Architecture & Real-World Scenarios
Judgement questions that separate senior developers from mid-level
Q95–Q100
Q95How do you decide between Flow and Apex for new automation? Medium
🤔Declarative first, Flow next, Apex when the requirement genuinely exceeds Flow. The real question is who will maintain this in two years — if an admin should own it, it belongs in Flow.
SignalChoose
An admin should be able to change itFlow
Callouts or complex error handling requiredApex
Complex collection manipulationApex
Needs mock-based unit testingApex
Flow diagram would exceed roughly 30 elementsApex, or invocable Apex from Flow
Say This in Interview
"Declarative first, then Flow, then Apex. The deciding question is who maintains it in two years, and a hybrid where Flow calls invocable Apex often gives admin ownership with the logic properly tested."
Q96Explain the order of execution and why senior developers care about it. Advanced
📋Knowing the order is how you explain why a validation rule fired before your trigger logic ran, or why a before-save Flow overwrote a value your trigger just set.
📋 Save Order (Simplified)
  • 1
    Load original record, apply new values, run system validation
  • 2
    Before-save Record-Triggered Flows
  • 3
    Before Apex triggers
  • 4
    Custom validation rules and duplicate rules
  • 5
    Save to database, not yet committed
  • 6
    After Apex triggers
  • 7
    Assignment, auto-response and escalation rules
  • 8
    After-save Flows, then roll-up summary recalculation
  • 9
    Commit, then emails send and platform events publish
Say This in Interview
"The detail that catches people out is that before-save Flows run before before triggers, so a Flow can overwrite what a trigger is about to set. Knowing the sequence is how you debug automation fighting itself."
Q97How would you design multi-currency reporting that stays accurate over time? Advanced
💰Enable Advanced Currency Management so historical records convert at the rate applicable on their date, not today's rate. Store a converted amount on the record for anything that needs to be rolled up or filtered.
🏭 Real World — XYZ Company

XYZ Company reports in INR but sells in USD and EUR. Without dated exchange rates, last year's revenue changed every time the rate moved, so year-over-year comparisons were meaningless. Dated rates plus a stored converted amount on each order fixed both reporting and rollups.

Say This in Interview
"Dated exchange rates through Advanced Currency Management, so a closed deal converts at its close-date rate rather than today's. I also store the converted amount on the record because rollups and filters cannot use dynamic conversion."
Q98A nightly integration creates duplicate accounts. Diagnose and fix. Advanced
🔄The integration has no reliable matching key. Add a unique External ID holding the source system identifier and switch the operation from insert to upsert, which makes the whole job idempotent.
✅ Full Remediation
  • 1
    Add a unique External ID field for the source system key
  • 2
    Backfill it on existing records before switching the job
  • 3
    Change the integration from insert to upsert on that field
  • 4
    Add duplicate rules as a secondary safety net
  • 5
    Run a one-off merge job to clean up the duplicates already created
Say This in Interview
"Duplicates mean no reliable matching key. A unique External ID plus upsert makes the job idempotent so retries update rather than duplicate, then duplicate rules catch anything else and a merge job cleans up history."
Q99How do you approach paying down technical debt in a legacy org? Advanced
🛠Inventory first, add characterisation tests before touching anything, then consolidate one object at a time. Refactoring without tests in an org you did not build is how outages happen.
📋 Sequence
  • 1
    Inventory every trigger, Flow, workflow rule and process per object
  • 2
    Rank by risk and business impact, not by how annoying the code is
  • 3
    Write characterisation tests that lock in current behaviour first
  • 4
    Consolidate one object at a time, releasing between each
  • 5
    Migrate Workflow Rules and Process Builder to Flow as you go
Say This in Interview
"Inventory, then characterisation tests that lock in current behaviour, then consolidate object by object with a release between each. Big-bang refactors in an org you did not build are how you cause an outage."
Q100As a senior developer, how do you run code reviews and grow junior developers? Advanced
🎯Review against a consistent checklist and explain the reasoning rather than just the fix. A review that teaches a principle prevents the next twenty instances; a review that only corrects syntax creates dependency.
✅ Review Checklist
  • No SOQL or DML inside loops; collections used for lookups
  • Sharing and FLS enforced on anything user-facing
  • Tests assert behaviour, cover negatives, and run 200 records
  • No hardcoded Ids, endpoints or credentials
  • Naming and structure a stranger could follow in a year
🏭 Real World — XYZ Company

XYZ Company introduced a five-point pull request checklist covering bulkification, security, assertions, hardcoded values and naming. Review comments dropped sharply within two months because juniors internalised the checklist and self-corrected before submitting.

Say This in Interview
"A consistent checklist plus explaining the why, not just the fix. Design discussion before coding prevents far more rework than any review comment, and a good review should make the next twenty reviews shorter."
SF
By SF Interview Pro
Salesforce Interview Prep Team
Practical Q&A by working Salesforce professionals · LWC, Apex, Data Cloud & AI
About Us ↗
☕ 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)
UPI QR Code to support sfinterviewpro
Pay by QR
GPay · PhonePe · Paytm · BHIM
🌎 International
PayPal QR Code to support sfinterviewpro Pay via PayPal ↗
Scan or tap to pay