75 Salesforce Interview Questions Asked at Capgemini (2026) — L1 & L2 Rounds | SF Interview Pro

📅  Capgemini
🏢 Company-Specific Interview Prep 2026
Salesforce Interview Questions
Asked at Capgemini 2026
58 real Salesforce interview questions asked at Capgemini across L1 and L2 technical rounds — Apex, Triggers, LWC, Flows, Integration, Security & Sharing, fully explained with code and real-world examples. 100% Free, no signup.
58
Questions
9
Topics
L1+L2
Rounds Covered
100%
Free
Preparing for a Capgemini Salesforce interview? This guide compiles 58 real questions gathered from actual L1 and L2 candidate interviews at Capgemini, covering record sharing & security, Apex triggers, governor limits, Lightning Web Components, Flows, and integration patterns. Every answer follows our 7-step format — direct answer, why it works, code example, real-world scenario, key interview points, and a one-line answer you can say directly in the room. If you're prepping for 6+ years of experience level interviews, also check our 100 Senior Developer Questions guide.

📋 Capgemini Interview Pattern (L1 / L2)

L1
Technical Round 1 (45-60 mins)
Core Apex (triggers, governor limits, async), LWC fundamentals (lifecycle hooks, decorators, communication), field-level security, sharing model. Mix of theory + live coding.
L2
Technical Round 2 (45-60 mins)
Deeper LWC (pub-sub via Message Channel, toast notifications, child-to-parent), integration patterns, project-specific scenario questions, code-writing exercises.
SF
Scenario / Managerial Round
Real project scenarios — data security edge cases, deployment process, dataloader rollback situations, sharing troubleshooting. Tests hands-on experience, not just theory.

🔒 Section 1 — Security & Sharing Model

Q1–Q12 · Most Asked Topic at Capgemini — Every Round

Q1
How many ways can you make a field mandatory in Salesforce?
✅ Direct Answer
Five ways: 1) Field-level "Required" checkbox at field definition (org-wide, all page layouts, all entry points including API). 2) Page Layout "Required" attribute (layout-specific only, doesn't block API inserts). 3) Validation Rule using ISBLANK() or ISNULL() (most flexible — supports conditional logic). 4) Required attribute on a Flow Screen input component. 5) Apex trigger using addError() (last resort, for logic too complex for a Validation Rule).
💡 Why?
Each method enforces at a completely different layer of the stack, and interviewers ask this specifically to see if you understand WHERE each one applies. Field-level required is the strictest — it blocks the UI, the REST/SOAP API, and Data Loader simultaneously, because it's baked into the field's metadata definition. Page layout required only affects users editing through that specific layout in the UI — a Data Loader bulk insert completely bypasses it, which is a common gotcha in data migration projects. Validation Rules sit in between: they apply everywhere DML happens (UI, API, Flow, Apex) but let you make the requirement conditional — e.g., "Website is required only when Industry = Technology" — something a simple field-level checkbox can't express.
🌍 Real World Example
XYZ Company had a field-level required checkbox on Account.Phone — this blocked every insert path uniformly, including nightly Data Loader jobs from their ERP, which sometimes legitimately didn't have a phone number yet. Switching to a Validation Rule (require Phone only when Type = 'Customer') let the ERP sync continue for prospect records while still enforcing the rule for active customers — this is exactly the kind of conditional nuance that makes Validation Rules the most commonly used method in real projects.
🔑 Key Points for Interviewer
  • Field-level required: strictest, applies everywhere including API and Data Loader
  • Page layout required: UI only, bypassed by Data Loader/API — common data quality gap
  • Validation Rule: conditional required logic, most commonly used in production orgs
  • Flow Screen required: only within that guided process, doesn't affect direct record edits
  • Trigger addError(): only for complex logic (e.g., requiring a related record to exist) Validation Rules can't express
🎤 One-Line Answer
"Five ways: field-level required (strictest, all entry points), page layout required (UI only), Validation Rule (conditional logic, most common), Flow Screen required, and Apex trigger addError() for complex cases."
Q2
Can we make a field mandatory via a trigger? How?
✅ Direct Answer
Yes — in a before insert/before update trigger, check if the field is null/blank and call fieldName.addError('message') on the specific SObject field. This blocks the save with a field-level error, exactly like a Validation Rule, but allows more complex conditional logic (e.g., checking related records via SOQL) that a declarative Validation Rule formula can't express.
💡 Why?
Validation Rules operate purely on formula logic evaluated against the current record and its direct parent relationships — they can't run a SOQL query. If your "required" logic depends on counting related child records, checking a value from an unrelated object, or complex multi-step conditions, only Apex can express that. addError() called on a specific field (rather than the record) is important because it highlights that exact field in the UI with a red border — the same UX as a native required field or Validation Rule error.
🌍 Real World Example
trigger AccountTrigger on Account (before insert, before update) { for(Account acc : Trigger.new) { if(acc.Industry == 'Technology' && String.isBlank(acc.Website)) { acc.Website.addError('Website is required for Technology accounts'); } } }
XYZ Company needed "Contract_Number__c is required only if there is at least one Closed Won Opportunity on this Account" — counting related Opportunities is impossible in a Validation Rule formula, so this had to move to a before-update trigger that queries related Opportunities and calls addError() conditionally.
🔑 Key Points for Interviewer
  • Use before trigger — blocks before DML commits, no wasted DML operation
  • addError() on the field highlights that exact field in UI (better UX than a record-level error)
  • Prefer Validation Rules when logic is simple — triggers only when it needs SOQL/complex logic
  • Trigger-based mandatory checks also apply uniformly to API/Data Loader inserts, just like Validation Rules
🎤 One-Line Answer
"Yes — in a before trigger, check if the field is blank and call field.addError() — use this only when the required-logic needs SOQL or complexity beyond what a Validation Rule can express."
Q3
What are the different access levels (record access) in Salesforce?
✅ Direct Answer
Private, Public Read Only, Public Read/Write, Public Read/Write/Transfer (available on Leads and Cases only), and Controlled by Parent — set as Organization-Wide Defaults (OWD) per object. These form the restrictive baseline and are then extended — never restricted further — via Role Hierarchy, Sharing Rules, and Manual Sharing.
💡 Why?
OWD is the single most important setting in Salesforce's security model because everything downstream builds on top of it — you always design OWD as the MOST restrictive setting the business genuinely needs, then open up access with more targeted mechanisms. Getting this backward (starting permissive and trying to restrict later) is a classic implementation mistake, because sharing mechanisms in Salesforce can only ADD access on top of OWD, never subtract from it.
🌍 Real World Example
XYZ Company set Opportunity OWD to Private because sales reps shouldn't see each other's deals by default. Sales Managers then get visibility through Role Hierarchy (they sit above their reps). A cross-functional "Enterprise Deals" sharing rule opens specific high-value Opportunities to the Legal and Finance teams. If OWD had instead been set to Public Read/Write from the start, none of that granular control would have been achievable — everyone would see everything and there'd be no way to dial it back without a painful re-architecture.
🔑 Key Points for Interviewer
  • Private: only owner + role hierarchy above + admins with "View/Modify All"
  • Public Read Only: everyone can view, only owner (+ hierarchy) can edit
  • Public Read/Write: everyone can view and edit
  • Controlled by Parent: child inherits access from Master-Detail parent — no independent OWD setting
  • OWD is the baseline — every other sharing mechanism (roles, rules, manual) only adds access on top
🎤 One-Line Answer
"OWD access levels: Private, Public Read Only, Public Read/Write, Public Read/Write/Transfer, and Controlled by Parent — set as the restrictive baseline, then opened up via roles, sharing rules, and manual sharing."
Q4
What is the difference between a Queue and a Public Group?
✅ Direct Answer
Queue: owns records — used for unassigned Leads, Cases, or custom objects; queue members can accept/take ownership from a shared pool; appears as a valid value in the OwnerId field. Public Group: does NOT own records — only used for sharing rules, list view visibility, and email distribution; can contain individual users, roles, and other groups.
💡 Why?
The confusion between these two comes from the fact that both are "collections of users" conceptually — but Salesforce treats them completely differently at the database level. A Queue is registered as a valid OwnerId target, meaning it participates in ownership-based sharing rules and Role Hierarchy just like a User would. A Public Group can never own a record — it exists purely as a reference target for criteria-based or owner-based sharing rules, and for controlling who sees which list views or receives which report emails.
🌍 Real World Example
XYZ Company routes all inbound web-form Leads into an "Unassigned Web Leads" Queue via an Assignment Rule — SDRs monitor the queue's list view and self-assign Leads as they work through them, and every queue member gets an email notification the instant a new Lead lands. Separately, they use a "West Region Sales" Public Group purely to share all Opportunities tagged Region = West with that team via a criteria-based sharing rule — the Public Group never owns a single record; it's purely a sharing target.
🔑 Key Points for Interviewer
  • Queue: record ownership, works with Assignment Rules for auto-routing
  • Public Group: sharing/visibility only, never owns records
  • Queue members get email notification on new queue records automatically
  • SOQL: WHERE OwnerId = :queueId finds queue-owned records — same pattern as querying by User Id
🎤 One-Line Answer
"Queues own records (unassigned Leads/Cases pool) — Public Groups don't own anything, they're used purely for sharing rules and list view visibility."
Q5
What are the different ways to share a record in Salesforce?
✅ Direct Answer
1) Organization-Wide Defaults (baseline). 2) Role Hierarchy (manager sees subordinate's records). 3) Sharing Rules — owner-based or criteria-based (extend access to groups/roles). 4) Manual Sharing (record owner shares individually via UI). 5) Apex Managed Sharing (programmatic, via Share objects, persists through ownership change). 6) Territory Management (for account-based coverage models where multiple reps cover the same account).
💡 Why?
Interviewers ask for "all the ways" specifically to see whether you know the full toolbox, because real projects almost never rely on just one mechanism — a mature Salesforce org typically layers 3-4 of these simultaneously. Knowing which one to reach for matters: declarative sharing rules cover 90% of static business requirements; Apex Managed Sharing is reserved for genuinely dynamic conditions (like "share with everyone who attended this meeting") that declarative tools structurally cannot express.
🌍 Real World Example
XYZ Company's Deal record sharing stack: OWD = Private (baseline). Role Hierarchy gives regional managers visibility into their reps' deals. A criteria-based Sharing Rule opens deals tagged "Strategic Account" to the executive team. Manual Sharing lets a rep share one specific confidential deal with outside counsel. And Apex Managed Sharing programmatically shares a deal with every attendee of a related Meeting custom object — a condition no declarative rule could express, since "meeting attendee" isn't a fixed role or group.
🔑 Key Points for Interviewer
  • All mechanisms only ADD access — never restrict beyond OWD
  • Criteria-based sharing rules can't use formula fields as criteria (see Q6)
  • Manual sharing doesn't persist through ownership change; Apex Managed Sharing does
  • Apex Managed Sharing requires a custom Sharing Reason (RowCause) — using the default "Manual" reason gets deleted on owner change
🎤 One-Line Answer
"OWD → Role Hierarchy → Sharing Rules (owner/criteria-based) → Manual Sharing → Apex Managed Sharing → Territory Management — each layer only extends access, never restricts it."
Q6
Can we use a formula field in a criteria-based sharing rule?
✅ Direct Answer
No — criteria-based sharing rules can only reference stored (non-formula) fields on the object. Formula fields are calculated at query time and aren't indexed in a way the sharing engine can evaluate for recalculation. Workaround: use a Flow (or Apex) to write the formula's calculated result into a real, stored helper field on record save, then use THAT field in the sharing rule criteria.
💡 Why?
Sharing recalculation is a background, event-driven process — Salesforce watches for changes to specific stored fields and re-evaluates which records need re-sharing. A formula field's value changes implicitly whenever ANY of its dependency fields change, sometimes on completely unrelated objects (cross-object formulas) — there's no reliable trigger point for Salesforce's sharing engine to "notice" the formula changed and re-run the criteria. Storing the value explicitly gives the platform a concrete field to watch.
🌍 Real World Example
XYZ Company wanted to share Accounts where a cross-object formula (Owner.Region__c) equaled "APAC" with a regional group. That formula field couldn't be used directly in the sharing rule. Fix: added a workflow field update that copies the formula's calculated value into a stored Region_Snapshot__c field whenever the Account is saved, then built the criteria-based sharing rule against Region_Snapshot__c instead.
🎤 One-Line Answer
"No — formula fields can't be used in criteria-based sharing rules. Workaround: use a Flow to copy the formula's value into a stored helper field, then reference that field in the sharing rule."
Q7
SCENARIO: Users A, B, C, D all have the same profile. You need to remove Amount edit access from only User A. How?
✅ Direct Answer
Don't touch the shared profile (it would affect B, C, D too, since all four users are assigned to it). Instead, flip the logic: set the Profile's field-level security on Amount to Read Only for everyone (the most restrictive baseline), then create a Permission Set granting Edit access on Amount and assign it to B, C, and D only — never to A.
💡 Why?
Salesforce security is purely additive — a user's effective access is the UNION of everything granted across their Profile plus all assigned Permission Sets. There is no mechanism to subtract access with a standard Permission Set (only Muting Permission Sets inside a Permission Set Group can subtract, and only from that group's own grants). So whenever you need "everyone except one person" access, the pattern is always: set the shared baseline (Profile) to the MOST restrictive level any of the users needs, then grant the additional access via Permission Set to the majority who need it.
🌍 Real World Example
XYZ Company had this exact situation on their "Sales Rep" profile — four reps shared it, but one rep (under a compliance review) needed to lose Amount edit rights temporarily without affecting the other three or requiring a brand-new profile. They set Amount to Read Only on the Sales Rep profile, then created an "Amount_Edit_Access" Permission Set and assigned it to the three unaffected reps. When the compliance review closed, they simply assigned the same Permission Set to the fourth rep — no profile changes, no downtime for the other three.
🔑 Key Points for Interviewer
  • Permission Sets are purely additive — can never remove access from a profile
  • Correct pattern: restrictive profile baseline + Permission Set to grant extra access to the majority
  • Muting Permission Sets (within a Permission Set Group) CAN subtract specific permissions — an alternative if using PSGs
  • Never modify a shared profile just for one user's exception — it breaks everyone else sharing that profile
🎤 One-Line Answer
"Permission Sets can only add, not remove access — so set the profile's FLS on Amount to Read Only (restrictive baseline), then grant Edit access via a Permission Set assigned to B, C, D but not A."
Q8
How do you achieve Field-Level Security (FLS) in Salesforce, and how do you enforce it in Apex?
✅ Direct Answer
Declaratively: set FLS per field on Profiles or Permission Sets (Visible/Read-Only/Hidden) — this is enforced automatically in standard UI, Reports, and List Views. In Apex, FLS is NOT enforced by default because Apex runs in system context — you must explicitly enforce it using WITH SECURITY_ENFORCED in SOQL (throws an exception if the running user lacks access to any selected field) or Security.stripInaccessible() (silently strips inaccessible fields from the result set instead of throwing).
💡 Why?
This is one of the highest-impact security gaps interviewers probe for, because Apex classes exposed to LWC/Aura via @AuraEnabled run with full system-level field access UNLESS you explicitly add enforcement — meaning a custom Apex controller can silently leak a field a user's Profile hides from them everywhere else in the org. This is exactly the class of vulnerability that shows up in Salesforce security review checklists and penetration test reports.
🌍 Real World Example
// WITH SECURITY_ENFORCED throws if user lacks FLS on any selected field List<Account> accs = [SELECT Id, Name, AnnualRevenue FROM Account WITH SECURITY_ENFORCED]; // stripInaccessible silently removes inaccessible fields SObjectAccessDecision dec = Security.stripInaccessible(AccessType.READABLE, accs); List<Account> safeAccs = (List<Account>) dec.getRecords();
XYZ Company's Account custom Apex controller was originally exposed to a Community (Experience Cloud) portal without WITH SECURITY_ENFORCED — a security audit caught that AnnualRevenue (hidden via FLS from the external portal profile) was being returned in the JSON payload anyway, because Apex bypassed FLS by default. Adding WITH SECURITY_ENFORCED closed the gap in a single line.
🔑 Key Points for Interviewer
  • FLS set on profiles and permission sets per field per object — enforced automatically in standard UI
  • WITH SECURITY_ENFORCED: throws exception if field inaccessible — good for internal tools where an error is acceptable
  • stripInaccessible(): removes inaccessible fields gracefully — better UX for community/portal-facing components
  • Apex bypasses FLS by default — always assume you must add enforcement explicitly on anything user-facing
🎤 One-Line Answer
"FLS is set on Profile/Permission Set per field — but Apex bypasses it by default. Enforce with WITH SECURITY_ENFORCED (throws) or Security.stripInaccessible() (silently strips inaccessible fields)."
Q9
SCENARIO: How do you prevent a manager from accessing a subordinate's records, even though Role Hierarchy is enabled?
✅ Direct Answer
On the specific object's sharing settings, uncheck "Grant Access Using Hierarchies" — this checkbox is available ONLY for custom objects. When unchecked, that object's sharing is controlled purely by OWD + Sharing Rules + Manual Sharing — Role Hierarchy no longer grants automatic upward visibility for that object at all. For standard objects (Account, Opportunity, Case, Contact), this checkbox does not exist — role hierarchy access can never be disabled for them.
💡 Why?
This is a classic trick question because most candidates assume role hierarchy is a universal, always-on behavior — but Salesforce deliberately makes it optional per custom object, because some custom objects genuinely shouldn't roll up visibility to managers (e.g., a "Performance Review" custom object where even a manager's manager shouldn't automatically see everything). For standard objects, Salesforce made the design decision that role hierarchy access is a fixed part of the platform's behavior and cannot be turned off — if a standard object truly needs to bypass hierarchy for one team, the only lever left is restructuring the Role Hierarchy itself (e.g., placing that team in a parallel branch that doesn't roll up to the manager in question).
🌍 Real World Example
XYZ Company built a custom Performance_Review__c object where HR explicitly did NOT want line managers automatically seeing reviews via role hierarchy (reviews are only shared with the reviewer and HR directly via a targeted sharing rule). They unchecked "Grant Access Using Hierarchies" on that object's sharing settings — OWD stayed Private, and only an explicit Sharing Rule (targeting the HR group) grants visibility, completely bypassing the org's role hierarchy for that one object.
🔑 Key Points for Interviewer
  • "Grant Access Using Hierarchies" checkbox — custom objects only
  • Standard objects: role hierarchy access can NEVER be disabled
  • For standard objects, workaround = restructure role hierarchy (parallel/flat roles for that team)
  • Unchecking this doesn't affect OWD, sharing rules, or manual sharing — those still apply normally
🎤 One-Line Answer
"For custom objects, uncheck 'Grant Access Using Hierarchies' in sharing settings to bypass role hierarchy entirely. For standard objects, this can't be disabled — you'd need to restructure the role hierarchy itself."
Q10
SCENARIO: Two users have the exact same Profile, but one sees more data than the other. Why, and how do you debug it?
✅ Direct Answer
Same Profile only guarantees the same object/field-level (CRUD/FLS) permissions — it says nothing about record-level visibility, which is governed entirely separately. Root causes to check in order: 1) Different Role (one sits higher in Role Hierarchy). 2) Different Public Group / Queue membership affecting sharing rules. 3) Manual sharing granted to one user specifically on individual records. 4) Different Permission Sets assigned (e.g., "View All" on one user only). 5) Territory assignment differences if Territory Management is enabled.
💡 Why?
This question exists because it's one of the most common real-world support tickets a Salesforce admin gets — "we have the same access but I see less" — and understanding it requires clearly separating two orthogonal security dimensions: object/field access (Profile-driven, identical for both users here) versus record visibility (driven entirely by Role, Groups, Sharing Rules, and Manual Sharing, none of which are tied to Profile at all). Interviewers use this to confirm you don't conflate these two systems.
🌍 Real World Example
At XYZ Company, two reps on the identical "Sales Rep" profile reported different Opportunity counts on their dashboards. Debug trail: both had the same Profile and identical Permission Set assignments — but one had recently been moved to a new Role that sat in a different branch of the hierarchy, no longer inheriting visibility into a specific regional team's Opportunities. The fix was correcting the Role assignment, not touching the Profile at all.
🔑 Key Points for Interviewer
  • Profile = object/field access. Record visibility = OWD + Role + Sharing + Groups — completely separate systems
  • Use Setup → Users → "Why can't I see this?" or Login As to reproduce the exact user experience
  • Check Permission Set assignments — often the real culprit ("View All Data" granted to one user only)
  • Check Role Hierarchy position — even same Profile, different Role = different visibility
🎤 One-Line Answer
"Same Profile only means same object/field access — record visibility depends on Role, Group membership, manual sharing, and Permission Sets, which can all differ between users on an identical Profile."
Q11
What is a Mixed DML error, and how do you bypass it (apart from @future)?
✅ Direct Answer
A Mixed DML error occurs when you try to DML a setup object (User, Group, PermissionSetAssignment, UserRole) and a non-setup object (Account, Contact, custom objects) in the SAME transaction — Salesforce blocks this to protect setup-object integrity during ongoing business-object transactions. Bypass options apart from @future: run the setup DML inside a Queueable (which executes in its own separate transaction context), use System.runAs() (only works inside test context, never in production), or split the operations across two separate user-initiated transactions entirely.
💡 Why?
Salesforce enforces this restriction because setup objects can affect sharing rules, permissions, and record visibility for the ENTIRE org — allowing them to be modified in the same transaction as regular business data creates a risk of security-relevant changes being partially applied or rolled back inconsistently. Since @future methods run asynchronously in their own transaction, they naturally sidestep the conflict — Queueable Apex offers the same isolation with more modern capabilities (chaining, SObject parameters).
🌍 Real World Example
XYZ Company had a trigger on a custom Onboarding__c object that, on insert, both created a Contact record AND assigned a Permission Set to the associated User in the same transaction — this threw MIXED_DML_OPERATION every time. Fix: moved the PermissionSetAssignment insert into a Queueable enqueued from the trigger's after-insert context, isolating the setup-object DML into its own transaction that runs immediately after the Contact creation commits.
🎤 One-Line Answer
"Mixed DML fires when setup objects (User, Group) and non-setup objects share a transaction. Apart from @future, use a Queueable to isolate the setup DML into its own transaction, or System.runAs() in test context."
Q12
What security enforcement options exist in Apex apart from 'with sharing'?
✅ Direct Answer
'with sharing' only enforces record-level sharing — it says nothing about FLS or object CRUD. Additional layers you need to add explicitly: WITH SECURITY_ENFORCED in SOQL (enforces FLS/CRUD together, throws exception on violation), Security.stripInaccessible() (enforces FLS/CRUD, silently strips inaccessible fields instead of throwing — good for graceful UX), manual Schema.sObjectType.describe() checks (fully custom FLS/CRUD verification for complex scenarios), and the newer WITH USER_MODE (enforces sharing + FLS + CRUD together in a single SOQL query, effectively combining 'with sharing' behavior with field/object security in one keyword).
💡 Why?
This question exposes a genuinely common misconception — many developers assume 'with sharing' is a complete security solution, when it only ever governs ONE of the three security layers (record-level). Object CRUD and field-level security require entirely separate enforcement, and Apex bypasses both by default. Understanding this distinction is exactly what separates a developer who writes secure Apex from one who writes Apex that "happens to work" until a security review finds the gap.
🌍 Real World Example
XYZ Company's controller class was declared 'with sharing' and the team assumed that alone made it secure — a code review found it was still returning a hidden Salary__c field to users whose Profile explicitly hid that field, because 'with sharing' had never touched FLS. Adding WITH USER_MODE to the SOQL query closed both the sharing AND the FLS gap in one change, replacing what would have needed two separate enforcement mechanisms.
🎤 One-Line Answer
"'with sharing' only covers record-level sharing. For FLS/CRUD, add WITH SECURITY_ENFORCED, Security.stripInaccessible(), or the newer WITH USER_MODE which enforces sharing + FLS + CRUD together in one query."

⚙️ Section 2 — Apex, Triggers & Governor Limits

Q13–Q24 · Live Coding Common

Q13
What are governor limits in Salesforce, and can you handle them in Apex using try-catch?
✅ Direct Answer
Governor limits cap resource usage per transaction to protect the multi-tenant platform (100 SOQL queries, 150 DML statements, 6 MB heap, 10,000 ms CPU time, and more). No — you CANNOT catch a governor limit exception (LimitException) with try-catch and continue execution — it's a fatal, uncatchable error that immediately rolls back the entire transaction. The only real solution is to never hit the limit in the first place — bulkify your code, and proactively check the various Limits.getLimitX() methods before attempting risky operations.
💡 Why?
This is a genuinely important trick question because it reveals whether a candidate understands the difference between "handleable" exceptions and platform-level circuit breakers. A DmlException or QueryException from a bad record IS catchable and recoverable mid-transaction — but a LimitException means the platform itself has determined the transaction is misbehaving in a way that could impact other tenants sharing the same infrastructure, so it forces an immediate, non-negotiable rollback. There's no graceful degradation path once you've hit the wall.
🌍 Real World Example
A junior developer at XYZ Company wrapped a loop containing SOQL in a try-catch, assuming that if it hit the 100-query limit on record 101, the catch block would let processing continue for the remaining records with a logged warning. Instead, the entire transaction — including the 100 successfully processed records — rolled back completely, because LimitException bypasses normal catch blocks. The real fix was bulkifying the loop to use a single SOQL query with an IN clause instead of one query per record.
🔑 Key Points for Interviewer
  • LimitException is NOT catchable with try-catch — it aborts the entire transaction, including prior successful work
  • Prevention is the only real strategy: bulkify, and use Limits.getQueries()/getDMLStatements() to monitor proactively
  • Design defensively: check remaining limit BEFORE the risky operation, not after
  • This differs from ordinary exceptions (DmlException, QueryException) which ARE catchable and recoverable
🎤 One-Line Answer
"No — governor limit exceptions can't be caught and recovered from; they immediately abort the transaction. The only real defense is prevention: bulkify code and proactively check Limits.getX() methods before risky operations."
Q14
Write a trigger to update a corresponding field on child records whenever a field is updated on the parent.
✅ Direct Answer
After-update trigger on the parent object — first detect which parent records actually had the relevant field change (comparing Trigger.new against Trigger.oldMap), then bulk-query all affected children via the relationship, and bulk-update them in a single DML statement outside any loop.
💡 Why?
The critical piece here isn't just "write a trigger that updates children" — it's demonstrating bulk-safe design under governor limits. A naive implementation queries and updates children inside the loop over Trigger.new, which works fine for 1 record in a test but throws "Too many SOQL queries" the moment 200 Account records are updated via Data Loader in one batch. The correct pattern always collects IDs first, queries once with an IN clause, builds a Map for O(1) lookups, and performs exactly one DML statement at the end regardless of how many parent/child records are involved.
🌍 Real World Example
trigger AccountTrigger on Account (after update) { Set<Id> accIdsChanged = new Set<Id>(); for(Account acc : Trigger.new) { Account oldAcc = Trigger.oldMap.get(acc.Id); if(acc.Industry != oldAcc.Industry) { accIdsChanged.add(acc.Id); } } if(accIdsChanged.isEmpty()) return; List<Contact> childrenToUpdate = new List<Contact>(); Map<Id, Account> accMap = new Map<Id, Account>([SELECT Id, Industry FROM Account WHERE Id IN :accIdsChanged]); for(Contact c : [SELECT Id, AccountId, Industry__c FROM Contact WHERE AccountId IN :accIdsChanged]) { c.Industry__c = accMap.get(c.AccountId).Industry; childrenToUpdate.add(c); } if(!childrenToUpdate.isEmpty()) update childrenToUpdate; }
XYZ Company needed Contact.Industry__c to always mirror Account.Industry for reporting purposes. The naive first draft queried Contacts inside the Account loop and hit governor limits during a bulk Account import of 5,000 records. The bulkified version above processes any batch size — 1 record or 5,000 — in exactly one SOQL query and one DML statement.
🎤 One-Line Answer
"After-update trigger: detect which parents actually changed via Trigger.oldMap comparison, bulk-query all children in one SOQL with IN clause, update them in one bulk DML — never query/update inside a loop."
Q15
Write a trigger to sum the Amount of all Opportunities and update it on the related Account.
✅ Direct Answer
This is exactly what a native Roll-Up Summary field would do automatically if Opportunity-to-Account were a Master-Detail relationship — but it's a standard Lookup relationship, so Salesforce provides no native roll-up mechanism, requiring either a custom Apex trigger or the DLRS (Declarative Lookup Rollup Summaries) AppExchange package.
💡 Why?
This is one of the most frequently asked "write it live" questions because it forces you to handle all four DML contexts correctly (insert, update, delete, undelete) — most candidates handle insert/update but forget that deleting an Opportunity should also trigger recalculation of the Account total, and Trigger.new is empty during a delete (you must use Trigger.old instead). It also tests whether you know to use an AggregateResult query with GROUP BY instead of looping through individual Opportunity records to sum manually in Apex.
🌍 Real World Example
trigger OpportunityTrigger on Opportunity (after insert, after update, after delete, after undelete) { Set<Id> accountIds = new Set<Id>(); List<Opportunity> opps = Trigger.isDelete ? Trigger.old : Trigger.new; for(Opportunity o : opps) { if(o.AccountId != null) accountIds.add(o.AccountId); } if(accountIds.isEmpty()) return; Map<Id, Decimal> totalsByAccount = new Map<Id, Decimal>(); for(AggregateResult ar : [SELECT AccountId acc, SUM(Amount) total FROM Opportunity WHERE AccountId IN :accountIds GROUP BY AccountId]) { totalsByAccount.put((Id)ar.get('acc'), (Decimal)ar.get('total')); } List<Account> accountsToUpdate = new List<Account>(); for(Id accId : accountIds) { accountsToUpdate.add(new Account( Id = accId, Total_Opportunity_Amount__c = totalsByAccount.containsKey(accId) ? totalsByAccount.get(accId) : 0 )); } update accountsToUpdate; }
XYZ Company's first version of this trigger only handled after insert/update — deleting a lost Opportunity never reduced the Account's Total_Opportunity_Amount__c, leaving a stale inflated number that finance flagged during a quarterly review. Adding after delete/undelete with the Trigger.isDelete check fixed the discrepancy.
🔑 Key Points for Interviewer
  • Handle all 4 DML contexts: insert, update, delete, undelete (Trigger.old for delete, since Trigger.new is empty)
  • AggregateResult with GROUP BY: one SOQL sums for all accounts at once, not a loop
  • Set to 0 explicitly when no opportunities remain — otherwise the stale prior value lingers on the Account
  • Real-world alternative: DLRS (Declarative Lookup Rollup Summaries) — zero code, admin-configurable
🎤 One-Line Answer
"Since Opportunity-Account is a Lookup (not Master-Detail), use an after-trigger with an AggregateResult SUM/GROUP BY query handling all four DML contexts, then bulk-update Accounts — or use the DLRS AppExchange package to avoid code entirely."
Q16
Write a trigger on Opportunity so it can't have more than one Opportunity Contact Role marked as Primary.
✅ Direct Answer
Before insert/update trigger on OpportunityContactRole — for each Opportunity in the current transaction that's being marked IsPrimary = true, bulk-query existing Primary contact roles on that Opportunity (excluding the record being saved, if it's an update to itself) and block the save with addError() if a different Primary already exists.
💡 Why?
This question is deliberately tricky because of the update-to-self edge case: if a record that's ALREADY the Primary contact role is being re-saved (e.g., a user edits an unrelated field on it), your validation must not falsely flag it as "conflicting with itself." A naive implementation that just checks "does a Primary already exist" without excluding the current record's own prior state will incorrectly block a legitimate save.
🌍 Real World Example
trigger OpportunityContactRoleTrigger on OpportunityContactRole (before insert, before update) { Set<Id> oppIds = new Set<Id>(); for(OpportunityContactRole ocr : Trigger.new) { if(ocr.IsPrimary) oppIds.add(ocr.OpportunityId); } if(oppIds.isEmpty()) return; Map<Id, Integer> primaryCountByOpp = new Map<Id, Integer>(); for(OpportunityContactRole existing : [SELECT Id, OpportunityId FROM OpportunityContactRole WHERE OpportunityId IN :oppIds AND IsPrimary = true]) { Integer count = primaryCountByOpp.containsKey(existing.OpportunityId) ? primaryCountByOpp.get(existing.OpportunityId) : 0; primaryCountByOpp.put(existing.OpportunityId, count + 1); } for(OpportunityContactRole ocr : Trigger.new) { if(ocr.IsPrimary) { Integer existingCount = primaryCountByOpp.containsKey(ocr.OpportunityId) ? primaryCountByOpp.get(ocr.OpportunityId) : 0; Boolean isSelfAlreadyPrimary = Trigger.isUpdate && Trigger.oldMap.get(ocr.Id).IsPrimary; if(existingCount > 0 && !isSelfAlreadyPrimary) { ocr.addError('This Opportunity already has a Primary Contact Role.'); } } } }
Without the isSelfAlreadyPrimary check, XYZ Company's sales reps couldn't edit ANY field on an existing Primary Contact Role record without the trigger falsely blocking the save, thinking a duplicate Primary existed — because the query found the record's own current state as "already Primary."
🎤 One-Line Answer
"Before trigger: bulk-query existing IsPrimary=true OCR records per Opportunity, and if a new/updated record tries to set IsPrimary=true while one already exists, block it with addError() — accounting for the update-to-self edge case."
Q17
What is the difference between Master-Detail and Lookup relationships, and how do you convert one to the other?
✅ Direct Answer
Master-Detail: cascade delete (deleting the parent deletes all children), required parent field, child inherits the parent's sharing/OWD, supports native Roll-Up Summary fields. Lookup: optional parent, independent sharing settings, no native Roll-Up Summary. Converting Lookup → Master-Detail is allowed ONLY if zero records have a null value in that lookup field. Converting Master-Detail → Lookup is always allowed (Setup → field → "Change to Lookup Relationship") but you immediately and irreversibly lose all Roll-Up Summary fields built on that relationship.
💡 Why?
Interviewers ask about the conversion direction specifically because it's asymmetric and has real production consequences — many teams design with Lookup by default (since it's more flexible) and only later realize they need Roll-Up Summary functionality, at which point they discover the conversion isn't guaranteed to succeed (a single orphaned/null-parent record blocks the whole conversion). Understanding this asymmetry is what separates textbook knowledge from real implementation experience.
🌍 Real World Example
XYZ Company originally modeled Order_Line_Item__c to Order__c as a Lookup, then six months later needed a native Roll-Up Summary for Total_Line_Value__c. The conversion attempt failed initially because 40 legacy line items had a null Order__c reference from a botched historical import — they had to clean up those orphaned records before Salesforce would allow the Lookup-to-Master-Detail conversion to complete.
🎤 One-Line Answer
"Master-Detail requires the parent and supports Roll-Up Summary; Lookup is optional and independent. Lookup→MD conversion needs zero nulls in the field; MD→Lookup is always allowed but destroys existing Roll-Up Summary fields."
Q18
What is a Bucket Field in Reports?
✅ Direct Answer
A Bucket Field lets you group report rows into custom categories entirely within the report builder — without creating a new formula field or modifying the underlying data model. Example: bucketing Opportunity Amount into "Small (<10K)", "Medium (10K-50K)", "Large (>50K)" for a single report's grouping and summarization.
💡 Why?
Bucket Fields exist to solve a common tension: business users frequently want ad-hoc groupings for a single report (e.g., "show me deals by size tier") that don't warrant permanently adding a new field to the object's schema. Since a Bucket Field lives entirely within the report's own configuration, it doesn't touch the data model, isn't visible on the record page layout, and doesn't require any admin deployment — a report builder can create one in seconds directly in the Report Builder UI.
🌍 Real World Example
XYZ Company's sales leadership wanted a quarterly pipeline report grouped by deal size tier, but didn't want a permanent Deal_Size_Tier__c field cluttering the Opportunity object just for one dashboard. A Bucket Field on the Amount column, configured with three ranges, delivered exactly that grouping without any schema change or deployment.
🎤 One-Line Answer
"Bucket Fields group report values into custom ranges/categories on the fly, within the report builder itself — no new field or formula needed on the object."
Q19
What are the different debug modes / log levels in Salesforce?
✅ Direct Answer
Log levels from least to most verbose: NONE, ERROR, WARN, INFO, DEBUG, FINE, FINER, FINEST — configurable independently per category (Apex Code, Database, Workflow, Validation, Callout, System, Visualforce). Set via Debug Levels in Setup, then attached to a Trace Flag that scopes logging to a specific user for a defined time window.
💡 Why?
Understanding the per-category granularity matters in real debugging — setting everything to FINEST generates enormous, hard-to-parse logs and hits the 20 MB log truncation limit fast, whereas a targeted approach (e.g., DEBUG on Apex Code but ERROR-only on Workflow) surfaces exactly the information you need without drowning it in noise. Trace Flags being time-boxed and user-scoped also matters operationally — you never leave verbose logging running indefinitely in production, since it degrades performance and fills storage.
🌍 Real World Example
XYZ Company's support team needed to debug a specific user's intermittent Flow failure in production. Instead of enabling org-wide verbose logging (which would have captured every user's activity and quickly hit log storage limits), they created a Trace Flag scoped to just that one user for a 2-hour window with FINEST on Workflow/Flow but only ERROR on everything else — capturing exactly the relevant detail without noise.
🎤 One-Line Answer
"Log levels from least to most verbose: NONE, ERROR, WARN, INFO, DEBUG, FINE, FINER, FINEST — set per category via Debug Levels and applied through a Trace Flag scoped to a user and time window."
Q20
What is the difference between Sales Cloud and Service Cloud?
✅ Direct Answer
Sales Cloud: manages the sales pipeline — Leads, Opportunities, Quotes, Forecasting, Campaigns. Service Cloud: manages customer support operations — Cases, Omni-Channel routing, Knowledge Base, Entitlements/SLAs, Live Agent/Chat. Both run on the exact same underlying platform and typically share the same Account/Contact objects — they differ primarily in which standard objects and feature licenses are enabled, not in the core architecture.
💡 Why?
Interviewers ask this to confirm you understand Salesforce's "Cloud" naming is a licensing/feature-bundle distinction rather than a separate platform — a company can, and most enterprise clients do, run BOTH Sales Cloud and Service Cloud licenses side-by-side in the SAME org, sharing the same Account/Contact records across pre-sale and post-sale processes seamlessly, because it's all one unified data model underneath.
🌍 Real World Example
XYZ Company runs both clouds in a single org: their sales reps work Opportunities through the pipeline using Sales Cloud features, and the moment a deal closes, the same Account record seamlessly transitions into the Service Cloud world where support reps handle post-sale Cases — no data migration or duplicate Account records needed, because both clouds are just different feature layers over the same platform.
🎤 One-Line Answer
"Sales Cloud focuses on the sales pipeline (Leads, Opportunities, Forecasting). Service Cloud focuses on post-sale support (Cases, Omni-Channel, Knowledge, Entitlements) — both share the same core platform and Account/Contact data."
Q21
Can we do SOAP integration without a WSDL?
✅ Direct Answer
Not practically — SOAP is fundamentally contract-based, and the WSDL IS that contract, defining every operation, message format, and data type available. Without a WSDL, you'd have to hand-craft raw XML SOAP envelopes and manually parse responses using low-level HTTP callouts (HttpRequest/HttpResponse) — technically possible, but you completely lose the type safety and auto-generated Apex classes that Salesforce's "Generate from WSDL" tool normally provides.
💡 Why?
This tests whether you understand WHY Salesforce's WSDL2Apex generator is valuable in the first place — it parses the WSDL contract and auto-generates strongly-typed Apex stub classes, meaning you call methods like myPort.getCustomer(id) instead of manually building XML strings and hoping the tags and namespaces are exactly right. Without a WSDL, you lose compile-time safety entirely and every integration becomes fragile, hand-maintained XML string manipulation.
🌍 Real World Example
XYZ Company inherited a legacy vendor integration where the vendor refused to provide a WSDL file, only raw API documentation. The team had to build the SOAP envelope manually via HttpRequest with hardcoded XML namespaces and manually parse the XML response using the Dom.Document class — a fragile, maintenance-heavy approach that broke every time the vendor changed a field name, exactly the risk a proper WSDL-generated class would have caught at compile time.
🎤 One-Line Answer
"Not practically — the WSDL is SOAP's contract. Without it you'd hand-build raw XML envelopes via HttpRequest, losing the type safety and auto-generated Apex classes that Salesforce's WSDL2Apex tool provides."
Q22
When would you go with REST integration over SOAP?
✅ Direct Answer
Choose REST when: you need lightweight JSON payloads, mobile/web clients are involved, you want simpler and faster development (no WSDL contract required), or higher API call limits matter for your integration volume. Choose SOAP only when the external/legacy system mandates it (many older ERP/banking systems only speak SOAP) or you need strict typed contracts for enterprise governance/compliance reasons.
💡 Why?
This question tests whether you have real integration architecture judgment rather than just knowing definitions — Salesforce itself recommends REST for all new development, and the reasoning matters: REST's stateless JSON model maps naturally onto how modern mobile apps and JavaScript frontends consume data, development iteration is faster without WSDL contract regeneration on every change, and Salesforce's REST API generally allows more calls per day than SOAP under the same license.
🌍 Real World Example
XYZ Company built a brand-new mobile app integration using REST because the mobile team's JSON-native tooling made it a natural fit with fast iteration cycles. Meanwhile, their 15-year-old SAP financial system integration remained on SOAP simply because SAP's middleware team had no roadmap to support REST endpoints and migrating would have required a multi-year SAP-side project — the decision was entirely driven by what the external system could support, not by Salesforce's own preference.
🎤 One-Line Answer
"REST for modern, lightweight JSON integrations with mobile/web clients and simpler development. SOAP only when a legacy external system mandates it or strict typed contracts are a compliance requirement."
Q23
What is the difference between var and let in JavaScript?
✅ Direct Answer
var is function-scoped and hoisted (accessible before its declaration line, initialized as undefined until reached) — and it can be redeclared multiple times in the same scope without error. let is block-scoped (respects {} boundaries like if/for blocks), is NOT accessible before declaration (sits in a "temporal dead zone" and throws a ReferenceError if accessed early), and cannot be redeclared in the same scope. Modern LWC JavaScript should always use let/const, never var.
💡 Why?
This gap in scoping behavior is exactly the kind of subtle bug that causes real production issues — a var declared inside an if block or for loop silently "leaks" out and remains accessible (and mutable) in the surrounding function scope, which has historically caused countless closure and loop-variable bugs in JavaScript (e.g., a var loop counter shared incorrectly across async callbacks). let's block-scoping eliminates that entire category of bug, which is why Salesforce's LWC linting rules and best practices explicitly disallow var.
🌍 Real World Example
// var - function scoped, leaks out of blocks if(true) { var x = 10; } console.log(x); // 10 - accessible outside the if block! // let - block scoped, stays contained if(true) { let y = 20; } console.log(y); // ReferenceError: y is not defined
An LWC component at XYZ Company had a subtle bug where a var declared inside a for loop's callback was being shared across all iterations of an async operation, causing every row in a table to display the same (last-iteration) value instead of its own — switching to let fixed it immediately because each iteration got its own block-scoped binding.
🎤 One-Line Answer
"var is function-scoped and hoisted — leaks out of blocks. let is block-scoped and respects {} boundaries — always use let/const in modern LWC JavaScript, never var."
Q24
SOQL — write a query to fetch Leads created in the last 4 years.
✅ Direct Answer
Use the LAST_N_YEARS date literal — it dynamically calculates the date range relative to today at query execution time, so no hardcoded dates or Apex Date math are ever needed.
💡 Why?
Date Literals are specifically designed for exactly this kind of relative-time query, and they're strongly preferred over hardcoded date comparisons because the query stays correct forever — a report or scheduled batch built today with LAST_N_YEARS:4 will still correctly mean "the last 4 years" a year from now without any code change, whereas a hardcoded date would silently become wrong the moment time passes.
🌍 Real World Example
SELECT Id, Name, CreatedDate, Status FROM Lead WHERE CreatedDate = LAST_N_YEARS:4
XYZ Company runs a nightly Scheduled Apex batch that archives stale Leads older than 4 years — using LAST_N_YEARS:4 (inverted with a NOT or < comparison) means the batch's query logic never needs to be updated as time passes, unlike an earlier version that hardcoded a specific cutoff date and had to be manually corrected every year.
🎤 One-Line Answer
"SELECT Id, Name FROM Lead WHERE CreatedDate = LAST_N_YEARS:4 — the date literal auto-calculates relative to today, no hardcoded dates."

⏳ Section 3 — Async & Batch Apex

Q25–Q31 · Advanced

Q25
What is Asynchronous Apex? Name the types and when to use each.
✅ Direct Answer
Asynchronous Apex runs in a separate thread/transaction outside the current request, with its own fresh set of governor limits. Four types: @future (simple fire-and-forget, primitive parameters only), Queueable (chainable, accepts SObjects, exposes a monitorable Job ID), Batch Apex (processes millions of records in manageable chunks), Scheduled Apex (time-based trigger, most commonly used just to launch a Batch job on a schedule).
💡 Why?
Choosing the right async type is a genuine architecture decision, not just trivia — @future is the simplest but most limited (no chaining, no SObject params, no monitoring), which is why Queueable has largely superseded it in modern Apex. Batch is reserved specifically for volumes too large to fit within a single transaction's governor limits (millions of records), while Scheduled Apex is purely about WHEN something runs, not HOW MUCH it processes — which is why it's almost always paired with Batch rather than doing heavy processing directly in its own execute() method.
🌍 Real World Example
XYZ Company uses: a nightly Scheduled Apex job (fires at 2 AM) that launches a Batch Apex class to recalculate scores across 2 million Account records; a Queueable chain to process payment-then-confirmation-email in guaranteed sequence when an Order closes; and a legacy @future method for a fire-and-forget audit log callout where failure doesn't matter and no chaining is needed.
🎤 One-Line Answer
"Async Apex runs outside the current transaction with fresh governor limits — @future for simple fire-and-forget, Queueable for chainable SObject-based jobs, Batch for millions of records, Scheduled for time-based triggers."
Q26
Why can't we call other async operations from the start() or execute() method in Batch Apex?
✅ Direct Answer
start() and execute() already run in an asynchronous context. Salesforce restricts nesting async-within-async at that level specifically to prevent runaway chains that could exhaust the org's 24-hour async execution limit (250,000 executions per rolling day). You CAN enqueue a Queueable from execute() (that's explicitly allowed), but you cannot call Database.executeBatch() to start a brand-NEW batch job from execute() — that's only permitted from finish(), which runs once after the entire current batch job has fully completed.
💡 Why?
This restriction exists as a safety mechanism — imagine execute() (which runs once PER CHUNK, potentially hundreds of times for a large dataset) were allowed to freely start new batch jobs; a single misconfigured batch could spawn hundreds of additional batch jobs uncontrollably, exhausting org-wide async capacity in minutes. Restricting new-batch-starts to finish() (which runs exactly ONCE regardless of how many chunks were processed) caps the chaining to a predictable, linear sequence rather than an exponential explosion.
🌍 Real World Example
A developer at XYZ Company initially tried calling Database.executeBatch() inside execute() to kick off a follow-up batch per chunk — with 5,000 records and a scope of 200, that would have attempted to start 25 separate new batch jobs, immediately throwing a runtime error since new batches can only launch from finish(). Restructuring to trigger the next batch from finish() (running once, after ALL chunks complete) fixed it cleanly.
🎤 One-Line Answer
"start()/execute() already run async — Salesforce blocks starting a new Batch from execute() to prevent runaway chaining. You can enqueue a Queueable from execute(), but a new Batch can only be started from finish()."
Q27
Can we call @future from another @future method? Can we call a Batch from another Batch?
✅ Direct Answer
@future cannot call another @future method — Salesforce blocks nested future calls entirely and throws a runtime error if attempted. Batch calling Batch: yes, this IS allowed (called "chaining"), but ONLY from the finish() method of the currently completing batch — you cannot start a new batch from the start() or execute() method of the currently running batch (see Q26).
💡 Why?
@future's restriction is stricter than Batch's because @future has no equivalent to Batch's finish() lifecycle hook — there's no "safe point" in a @future method's execution where Salesforce can guarantee a nested call won't create an uncontrolled chain, so it's blocked outright. Batch Apex was specifically designed with finish() as that safe chaining point, which is why Salesforce allows Batch-to-Batch chaining but not @future-to-@future nesting.
🌍 Real World Example
XYZ Company migrated a multi-step data sync process that originally attempted @future methods calling other @future methods (which failed at runtime) into a Queueable chain instead — Queueable supports self-chaining from within its own execute() method (up to a limited depth in production), which @future simply cannot do.
🎤 One-Line Answer
"No — @future methods cannot call other @future methods, that's blocked outright. Batch chaining IS allowed, but only by calling Database.executeBatch() from the finish() method, never from start() or execute()."
Q28
Can we make an HTTP callout from Batch Apex?
✅ Direct Answer
Yes — implement the Database.AllowsCallouts marker interface alongside Database.Batchable on the batch class. Without explicitly declaring this interface, any HTTP callout attempted inside execute() throws a CalloutException, because Salesforce requires you to explicitly opt in to network access from within an async job for security/governance reasons.
💡 Why?
Requiring the explicit AllowsCallouts declaration forces developers to make a conscious decision about network access rather than accidentally allowing it — this matters in enterprise governance because callouts from batch jobs processing large volumes of records can create significant external system load (imagine 100,000 records each triggering an API call), so making it opt-in encourages developers to think about rate limiting and scope size before enabling it.
🌍 Real World Example
global class SyncBatch implements Database.Batchable<SObject>, Database.AllowsCallouts { global Database.QueryLocator start(Database.BatchableContext bc) { return Database.getQueryLocator('SELECT Id FROM Account WHERE Synced__c = false'); } global void execute(Database.BatchableContext bc, List<Account> scope) { Http h = new Http(); HttpRequest req = new HttpRequest(); req.setEndpoint('callout:ERP_System/sync'); req.setMethod('POST'); h.send(req); // Allowed because of Database.AllowsCallouts } global void finish(Database.BatchableContext bc) {} }
XYZ Company's ERP sync batch initially forgot the AllowsCallouts interface — it worked fine in dev testing with a small dataset until it was run against a real batch of 50,000 unsynced Accounts, immediately throwing CalloutException on the first execute() chunk. Adding the interface, and additionally reducing the batch scope size to 50 records per chunk to respect the ERP's rate limits, resolved it.
🎤 One-Line Answer
"Yes — implement Database.AllowsCallouts alongside Database.Batchable. Without that marker interface, any HTTP callout in execute() throws a CalloutException."
Q29
How do you run two unrelated queries in a single Batch class?
✅ Direct Answer
Database.getQueryLocator() in start() supports only ONE query. For two genuinely unrelated queries, your realistic options are: 1) run two completely separate Batch classes if the datasets don't need to be processed together, 2) combine them into a single query if the objects share any relationship (even indirectly), or 3) query the second unrelated dataset fresh inside execute() for each chunk of the first — accepting the extra SOQL cost per chunk since execute() gets fresh governor limits each time.
💡 Why?
This question exposes a real architectural constraint of the Batchable interface — start() is designed around a single QueryLocator/Iterable as the batch's driving dataset, because that's what determines the chunking and pagination behavior. Trying to force two unrelated datasets into one batch job usually signals a design smell — if the two datasets genuinely have nothing to do with each other, they probably belong in two separate scheduled batch jobs rather than artificially coupled into one.
🌍 Real World Example
XYZ Company needed to nightly process both stale Leads AND expired Contracts — two entirely unrelated objects with no shared relationship. Rather than forcing them into one awkward batch, they simply scheduled two separate Batch classes (LeadCleanupBatch and ContractExpiryBatch) at the same 2 AM window — cleaner code, easier to monitor independently in the Apex Jobs list, and each can be re-run or paused without affecting the other.
🎤 One-Line Answer
"start() only supports one QueryLocator — for a genuinely unrelated second dataset, either split into two separate Batch classes, or query the second dataset fresh inside execute() for each chunk of the first."
Q30
Write the basic syntax structure of Batch Apex.
✅ Direct Answer
Implement the Database.Batchable<SObject> interface with three required methods: start() returns a QueryLocator (or Iterable) defining the full scope, execute() runs once per chunk (default 200 records, configurable), and finish() runs exactly once after all chunks have completed, typically used for post-processing or chaining the next job.
🌍 Real World Example
global class MyBatchClass implements Database.Batchable<SObject> { global Database.QueryLocator start(Database.BatchableContext bc) { return Database.getQueryLocator('SELECT Id, Name FROM Account WHERE Rating = null'); } global void execute(Database.BatchableContext bc, List<SObject> scope) { List<Account> toUpdate = new List<Account>(); for(SObject s : scope) { Account a = (Account)s; a.Rating = 'Cold'; toUpdate.add(a); } update toUpdate; } global void finish(Database.BatchableContext bc) { // Post-processing: send email, chain next batch, etc. } } // Execute: Database.executeBatch(new MyBatchClass(), 200);
🎤 One-Line Answer
"Implement Database.Batchable — start() returns a QueryLocator defining scope, execute() processes each chunk (default 200), finish() runs once after all chunks complete for post-processing or chaining."
Q31
SCENARIO: A Data Loader job updates 100 Opportunities to Stage = Closed 1 month ago. Now the customer wants to undo those changes. How do you approach this?
✅ Direct Answer
The Recycle Bin won't help here (that's only for deleted records, not field updates, and only lasts 15 days anyway — the change was made a month ago). Real options: 1) If Field History Tracking was enabled on StageName BEFORE the original update, query the OpportunityHistory object to find the pre-change value per record, then Data Loader update them back to those original values. 2) If a backup/export was taken before the original update, restore from that export via Data Loader. 3) If neither exists, there is no reliable way to auto-recover the exact prior values — you would need business input on what the correct values should be for each of the 100 records.
💡 Why?
This is one of the most important scenario questions because it tests whether you understand that Salesforce has NO built-in "undo" for DML updates — unlike a document editor's Ctrl+Z, once a field update commits, the prior value is gone unless you specifically had a safety net in place beforehand. The only real safety nets are Field History Tracking (if proactively enabled before the change), a pre-change export/backup, or a 3rd-party backup tool like OwnBackup or Odaseva that offers point-in-time restore. This is exactly why the real "correct" answer to this scenario always includes the forward-looking lesson: always export a backup CSV before any mass Data Loader update.
🌍 Real World Example
SELECT Field, OldValue, NewValue, CreatedDate FROM OpportunityFieldHistory WHERE OpportunityId IN :affectedOppIds AND Field = 'StageName' ORDER BY CreatedDate DESC
XYZ Company faced this exact situation — a bulk Stage update needed reverting after the customer changed their mind about a process change a month later. Because Field History Tracking had been enabled on StageName for over a year, they queried OpportunityFieldHistory for the OldValue per record right before the mass update's timestamp, exported that as a CSV, and re-ran Data Loader to restore each Opportunity to its correct pre-change Stage — a recovery that would have been completely impossible without history tracking already switched on.
🔑 Key Points for Interviewer
  • Recycle Bin ≠ field-level undo — only helps with deletes, and only for 15 days
  • Field History Tracking (if enabled beforehand): OpportunityHistory object stores OldValue/NewValue per field change
  • Best practice: ALWAYS export current data before any mass Data Loader update — this is the real lesson interviewers want to hear
  • 3rd-party backup tools (OwnBackup, Odaseva) provide point-in-time restore for exactly this scenario
🎤 One-Line Answer
"There's no native undo — check if Field History Tracking was enabled on StageName (query OpportunityHistory for prior values) or restore from a pre-update backup export. The real lesson: always export a backup CSV before any mass Data Loader update."

💻 Section 4 — LWC Fundamentals

Q32–Q42 · Asked in Every Round

Q32
Explain LWC lifecycle hooks with practical use cases.
✅ Direct Answer
constructor() — fires once, before any DOM or parent property access, rarely used beyond calling super(). connectedCallback() — fires once when the component is inserted into the DOM, ideal for data loading and subscriptions. renderedCallback() — fires after EVERY render (initial AND every subsequent re-render), so it MUST be guarded with a boolean flag to avoid repeating logic. disconnectedCallback() — fires once on removal from DOM, used for cleanup (unsubscribing, clearInterval). errorCallback() — catches errors thrown by descendant/child components.
💡 Why?
renderedCallback is the single most common source of LWC bugs candidates need to understand deeply — because it fires on EVERY re-render (triggered by any reactive property change), code that initializes a chart library or a DOM element without a guard will re-initialize on every single click, keystroke, or data update, sometimes causing duplicate event listeners, memory leaks, or visibly broken UI (like a chart re-rendering from scratch and flickering). The isInitialized boolean pattern exists specifically to make renderedCallback behave like a "run once" hook when that's what you actually need.
🌍 Real World Example
renderedCallback() { if(this.isInitialized) return; this.isInitialized = true; const canvas = this.template.querySelector('canvas'); if(canvas) this.initializeChart(canvas); }
An LWC dashboard component at XYZ Company initialized a Chart.js graph inside renderedCallback WITHOUT a guard — every time a filter dropdown changed (triggering a reactive re-render), the chart re-initialized from scratch, causing a visible flicker and, worse, leaking a new Chart.js instance into memory each time without disposing the old one, eventually crashing the browser tab after extended use. Adding the isInitialized guard fixed both issues.
🎤 One-Line Answer
"constructor (once, no DOM) → connectedCallback (once, load data here) → renderedCallback (every render, guard with a boolean) → disconnectedCallback (once, cleanup here) — renderedCallback firing repeatedly is the #1 gotcha."
Q33
What are Decorators in LWC? Explain @api, @track, @wire.
✅ Direct Answer
Decorators add special behavior to class properties/methods. @api: marks a property/method public — the parent component can set it via an attribute, or an external caller (like a Flow) can invoke it. @track: makes an object/array reactive to internal (deep) mutations — rarely needed in modern LWC since primitives are auto-reactive by default; @track is only required when you mutate nested properties of an object or array in place. @wire: binds a property or function to an Apex method or Lightning Data Service wire adapter, and re-fires reactively whenever any reactive ($) parameter changes.
💡 Why?
A common misconception is applying @track everywhere "just in case" — but LWC's rendering engine is already reactive by default for any property reassignment (this.myArray = [...this.myArray, newItem]), and @track only matters for the narrower case of mutating an existing object/array's internal properties WITHOUT reassigning the whole reference (this.myObject.name = 'new'). Understanding this distinction avoids both unnecessary boilerplate and, more importantly, subtle bugs where a developer mutates a nested property expecting reactivity that doesn't actually fire without @track.
🌍 Real World Example
A developer at XYZ Company had a component where updating a nested field inside a wrapper object (this.formData.email = newEmail) silently didn't refresh the UI — because reassigning a nested property doesn't trigger reactivity on a plain @api or default property. Adding @track this.formData fixed it, though the team eventually preferred reassigning the whole object (this.formData = {...this.formData, email: newEmail}) since that pattern works without needing @track at all and is considered the more modern approach.
🎤 One-Line Answer
"@api exposes a property/method publicly to the parent. @track makes deep object mutations reactive (rarely needed now). @wire declaratively binds Apex/LDS data, auto-refiring when reactive ($) parameters change."
Q34
What is the difference between @wire and an imperative Apex call? Show the syntax for both.
✅ Direct Answer
@wire: declarative, auto-calls the Apex method the instant the component loads and again automatically whenever any reactive ($) parameter changes, best for reads, and only works with @AuraEnabled(cacheable=true) methods. Imperative: Promise-based, called explicitly (e.g., on button click via await), required for any DML/write operation since @wire structurally only supports cacheable (read-only) Apex methods.
💡 Why?
This restriction — @wire only works with cacheable=true — isn't arbitrary; it exists because @wire's whole value proposition is client-side caching and automatic re-fetching, both of which require the underlying operation to be side-effect-free and repeatable. A method that performs DML can't safely be "silently re-called" whenever a reactive parameter changes, since that could trigger unintended duplicate writes — so Salesforce enforces the cacheable requirement at the platform level, refusing to even compile a @wire pointing at a non-cacheable method.
🌍 Real World Example
// @WIRE - declarative, auto-loads import { wire } from 'lwc'; import getAccounts from '@salesforce/apex/AccountController.getAccounts'; @wire(getAccounts, { industry: '$selectedIndustry' }) wiredAccounts({ data, error }) { if(data) this.accounts = data; if(error) this.errorMsg = error.body.message; } // IMPERATIVE - called on demand import saveAccount from '@salesforce/apex/AccountController.saveAccount'; async handleSave() { try { await saveAccount({ accData: this.formData }); } catch(error) { this.errorMsg = error.body.message; } }
XYZ Company's Account list component uses @wire with a reactive industry filter parameter — every time the user changes a dropdown, the wire automatically re-fires and refreshes the list with zero manual refresh logic needed. Saving edits, however, goes through an imperative call, since @wire simply cannot be used for the DML-performing saveAccount method.
🎤 One-Line Answer
"@wire auto-loads reactively and only works with cacheable=true methods — use for reads. Imperative calls (await someMethod()) are on-demand and required for DML/writes."
Q35
What is Lightning Data Service (LDS) and why is it important?
✅ Direct Answer
LDS provides zero-Apex CRUD via lightning-record-form / lightning-record-edit-form / getRecord+updateRecord wire adapters. Importance: it automatically enforces FLS and validation rules with no extra code, it shares one client-side cache across EVERY LDS-based component on the page (edit an Account in one component, every other component showing that same Account auto-refreshes to reflect it), and it eliminates the boilerplate Apex controller + test class code that a simple CRUD screen would otherwise require.
💡 Why?
The shared cache behavior is genuinely important and frequently misunderstood — because multiple independent LDS components on the same page read from the SAME underlying record cache, updating a record in one component causes every OTHER component displaying that record to automatically re-render with the new value, with zero manual event-passing or refreshApex calls required. This "just works" synchronization is a major reason to prefer LDS over custom Apex whenever the requirement is genuinely simple single-record CRUD.
🌍 Real World Example
XYZ Company built an Account detail page with three separate LWC components: a header showing the Account Name, an inline edit form, and a related-info panel. All three use LDS's getRecord/updateRecord for the same Account — when a user edits the Name via the inline form, the header component automatically updates too, without any custom event wiring, purely because both components are reading from the same LDS cache.
🎤 One-Line Answer
"LDS gives zero-Apex CRUD that respects FLS/validation rules automatically and shares one client-side cache across every component on the page — use it for standard record CRUD instead of writing custom Apex."
Q36
How do you display data from a @wire method into a lightning-datatable?
✅ Direct Answer
Wire the Apex method's result directly into a property, define a columns array describing each column's label/fieldName/type, and bind both data and columns to the lightning-datatable component along with a required key-field (typically Id) that lets the framework efficiently track and diff rows.
🌍 Real World Example
// JS import { LightningElement, wire } from 'lwc'; import getAccounts from '@salesforce/apex/AccountController.getAccounts'; export default class AccountTable extends LightningElement { accounts; error; columns = [ { label: 'Name', fieldName: 'Name' }, { label: 'Rating', fieldName: 'Rating' }, { label: 'Phone', fieldName: 'Phone', type: 'phone' } ]; @wire(getAccounts) wiredAccounts({ data, error }) { if(data) this.accounts = data; if(error) this.error = error; } } // HTML // <lightning-datatable key-field="Id" data={accounts} columns={columns} hide-checkbox-column> // </lightning-datatable>
XYZ Company's Opportunity dashboard uses this exact pattern — one @wire call populates the datatable, and because the wire is reactive to a search-term parameter, typing in a search box automatically re-fires the wire and refreshes the table with zero manual data-fetching logic.
🎤 One-Line Answer
"@wire the Apex method into a property, define a columns array with label/fieldName/type, then bind data={accounts} and columns={columns} on lightning-datatable with key-field='Id'."
Q37
Write JavaScript code to show Account fields Name, Rating, and Phone.
✅ Direct Answer
Import field references from @salesforce/schema, wire them into getRecord, and expose each field's value via a getter using getFieldValue() — this pattern gives compile-time validation of field/object API names, unlike a hardcoded string approach.
🌍 Real World Example
import { LightningElement, api, wire } from 'lwc'; import { getRecord, getFieldValue } from 'lightning/uiRecordApi'; import NAME_FIELD from '@salesforce/schema/Account.Name'; import RATING_FIELD from '@salesforce/schema/Account.Rating'; import PHONE_FIELD from '@salesforce/schema/Account.Phone'; export default class AccountFields extends LightningElement { @api recordId; @wire(getRecord, { recordId: '$recordId', fields: [NAME_FIELD, RATING_FIELD, PHONE_FIELD] }) account; get name() { return getFieldValue(this.account.data, NAME_FIELD); } get rating() { return getFieldValue(this.account.data, RATING_FIELD); } get phone() { return getFieldValue(this.account.data, PHONE_FIELD); } }
🎤 One-Line Answer
"Use getRecord wire adapter with @salesforce/schema field imports for Name/Rating/Phone, then getFieldValue() getters to safely extract each value from the wire result."
Q38
How do you restrict users from copy-pasting text into a text box in LWC — they should only be able to type?
✅ Direct Answer
Attach an onpaste (and typically oncopy/oncut) event handler on the lightning-input and call event.preventDefault() to block the paste action from taking effect.
💡 Why?
It's important to flag the limitation honestly in an interview: this is a UX-level deterrent only, not a genuine security or data-integrity control — a technically capable user can bypass it via browser DevTools, disabling JavaScript, or using browser automation tools to inject text directly. If the actual requirement is preventing certain content patterns (not just the paste ACTION), the correct approach is server-side validation (a Validation Rule or Apex check on save), because client-side JavaScript restrictions can never be trusted as a real security boundary.
🌍 Real World Example
// HTML // <lightning-input label="Enter Code" onpaste={handlePaste}></lightning-input> // JS handlePaste(event) { event.preventDefault(); // Optionally show a toast: "Pasting is not allowed here" }
XYZ Company's compliance team requested a "no paste" field for a verification code entry screen, intending to force users to genuinely type it rather than copy from elsewhere. The onpaste handler satisfied that specific UX requirement, but the team explicitly documented that it's not a hard security control — the actual verification logic still validates the code server-side in Apex regardless of how it was entered.
🎤 One-Line Answer
"Bind an onpaste handler and call event.preventDefault() — but flag to the interviewer that this is a UX-level deterrent only, not a real security or data-integrity control."
Q39
What is a Toast Message in LWC? How many types are there?
✅ Direct Answer
A Toast is a brief pop-up notification shown via ShowToastEvent (from lightning/platformShowToastEvent). Four variant types: info (default, blue), success (green), warning (yellow), error (red). It also supports a mode attribute: 'dismissible' (default, auto-closes after a few seconds), 'sticky' (stays visible until the user manually closes it — reserved for important errors), and 'pester' (repeats to draw attention).
💡 Why?
Choosing the right variant AND mode combination is a real UX decision, not just a technicality — using 'sticky' mode for routine success confirmations is annoying (users have to manually dismiss something they already expected), while using the default dismissible mode for a critical save-failure error means the user might miss it entirely if they look away for a moment. The convention most mature Salesforce teams follow is: success/info use dismissible, error uses sticky.
🌍 Real World Example
import { ShowToastEvent } from 'lightning/platformShowToastEvent'; this.dispatchEvent(new ShowToastEvent({ title: 'Success', message: 'Record saved successfully', variant: 'success', mode: 'dismissible' }));
XYZ Company standardized their toast usage across all LWC components: success/info toasts auto-dismiss after 5 seconds (mode: dismissible), while error toasts always use mode: sticky so a failed save is never accidentally missed by a busy user who's already moved their attention elsewhere.
🎤 One-Line Answer
"Four toast variants: info, success, warning, error — dispatched via ShowToastEvent from lightning/platformShowToastEvent, with mode 'sticky' for errors that shouldn't auto-dismiss."
Q40
What is uiRecordApi and why do we use it?
✅ Direct Answer
lightning/uiRecordApi is the module powering Lightning Data Service — it exposes getRecord, createRecord, updateRecord, deleteRecord, and getFieldValue functions for zero-Apex CRUD directly from LWC JavaScript. It's used because it auto-enforces FLS/validation rules with no extra code, and it shares its cache with every other LDS-based component on the page, reducing both the amount of Apex code you write AND the number of duplicate server round-trips.
🎤 One-Line Answer
"lightning/uiRecordApi provides getRecord/createRecord/updateRecord/deleteRecord for zero-Apex CRUD via LDS — used because it auto-enforces FLS/validation and shares a page-wide cache, avoiding redundant Apex and server calls."
Q41
What are the different ways to get data from the backend in LWC?
✅ Direct Answer
1) @wire to an Apex method (declarative, cacheable). 2) Imperative Apex call (on-demand, Promise-based). 3) Lightning Data Service (getRecord wire adapter — zero Apex). 4) @wire to a UI API module directly (getObjectInfo, getPicklistValues). 5) An Apex controller wrapping a REST callout to an external system. 6) The newer GraphQL wire adapter, for fetching complex nested data in a single round trip.
💡 Why?
Interviewers want to see that you can choose the RIGHT one for the situation, not just list them — LDS is fastest to build for simple single-record CRUD, @wire-to-Apex is the go-to for anything more complex needing custom SOQL logic, and GraphQL specifically shines when a single component needs deeply nested related data (e.g., Account + its Contacts + each Contact's Opportunities) in one efficient call instead of multiple round trips.
🎤 One-Line Answer
"Six ways: @wire to Apex, imperative Apex calls, Lightning Data Service (getRecord), UI API wire modules (getObjectInfo/getPicklistValues), Apex-wrapped REST callouts, and the newer GraphQL wire adapter."
Q42
Explain child-to-parent communication in Aura Components.
✅ Direct Answer
Aura uses a Component Event (aura:event type="COMPONENT") — the child registers and fires it, and the parent registers a handler for it by explicitly declaring the event name as an attribute on the child's tag in markup. Unlike LWC's CustomEvent (which bubbles up the DOM automatically), Aura Component Events require this explicit parent-side wiring at the point where the child is used.
💡 Why?
This is often asked specifically because most candidates only know LWC patterns and need to demonstrate they can also work on legacy Aura codebases still common at Capgemini and other consulting firms with long-tenured client orgs — knowing this pattern signals you can maintain existing Aura components without needing an immediate LWC rewrite for every bug fix, which is important for realistic project timelines.
🌍 Real World Example
<!-- Child Component markup --> <aura:component> <aura:registerEvent name="itemSelected" type="c:ItemSelectedEvent"/> <lightning:button label="Select" onclick="{!c.handleClick}"/> </aura:component> // Child controller.js handleClick: function(component, event, helper) { var selectedEvent = component.getEvent("itemSelected"); selectedEvent.setParams({ "itemId": "123" }); selectedEvent.fire(); } <!-- Parent markup - registers handler on the child --> <c:childComponent itemSelected="{!c.handleItemSelected}"/>
XYZ Company maintains a legacy Aura-based Community page predating their LWC migration — a child item-selector component fires a Component Event on selection, and the parent page's controller.js handles it via the itemSelected attribute declared on the child's tag, exactly mirroring how the equivalent LWC CustomEvent pattern works today.
🎤 One-Line Answer
"Aura child-to-parent uses a Component Event — child fires it via registerEvent/fire(), parent explicitly declares a handler attribute on the child's tag in markup, unlike LWC's auto-bubbling CustomEvent."

🔀 Section 5 — LWC Communication & Navigation

Q43–Q49 · Live Coding Common

Q43
Write LWC child-to-parent communication code.
✅ Direct Answer
The child creates and dispatches a CustomEvent carrying a detail payload; the parent listens for it by adding an on+eventname attribute directly on the child's tag in its own template, then reads event.detail in the handler to get the data the child sent.
🌍 Real World Example
// CHILD component (childComp.js) export default class ChildComp extends LightningElement { handleClick() { this.dispatchEvent(new CustomEvent('itemselect', { detail: { itemId: '001', itemName: 'Widget' } })); } } // PARENT markup // <c-child-comp onitemselect={handleItemSelect}></c-child-comp> // PARENT JS handleItemSelect(event) { const { itemId, itemName } = event.detail; console.log('Selected: ' + itemName + ' (' + itemId + ')'); }
🎤 One-Line Answer
"Child dispatches a CustomEvent with a detail payload — parent listens via on+eventname on the child's tag (onitemselect) and reads event.detail to get the data."
Q44
How do you pass a value from one component to another (unrelated) component in LWC?
✅ Direct Answer
For unrelated/sibling components with no parent-child relationship anywhere in the DOM tree, use Lightning Message Service (LMS) — publish a message on a shared Message Channel from one component, subscribe to it in the other, regardless of where each one sits on the page or how deeply nested it is.
💡 Why?
This distinction matters because the @api/CustomEvent pattern only works along a direct parent-child DOM relationship — if two components are siblings placed independently on a Lightning App Page or Record Page with no shared ancestor that mediates between them, there's structurally no way for a CustomEvent to bubble from one to the other. LMS was specifically built to solve exactly this gap, working even across completely different component types (LWC, Aura, Visualforce) on the same page.
🎤 One-Line Answer
"For unrelated components: Lightning Message Service — publish() on a Message Channel from one component, subscribe() in the other. For parent-child: @api property (down) and CustomEvent (up)."
Q45
How do you create a channel in the pub-sub model — create channel, subscribe, and publish?
✅ Direct Answer
1) Create Channel: add a Lightning Message Channel metadata file (force-app/main/default/messageChannels/MyChannel.messageChannel-meta.xml) defining the channel's name and its message payload fields. 2) Publish: import publish + MessageContext from lightning/messageService, call publish(context, MY_CHANNEL, payload) from the sending component. 3) Subscribe: import subscribe + MessageContext, call subscribe(context, MY_CHANNEL, callback) in connectedCallback of the receiving component, and always unsubscribe() in disconnectedCallback to prevent memory leaks.
💡 Why?
The unsubscribe-in-disconnectedCallback step is not optional housekeeping — it's a genuine bug source if skipped. If a component subscribes but is later removed from the DOM (e.g., a modal that closes, or navigation to a different record) without unsubscribing, the subscription callback remains registered and can fire on stale, already-removed component state, or accumulate multiple duplicate subscriptions if the component is re-mounted repeatedly — both cause real, hard-to-diagnose bugs in production.
🌍 Real World Example
<!-- 1. CREATE CHANNEL: messageChannels/RecordSelected.messageChannel-meta.xml --> <LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata"> <masterLabel>RecordSelected</masterLabel> <isExposed>true</isExposed> <lightningMessageFields> <fieldName>recordId</fieldName> </lightningMessageFields> </LightningMessageChannel> // 2. PUBLISH (componentA.js) import { publish, MessageContext } from 'lightning/messageService'; import RECORD_SELECTED from '@salesforce/messageChannel/RecordSelected__c'; @wire(MessageContext) messageContext; handleSelect(id) { publish(this.messageContext, RECORD_SELECTED, { recordId: id }); } // 3. SUBSCRIBE (componentB.js) import { subscribe, unsubscribe, MessageContext } from 'lightning/messageService'; import RECORD_SELECTED from '@salesforce/messageChannel/RecordSelected__c'; @wire(MessageContext) messageContext; subscription = null; connectedCallback() { this.subscription = subscribe(this.messageContext, RECORD_SELECTED, (msg) => { this.loadRecord(msg.recordId); }); } disconnectedCallback() { unsubscribe(this.subscription); }
XYZ Company built a warehouse dashboard where a list component and a detail-panel component sit independently on the same App Page (no parent-child relationship). Selecting a row in the list publishes a RecordSelected message; the detail panel subscribes and updates instantly — and because the detail panel properly unsubscribes in disconnectedCallback, navigating away and back doesn't leave orphaned subscriptions accumulating.
🎤 One-Line Answer
"Create a Message Channel metadata file, publish() from the sender with the channel + payload, subscribe() in connectedCallback on the receiver, and always unsubscribe() in disconnectedCallback to prevent memory leaks."
Q46
How do you navigate to another record page on click of a button in LWC?
✅ Direct Answer
Extend the component with NavigationMixin, then call this[NavigationMixin.Navigate]() with a standard__recordPage page reference object containing the recordId, objectApiName, and actionName.
🌍 Real World Example
import { LightningElement, api } from 'lwc'; import { NavigationMixin } from 'lightning/navigation'; export default class OpenRecord extends NavigationMixin(LightningElement) { @api recordId; handleClick() { this[NavigationMixin.Navigate]({ type: 'standard__recordPage', attributes: { recordId: this.recordId, objectApiName: 'Account', actionName: 'view' } }); } }
🎤 One-Line Answer
"Extend NavigationMixin(LightningElement), then call this[NavigationMixin.Navigate]({type:'standard__recordPage', attributes:{recordId, objectApiName, actionName:'view'}})."
Q47
What are Navigation Services in LWC?
✅ Direct Answer
NavigationMixin (from lightning/navigation) provides programmatic navigation to standard pages (records, object lists, tabs, named pages), web pages, and Knowledge articles — and it works consistently across Lightning Experience, the Salesforce Mobile App, and Experience Cloud without any environment-specific code. Two main methods: Navigate() (perform the actual navigation) and GenerateUrl() (get the destination URL as a plain string, e.g., for use in an href attribute, without triggering navigation).
🎤 One-Line Answer
"NavigationMixin exposes Navigate() (go to a page reference) and GenerateUrl() (get URL as a string without navigating) — works consistently across Lightning Experience, Mobile, and Experience Cloud."
Q48
How do you make a callout from LWC (i.e., call an external/third-party service from an LWC)?
✅ Direct Answer
LWC JavaScript CANNOT make HTTP callouts directly to external systems — browser CORS/CSP restrictions and Salesforce's security policy block arbitrary fetch()/XMLHttpRequest calls from LWC to non-whitelisted external domains. The correct pattern: the LWC calls an imperative Apex method → that Apex method makes the HTTP callout server-side (via HttpRequest + Named Credential) → the result is returned back to the LWC as the resolved Promise value.
💡 Why?
This restriction is a genuine security boundary, not just a technical limitation — allowing arbitrary client-side JavaScript to make direct network calls from inside a Salesforce org would expose credentials and endpoint details to the browser and any user who inspects network traffic, and would completely bypass server-side governance (Named Credentials, callout limits, audit logging). Routing every external call through Apex keeps authentication server-side and gives you a single, auditable choke point for all outbound integrations.
🌍 Real World Example
// Apex - makes the actual callout public with sharing class WeatherService { @AuraEnabled public static String getWeather(String city) { HttpRequest req = new HttpRequest(); req.setEndpoint('callout:Weather_API/forecast?city=' + city); req.setMethod('GET'); HttpResponse res = new Http().send(req); return res.getBody(); } } // LWC - calls Apex, never the external API directly import getWeather from '@salesforce/apex/WeatherService.getWeather'; async handleFetch() { this.result = await getWeather({ city: 'Mumbai' }); }
A new developer at XYZ Company initially tried calling fetch() directly from an LWC's JavaScript to hit a third-party shipping-rate API — it failed immediately due to CSP restrictions in the Salesforce runtime. Wrapping the call in an Apex method using a Named Credential resolved it cleanly and, as a bonus, meant the API key never touched the browser at all.
🎤 One-Line Answer
"LWC can't call external services directly — it calls an imperative Apex method, and that Apex method makes the actual HTTP callout via Named Credential, then returns the result back to the component."
Q49
SCENARIO: An Opportunity's Stage is published via a Platform Event and must be caught by a third-party system, and also shown live in an LWC component. Walk through the design.
✅ Direct Answer
1) Create a Platform Event (Stage_Change__e) with fields for OpportunityId and NewStage. 2) After-update trigger on Opportunity: when StageName actually changes, publish the event via EventBus.publish(). 3) The 3rd-party system subscribes independently via CometD/Streaming API directly to the event channel — it has no dependency on Salesforce's internal UI at all. 4) The LWC subscribes to the SAME Platform Event using the empApi module (imported from lightning/empApi), updating its own local state and re-rendering live in the callback.
💡 Why?
This scenario is specifically designed to test your understanding of the publish-subscribe decoupling principle — the trigger that publishes the event has ZERO knowledge of who's listening, whether it's the third-party system, the LWC, both, or neither. This is architecturally superior to the trigger directly calling out to the third party AND separately pushing an update to the UI, because adding a THIRD subscriber later (say, a Slack notification) requires zero changes to the original trigger — you just add another independent subscriber to the same event.
🌍 Real World Example
// Trigger - publish on stage change trigger OpportunityTrigger on Opportunity (after update) { List<Stage_Change__e> events = new List<Stage_Change__e>(); for(Opportunity opp : Trigger.new) { if(opp.StageName != Trigger.oldMap.get(opp.Id).StageName) { events.add(new Stage_Change__e( OpportunityId__c = opp.Id, NewStage__c = opp.StageName )); } } if(!events.isEmpty()) EventBus.publish(events); } // LWC - subscribe using empApi import { subscribe, unsubscribe } from 'lightning/empApi'; connectedCallback() { subscribe('/event/Stage_Change__e', -1, (msg) => { this.currentStage = msg.data.payload.NewStage__c; }).then(response => { this.subscription = response; }); }
XYZ Company's warehouse fulfillment system needed real-time notification the instant a deal closed, and their internal sales dashboard needed a live-updating "just closed" ticker. Both were built as independent subscribers to the same Stage_Change__e event — when the warehouse system was later replaced with a different vendor, the trigger and the LWC dashboard needed zero changes; only the external subscriber's connection details changed.
🔑 Key Points for Interviewer
  • Platform Event decouples publisher from subscribers — trigger doesn't know or care who's listening
  • 3rd-party system uses CometD/Streaming API to subscribe externally
  • LWC uses lightning/empApi module (built on the same underlying Streaming API)
  • Both subscribers receive the event independently and in real time, with no coordination needed between them
🎤 One-Line Answer
"Publish a custom Platform Event from an after-update trigger on stage change — the 3rd-party system subscribes via CometD/Streaming API, and the LWC subscribes to the same event using the lightning/empApi module — both react independently in real time."

🔄 Section 6 — Flows & Automation

Q50–Q54

Q50
Explain Flow Screen elements.
✅ Direct Answer
Screen elements are the UI building blocks in a Screen Flow: standard input components (Text, Number, Date, Picklist, Checkbox, Currency), Display Text (static/formatted read-only text), Section (multi-column layout grouping for organizing fields visually), Data Table (display and let users select records), Address/Name compound components, and custom Lightning Web Components exposed as Screen Flow components via the lightning__FlowScreen target.
💡 Why?
The ability to expose a custom LWC as a Screen Flow component is the single most powerful escape hatch in Flow — it means whenever the standard Screen elements can't deliver a specific UX (search-as-you-type, drag-and-drop, a rich data visualization), you don't have to abandon Flow entirely for a full custom Visualforce/Aura app. You build ONE LWC targeting lightning__FlowScreen, and it slots into the Flow's guided journey as if it were a native element, giving admins the ability to keep the surrounding process declarative while the one complex screen gets custom code.
🌍 Real World Example
XYZ Company's returns process runs as a Screen Flow so the support lead can freely edit the questions and branching logic without needing a developer — but one specific screen (the product picker) needed search-as-you-type with image thumbnails, which standard Screen elements structurally can't deliver. They built one custom LWC targeting lightning__FlowScreen just for that screen, keeping every other step of the process fully declarative.
🎤 One-Line Answer
"Screen elements are the Flow's UI building blocks — Text/Number/Picklist/Checkbox inputs, Display Text, Section (layout), Data Table, and custom LWCs exposed via the lightning__FlowScreen target."
Q51
What are the different types of Flows?
✅ Direct Answer
Record-Triggered Flow (Before Save / After Save), Screen Flow (guided user UI), Scheduled Flow (time-based), Auto-launched Flow (called from Apex/other Flows/REST, no trigger of its own), Platform Event-Triggered Flow. Note: Workflow Rules and Process Builder are retired — all new automation should be built in Flow, and Salesforce provides a "Migrate to Flow" tool to convert existing legacy automation.
💡 Why?
Understanding the Before Save vs After Save distinction on Record-Triggered Flows specifically matters for real interview depth — Before Save flows modify the triggering record's own fields WITHOUT a separate DML operation (fast, cheap, no governor limit cost), while After Save flows can create/update RELATED records but require actual DML since the triggering record has already been committed. Choosing the wrong one for a given requirement either wastes performance or simply won't work (you can't create a related Task in a Before Save flow context).
🌍 Real World Example
XYZ Company uses a Before Save Record-Triggered Flow to auto-populate a Description field on Opportunity the instant it's created (zero DML cost, blazing fast), and a separate After Save Flow on the same object to create related onboarding Tasks when Stage reaches Closed Won — deliberately split because the Task-creation logic structurally requires After Save's full DML capability.
🎤 One-Line Answer
"Record-Triggered (Before/After Save), Screen, Scheduled, Auto-launched, and Platform Event-Triggered — Workflow Rules and Process Builder are retired, so Flow now covers all these use cases."
Q52
What is the difference between a Workflow Rule and a Flow?
✅ Direct Answer
Workflow Rules are retired (no new ones can be created as of recent Salesforce releases) — they only ever supported simple actions on a SINGLE object: field update, email alert, task creation, outbound message. Flow does everything Workflow Rules did, plus multi-object DML in one transaction, loops, decision branching, subflows for reusable logic, external callouts (via Invocable Apex), and full guided screen interactions for users — Salesforce's "Migrate to Flow" tool automatically converts existing Workflow Rules into equivalent Flows.
💡 Why?
This question tests both historical platform knowledge (important for maintaining legacy orgs, which Capgemini's consulting clients frequently have) and current best practice — many enterprise orgs still have dozens of active Workflow Rules from years-old implementations that continue running fine (retirement means no NEW creation, not that existing rules stop working), so knowing when and how to migrate them, versus leaving them alone, is a real judgment call interviewers want to probe.
🌍 Real World Example
XYZ Company inherited an org with 40 active Workflow Rules from a 2019 implementation. Rather than migrating all 40 at once (a risky, low-value exercise for rules that work fine), they prioritized migrating only the ones that needed NEW capability (like multi-object logic a Workflow Rule structurally couldn't do), leaving the simple, stable single-field-update rules untouched until a future consolidation project.
🎤 One-Line Answer
"Workflow Rules are retired and were limited to single-object field updates/email/task actions. Flow replaces them entirely with multi-object DML, loops, decisions, and Apex integration — use Migrate to Flow to convert legacy rules."
Q53
Will a Record-Triggered Flow fire on a delete DML operation?
✅ Direct Answer
Yes — Record-Triggered Flows support "A record is deleted" as a trigger condition (this runs as Before Delete only — there's no After Delete equivalent for Flow the way Apex triggers have both before and after delete contexts). Use this for cleanup logic, like blocking deletion of a record under certain business conditions, or logging key details before the record is permanently removed.
💡 Why?
The Before-Delete-only limitation matters practically because it means a Flow can PREVENT a delete (by adding a validation-style error) or capture data right before removal, but it cannot react to a delete AFTER it's already committed — for genuinely after-the-fact delete reactions (like notifying an external system once a record is truly gone), you'd need an Apex after-delete trigger instead, since Flow's delete-trigger capability doesn't extend that far.
🌍 Real World Example
XYZ Company built a Before Delete Record-Triggered Flow on Opportunity that blocks deletion if the Opportunity has any related Closed Won child records, protecting historical revenue data from accidental removal — since the requirement was specifically to PREVENT a certain class of deletion, Before Delete was exactly the right trigger point.
🎤 One-Line Answer
"Yes — Record-Triggered Flows support delete as a trigger event (Before Delete), commonly used to block deletion under certain conditions or log details before the record is removed."
Q54
Can we call one Flow from another Flow?
✅ Direct Answer
Yes — using a Subflow element, which calls another Auto-launched Flow (not Record-Triggered or Screen Flow directly), passing input variables into it and receiving output variables back. This enables reusing common logic (e.g., a shared "Send Notification" flow) across multiple different parent flows without duplicating the logic in each one.
💡 Why?
Subflows are the direct Flow equivalent of extracting a reusable method in Apex — the DRY (Don't Repeat Yourself) principle applies just as much to declarative automation as it does to code. Without subflows, updating shared logic (like a notification pattern) means manually editing every single parent flow that duplicated it, which is exactly the kind of maintenance burden that turns a well-intentioned Flow-heavy org into a fragile mess over time.
🌍 Real World Example
XYZ Company built a Notification_Subflow that sends an email, posts to Chatter, and creates a follow-up Task — five completely different parent flows (Case Close, Opportunity Win, Lead Convert, Contract Activate, Order Complete) all call this same subflow at their respective completion points. When the notification template needed a design refresh, the team updated the one subflow once, and all five parent processes picked up the change automatically.
🎤 One-Line Answer
"Yes — via a Subflow element, which calls an Auto-launched Flow with input/output variables, letting multiple parent flows share and reuse common logic in one place."

🔗 Section 7 — Integration & Named Credentials

Q55–Q61 · Cross-Topic Focus

Q55
What is a Named Credential?
✅ Direct Answer
A Named Credential stores an external endpoint URL plus its authentication details (OAuth tokens, API keys, certificates) securely in Salesforce metadata — encrypted and never exposed in code. Apex callouts reference it as 'callout:CredentialName' instead of hardcoding the URL and credentials directly in the source. Salesforce auto-handles OAuth token refresh behind the scenes, and using a Named Credential means you don't need a separate Remote Site Setting for that same domain.
💡 Why?
The security implication is the real reason this matters beyond convenience — hardcoded credentials in Apex source code end up committed to version control (Git), visible to every developer with repo access, and nearly impossible to rotate cleanly (you'd need a code deployment just to change an API key). Named Credentials solve all three problems at once: the secret never touches source code, access is controlled through Setup permissions rather than repo access, and rotating a credential is a metadata change, not a deployment.
🌍 Real World Example
// WITHOUT Named Credentials - SECURITY RISK HttpRequest req = new HttpRequest(); req.setEndpoint('https://api.erp.com/orders'); req.setHeader('Authorization', 'Bearer hardcoded_token_here'); // In GitHub = security breach! // WITH Named Credentials - SECURE HttpRequest req = new HttpRequest(); req.setEndpoint('callout:ERP_Integration/orders'); req.setMethod('GET');
A security audit at XYZ Company found a hardcoded API token committed to their Git repository from an early integration prototype — the token had been sitting there, visible to every developer on the team, for over a year. Migrating to a Named Credential meant the token lived only in Salesforce's encrypted metadata store going forward, and rotating it after the incident required zero code changes.
🎤 One-Line Answer
"Named Credential stores the endpoint URL + auth details securely as metadata — referenced as 'callout:Name' in Apex, auto-refreshes OAuth tokens, and needs no separate Remote Site Setting."
Q56
How do you call a Named Credential from an Apex class?
✅ Direct Answer
Set the HttpRequest's endpoint to 'callout:CredentialName/path' instead of a full hardcoded URL — Salesforce automatically resolves the base URL from the Named Credential's configuration and injects the correct authentication header at send time, completely transparent to your Apex code.
🌍 Real World Example
HttpRequest req = new HttpRequest(); req.setEndpoint('callout:ERP_System/api/orders'); // Named Credential + path req.setMethod('GET'); req.setHeader('Content-Type', 'application/json'); Http http = new Http(); HttpResponse res = http.send(req); System.debug(res.getBody());
XYZ Company's ERP sync class references callout:ERP_System for the base URL — when the ERP system migrated to a new domain during a vendor consolidation, the entire Apex codebase needed zero changes; only the Named Credential's endpoint field in Setup was updated.
🎤 One-Line Answer
"Set req.setEndpoint('callout:CredentialName/path') instead of a hardcoded URL — Salesforce resolves the base URL and injects authentication automatically at send time."
Q57
Why do we use Remote Site Settings? What's the difference between Remote Site Settings and Named Credentials?
✅ Direct Answer
Remote Site Settings whitelist an external domain so Apex callouts to it are allowed at all — Salesforce blocks callouts to any non-whitelisted domain by default as a baseline security measure. The difference: Remote Site Settings ONLY whitelist the URL — you still hardcode and manage authentication yourself in code. Named Credentials whitelist the URL AND securely manage authentication (OAuth, tokens, certificates) for you — using a Named Credential means you don't need a separate Remote Site Setting for that domain, since the Named Credential implicitly covers that whitelisting.
💡 Why?
This is genuinely a "which tool for which job" question — Remote Site Settings still have a legitimate use case: when you need to whitelist a domain for a scenario that ISN'T a standard Apex HTTP callout (like an iframe embed or a Visualforce remote object reference), where Named Credentials don't apply. But for the standard "Apex makes an authenticated HTTP request" scenario, Named Credentials are strictly superior and considered current best practice — reaching for Remote Site Settings alone in that scenario is now considered a legacy pattern.
🌍 Real World Example
XYZ Company's legacy 2018 integration used a Remote Site Setting plus a hardcoded API key stored in a Custom Setting. During a security modernization pass, they migrated to a Named Credential, which eliminated both the separate Remote Site Setting entry AND the manually-managed API key in code — one metadata object now handles both the whitelisting and the authentication.
🎤 One-Line Answer
"Remote Site Settings just whitelist a domain for callouts — you still manage auth in code. Named Credentials whitelist the domain AND securely manage authentication for you — Named Credentials make Remote Site Settings unnecessary for standard callouts."
Q58
What is the difference between a Connected App and Remote Site Settings?
✅ Direct Answer
A Connected App registers an EXTERNAL application so IT can authenticate INTO Salesforce (inbound — an external system calling Salesforce's own APIs via OAuth). Remote Site Settings whitelist an external domain so Salesforce (Apex) can call OUT to it (outbound). They solve the exact opposite directions of the same underlying trust problem: one governs who can reach IN, the other governs where Salesforce is allowed to reach OUT.
💡 Why?
Confusing these two is a common interview mistake precisely because both involve "external systems talking to Salesforce" conceptually — but the direction of the arrow is what differentiates them entirely. A mobile app calling the Salesforce REST API to fetch Account data needs a Connected App (inbound trust). An Apex batch job calling out to a third-party shipping-rate API needs Remote Site Settings or a Named Credential (outbound trust). Getting this backward means configuring the wrong mechanism entirely and the integration simply won't work.
🌍 Real World Example
XYZ Company's mobile sales app authenticates INTO Salesforce via a Connected App using the Web Server OAuth flow to read Opportunity data — that's inbound. Separately, an Apex Batch job at XYZ Company calls OUT to a third-party tax-calculation API using a Named Credential (which internally covers the Remote Site Setting's job) — that's outbound. Two completely separate configurations solving two completely separate directions of trust.
🎤 One-Line Answer
"Connected App enables an external system to authenticate INTO Salesforce (inbound OAuth). Remote Site Settings whitelist a domain for Salesforce to call OUT to (outbound Apex callouts) — opposite directions."
Q59
Explain OAuth, SAML, and SSO — how are they different?
✅ Direct Answer
OAuth 2.0: an authorization protocol — grants an app scoped ACCESS to resources/APIs without ever sharing the actual password (e.g., "allow this app to read my Salesforce data"). SAML: an authentication protocol — an XML-based assertion used for identity federation, proving WHO you are to a Service Provider. SSO (Single Sign-On): the overall end-user CAPABILITY of logging in once and accessing multiple connected systems without re-entering credentials — typically IMPLEMENTED using SAML or OAuth/OpenID Connect underneath.
💡 Why?
The precise distinction between authentication (who you are) and authorization (what you can do) is a genuinely important security concept that these three terms map onto directly — SAML answers "prove your identity" while OAuth answers "grant this specific scoped permission." SSO is neither protocol itself; it's the USER EXPERIENCE outcome that either (or both) protocols can deliver. Interviewers ask this to confirm you're not just pattern-matching acronyms but understand the underlying security model each one solves.
🌍 Real World Example
XYZ Company's employees log into Salesforce through their corporate Okta identity provider using SAML — Okta asserts "this is definitely John from Finance" and Salesforce trusts that assertion (authentication). Separately, a third-party reporting tool uses OAuth to request read-only access to Opportunity data on behalf of a logged-in user, without that tool ever seeing the user's actual Salesforce password (authorization). Both mechanisms together deliver the overall "log in once, access everything seamlessly" SSO experience employees actually feel day to day.
🔑 Key Points for Interviewer
  • OAuth = authorization (what you can DO/access)
  • SAML = authentication (who you ARE), XML-based assertions
  • SSO = the end-user capability/experience, built on top of SAML or OAuth/OIDC
  • Salesforce supports both as Identity Provider and Service Provider for SSO configurations
🎤 One-Line Answer
"OAuth is authorization (scoped access without sharing passwords). SAML is authentication (proving identity via XML assertions). SSO is the overall 'log in once, access everything' experience, typically built on SAML or OAuth/OpenID Connect."
Q60
What is a Promise exception in JavaScript and how would you handle it?
✅ Direct Answer
A Promise rejects when an async operation fails — a network error, an exception thrown inside the executor, or an explicit reject() call. Handle it with .catch() in a .then()/.catch() promise chain, or with try-catch wrapped around an await expression inside an async function. An unhandled Promise rejection surfaces as a console warning/error and can silently break the intended application flow if it isn't explicitly caught somewhere.
💡 Why?
In LWC specifically, unhandled Promise rejections are a real production reliability risk — if an imperative Apex call fails and the rejection isn't caught, the user sees no error message and no feedback whatsoever, they simply experience the UI "doing nothing" with no indication of failure, which is a frustrating and confusing UX. Every imperative Apex call in production-quality LWC code should have explicit try-catch (async/await style) or .catch() handling that surfaces a meaningful message via ShowToastEvent.
🌍 Real World Example
// async/await style (preferred in LWC) async handleSave() { try { const result = await saveAccount({ accData: this.formData }); this.showToast('Success', 'Saved!', 'success'); } catch(error) { this.showToast('Error', error.body.message, 'error'); } } // .then/.catch style saveAccount({ accData: this.formData }) .then(result => this.showToast('Success', 'Saved!', 'success')) .catch(error => this.showToast('Error', error.body.message, 'error'));
XYZ Company's early LWC components frequently omitted .catch() on imperative Apex calls — when a save failed due to a validation rule, users reported "the save button just doesn't do anything" with zero visible error, generating confused support tickets. A code review standard now mandates try-catch on every imperative Apex call with a ShowToastEvent surfacing the failure clearly.
🎤 One-Line Answer
"A Promise exception fires when an async operation fails — handle it with try/catch around an await call (preferred in LWC) or .catch() in a .then() chain; unhandled rejections silently break app flow."
Q61
How were you doing deployment on your project? How were you deploying LWC components specifically?
✅ Direct Answer
Standard modern pattern: source-driven development in VS Code with Salesforce Extensions/SFDX, code committed to Git on feature branches, Pull Request review before merge, then deployed via SFDX CLI (sf project deploy start) or a CI/CD tool (Copado, Gearset, GitHub Actions, Azure DevOps) progressively through sandboxes (Dev → QA → UAT → Production). LWC components deploy identically to any other metadata type — they're just another folder (lwc) under force-app/main/default, retrieved and deployed with the exact same SFDX commands used for Apex classes, Flows, or any other metadata.
💡 Why?
This question tests whether you've actually worked in a modern, git-based Salesforce delivery pipeline rather than relying on legacy Change Sets — which is important because Capgemini, as a large consulting firm, runs almost exclusively source-driven CI/CD pipelines on client projects. Being able to speak concretely about branch strategy, PR review gates, and progressive sandbox promotion signals real production experience rather than sandbox-only tinkering.
🌍 Real World Example
XYZ Company's delivery pipeline: developers work in individual scratch orgs or dev sandboxes, commit to a feature branch, open a PR that triggers an automated GitHub Actions job running the full Apex/LWC Jest test suite, requires one peer approval, then auto-merges to a develop branch that deploys to an integration sandbox nightly — with a separate, manually-gated release branch promoting to UAT and then Production behind a change-approval board.
🎤 One-Line Answer
"Git-based source-driven development with SFDX — feature branch → PR review → CI/CD pipeline (Copado/Gearset/GitHub Actions) deploys through Dev → QA → UAT → Production. LWC deploys identically to Apex — it's just metadata under the lwc folder."

🔍 Section 8 — SOQL & Reports

Q62–Q66

Q62
Write a SOQL query to fetch Accounts created yesterday.
✅ Direct Answer
Use the YESTERDAY date literal, which dynamically calculates the previous calendar day relative to the org's time zone at query execution time, so no hardcoded date math is ever needed.
🌍 Real World Example
SELECT Id, Name, CreatedDate FROM Account WHERE CreatedDate = YESTERDAY
XYZ Company's daily "new accounts created" summary email uses this exact query inside a Scheduled Apex job running at 7 AM every morning — because YESTERDAY dynamically resolves relative to today, the exact same query logic works correctly every single day without any code change.
🎤 One-Line Answer
"SELECT Id, Name FROM Account WHERE CreatedDate = YESTERDAY — a dynamic date literal, no hardcoded date math needed."
Q63
How do you write a test class for that YESTERDAY query if there's no Account actually created yesterday in the test context?
✅ Direct Answer
Insert a test Account normally, then use Test.setCreatedDate(recordId, DateTime) to override its CreatedDate to yesterday. CreatedDate is a system audit field that normally can't be set directly during a standard insert (it always defaults to the actual moment of insertion) — Test.setCreatedDate() is a purpose-built test-context method specifically designed to allow this override for exactly this kind of scenario.
💡 Why?
This question tests a genuinely subtle but common testing challenge — any query using a relative date literal (YESTERDAY, LAST_N_DAYS, THIS_MONTH) is structurally impossible to test reliably by simply inserting a record "now," because the record's CreatedDate will always be today's timestamp regardless of when the test runs. Without Test.setCreatedDate(), you'd have no way to ever get 100% meaningful test coverage on this exact query logic — you'd only be testing that the query runs without error, not that it actually returns the correct records for the intended date condition.
🌍 Real World Example
@isTest static void testYesterdayAccountQuery() { Account acc = new Account(Name = 'Test Yesterday Co'); insert acc; // Override CreatedDate to yesterday Test.setCreatedDate(acc.Id, DateTime.now().addDays(-1)); Test.startTest(); List<Account> result = [SELECT Id FROM Account WHERE CreatedDate = YESTERDAY]; Test.stopTest(); System.assertEquals(1, result.size(), 'Should find the account created yesterday'); }
Before discovering Test.setCreatedDate(), a developer at XYZ Company had written a test that simply asserted the query "didn't throw an exception" — technically passing coverage requirements but providing zero real confidence the YESTERDAY filter logic actually worked correctly. Adding the CreatedDate override let the test genuinely validate the filter's correctness.
🎤 One-Line Answer
"Insert the test record normally, then call Test.setCreatedDate(recordId, DateTime.now().addDays(-1)) to override its system-generated CreatedDate to yesterday for the assertion."
Q64
Write a SOQL query to fetch Accounts that have related Contacts.
✅ Direct Answer
Use a semi-join subquery: filter Account where its Id appears in a nested SELECT AccountId FROM Contact query — optionally paired with a parent-to-child subquery in the SELECT clause to also return the matching Contact records inline in one round trip.
🌍 Real World Example
SELECT Id, Name, (SELECT Id, Name FROM Contacts) FROM Account WHERE Id IN (SELECT AccountId FROM Contact WHERE AccountId != null)
XYZ Company runs a monthly data-quality report identifying Accounts with zero related Contacts (the inverse of this query, using NOT IN) to flag prospect records that never received proper onboarding — the same semi-join pattern, just inverted.
🎤 One-Line Answer
"Use a semi-join: SELECT Id, Name FROM Account WHERE Id IN (SELECT AccountId FROM Contact) — optionally with a subquery (SELECT Id FROM Contacts) to also return the related contact records inline."
Q65
Do you know the History object in Salesforce? Can we query it?
✅ Direct Answer
Yes — every object with Field History Tracking enabled gets an auto-generated companion History object (e.g., OpportunityFieldHistory, AccountHistory) storing Field, OldValue, NewValue, CreatedDate, and CreatedById per tracked field change. It IS queryable via standard SOQL, but has real restrictions — standard retention is typically 18-24 months (extendable with Field Audit Trail/Shield), and standard objects use ObjectHistory naming conventions while custom objects use CustomObject__History.
💡 Why?
Knowing this object exists and is queryable is exactly what makes scenarios like Q31 (undoing a mass Data Loader update) solvable at all — without Field History Tracking having been proactively enabled BEFORE a change happens, there is genuinely no way to recover the prior values afterward. This is why experienced Salesforce architects recommend enabling Field History Tracking on any field where "what was this before" is a plausible future business question, even if there's no immediate use case for it yet.
🌍 Real World Example
SELECT Field, OldValue, NewValue, CreatedDate, CreatedBy.Name FROM OpportunityFieldHistory WHERE OpportunityId = '006XXXXXXXXXXXX' AND Field = 'StageName' ORDER BY CreatedDate DESC
XYZ Company's sales operations team regularly queries OpportunityFieldHistory to investigate disputes about when exactly a deal's Stage changed and who changed it — this audit trail has repeatedly resolved commission calculation disagreements that would otherwise have been impossible to settle without a documented history.
🎤 One-Line Answer
"Yes — enabling Field History Tracking creates a queryable ObjectFieldHistory object (Field, OldValue, NewValue, CreatedDate) — standard retention is 18-24 months unless Field Audit Trail (Shield) extends it further."
Q66
How do you use Custom Metadata Type values inside Apex code?
✅ Direct Answer
Query it like a regular SObject — Custom Metadata records are cached by the Salesforce platform itself, so reading them never counts against the SOQL governor limit, no matter how many times you access them in a transaction. Access via the generated getInstance() method by DeveloperName, getAll() for the full set, or a direct standard SOQL query on the __mdt object.
💡 Why?
The governor-limit-free caching behavior is the specific reason Custom Metadata is preferred over Custom Settings for configuration that's read frequently within a transaction — imagine a trigger handler checking a feature-flag-style Custom Metadata value on every iteration of a loop over 200 records; if that were a regular SOQL query, it would instantly exhaust the 100-query limit, but because Custom Metadata reads are cached and free, it costs nothing no matter how many times it's accessed.
🌍 Real World Example
// Option 1: getInstance() by DeveloperName API_Config__mdt config = API_Config__mdt.getInstance('ERP_Endpoint'); String url = config.Endpoint_URL__c; // Option 2: SOQL (still no governor limit cost) API_Config__mdt config2 = [SELECT Endpoint_URL__c FROM API_Config__mdt WHERE DeveloperName = 'ERP_Endpoint' LIMIT 1];
XYZ Company stores feature-flag-style toggles as Custom Metadata records (e.g., "IsNewPricingEngineEnabled") that a trigger handler checks on every record in a bulk transaction — because the read is free of SOQL limit cost, checking it 200 times in a bulkified loop costs exactly the same as checking it once.
🎤 One-Line Answer
"Access via API_Config__mdt.getInstance('RecordName') or a direct SOQL query on the __mdt object — either way, it's cached by the platform so it never counts against the SOQL governor limit."

🎯 Section 9 — Scenario-Based & Miscellaneous

Q67–Q75 · Real Capgemini Interview Scenarios

Q67
What is the difference between a Custom Setting and a Custom Metadata Type?
✅ Direct Answer
Custom Metadata Type (__mdt): deploys WITH code/packages automatically, cached by the platform (no SOQL limit cost), usable directly in formulas, but does NOT support per-user/per-profile hierarchy overrides. Custom Setting (__c): does NOT deploy automatically with metadata (needs a separate data load per org/sandbox), counts against the SOQL limit unless it's specifically a List Custom Setting (which IS fully cached), but DOES support the Hierarchy type for genuine per-profile or per-user configuration overrides.
💡 Why?
The deployment behavior difference is the practical deciding factor in most real projects — Custom Metadata deploying automatically with a package means your integration endpoints, feature flags, and mapping tables travel seamlessly through your CI/CD pipeline into every sandbox and production with zero manual post-deployment steps. Custom Settings requiring a separate data load per environment is a genuine operational burden that teams frequently forget, leading to "works in sandbox, broken in production because nobody loaded the data" incidents — which is exactly why Salesforce's current guidance defaults to Custom Metadata unless you specifically need Hierarchy-type per-user overrides.
🌍 Real World Example
XYZ Company originally stored API endpoint URLs in Custom Settings — every sandbox refresh wiped that data, requiring a manual re-entry step that was regularly forgotten, causing integration failures that took hours to diagnose (the code was fine; the config data was just missing). Migrating to Custom Metadata Types meant the endpoints now deploy automatically with every package, eliminating that entire class of post-refresh incident permanently.
🎤 One-Line Answer
"Custom Metadata deploys with code and is always cache-free of SOQL limits — use for config that must travel between environments. Custom Settings support Hierarchy (per-user/profile) overrides but need separate data loads per org."
Q68
Have you worked on Salesforce Integration? What patterns have you used?
✅ Direct Answer
Frame your answer around real patterns you've actually implemented: REST callouts via Named Credentials for outbound sync to external systems, Apex REST endpoints (@RestResource) for inbound calls FROM external systems into Salesforce, Platform Events for decoupled asynchronous notifications between systems, and Batch Apex combined with Bulk API for scheduled high-volume data synchronization. Mention specific tools if genuinely used: MuleSoft as a middleware layer, Postman for callout testing during development, or a specific ERP/CRM system you've integrated with.
💡 Why?
Interviewers ask this open-endedly specifically to see whether you can articulate the REASONING behind pattern choice, not just recite a list — a strong answer naturally connects each pattern to WHY it was the right fit (e.g., "we used Platform Events instead of a direct synchronous callout because the external warehouse system's uptime wasn't guaranteed, and we didn't want a slow/down external system blocking our order-creation transaction").
🌍 Real World Example
At XYZ Company, I built the outbound sync from Salesforce Orders to their SAP ERP using Named Credential-based REST callouts triggered from a Queueable (to avoid the mixed-DML/callout-after-DML restriction), and separately built an inbound @RestResource endpoint that let their warehouse management system push shipment-status updates back into Salesforce, secured behind a Connected App with a scoped integration user.
🎤 One-Line Answer
"Yes — outbound REST callouts via Named Credentials, inbound @RestResource endpoints for external systems calling in, Platform Events for decoupled notifications, and Batch Apex/Bulk API for scheduled high-volume syncs."
Q69
Have you worked on Flows? What's your experience building them?
✅ Direct Answer
Frame your answer around specific flow types you've built: Record-Triggered Flows for automated field updates and related record creation, Screen Flows for guided data-entry wizards, Scheduled Flows for periodic cleanup or notification tasks, and calling Invocable Apex from Flow whenever a requirement exceeded what declarative logic alone could express. Mention your debugging approach too — Flow Builder's built-in Debug button for step-through testing, and always connecting Fault paths so failures are handled gracefully rather than silently.
💡 Why?
Mentioning Fault path connection specifically is a strong signal in this answer, because it distinguishes "I've clicked through Flow Builder a few times" from "I've built production-grade Flows" — an experienced Flow builder always treats error handling as non-negotiable, since an unconnected fault path means a Flow failure surfaces to the end user as a raw, confusing technical error message rather than a graceful, logged, and alerted failure.
🌍 Real World Example
At XYZ Company, I built a Record-Triggered Flow that creates related onboarding Tasks when an Opportunity closes — with a Fault path that logs the failure to a custom Flow_Error_Log__c object and sends an admin alert, so a downstream failure (like a validation rule blocking the Task creation) is caught and reported rather than silently failing or exposing a raw error to the sales rep.
🎤 One-Line Answer
"Yes — built Record-Triggered Flows for automation, Screen Flows for guided processes, Scheduled Flows for periodic jobs, and called Invocable Apex from Flow when requirements exceeded declarative logic, always with fault paths connected."
Q70
SCENARIO: Explain the Salesforce security model end-to-end, as you'd describe it in an interview.
✅ Direct Answer
There are two parallel, independent tracks that must BOTH be satisfied for a user to see and act on a record: Track 1 (Record-level): Organization-Wide Defaults (OWD, the restrictive baseline) → Role Hierarchy (managers inherit subordinates' access) → Sharing Rules, owner-based or criteria-based (extend access to groups/roles beyond OWD) → Manual Sharing (individual record grants by the owner). Track 2 (Object/Field-level): Profile — object CRUD + field-level security (FLS) + system permissions, exactly one mandatory per user → Permission Sets/Permission Set Groups — additional object/field/system access layered on top of the profile, purely additive.
💡 Why?
The single most important insight to communicate in this answer is that these two tracks are genuinely INDEPENDENT — a user could have full CRUD permission on the Opportunity object via their Profile (Track 2 satisfied) but still see zero Opportunity records if OWD is Private and no sharing mechanism grants them access (Track 1 not satisfied), or vice versa. Interviewers specifically listen for whether you present this as one unified system or correctly separate it into these two parallel, AND-gated tracks.
🎤 One-Line Answer
"Two parallel tracks: record-level (OWD → Role Hierarchy → Sharing Rules → Manual Sharing, each only adding access) and object/field-level (Profile as mandatory baseline + Permission Sets/Groups adding more) — both must be satisfied for a user to see and act on a record."
Q71
Introduce yourself and walk through your Salesforce project experience.
✅ Direct Answer
Structure this as: years of experience + core technical stack (Apex, LWC, Flow, Integration) → 1-2 flagship projects with your SPECIFIC contribution named explicitly (not just "worked on the team") → one concrete technical challenge you personally solved and its measurable outcome → what you're currently focused on learning or improving. Keep the whole answer under roughly 90 seconds — interviewers want a clear signal of depth in the opening minute, not a full resume readout.
💡 Why?
This "warm-up" question sets the tone for the entire interview and interviewers form real judgments from it — a vague answer ("I worked on a CRM project doing various tasks") signals unclear ownership and often prompts more skeptical follow-up questions throughout the rest of the interview, while a specific, structured answer ("I owned the Order-to-Cash automation, specifically the Batch Apex reconciliation job that cut a manual 4-hour daily process to 15 minutes") immediately establishes credibility and often shapes which topics the interviewer chooses to dig into next.
🎤 One-Line Answer
"Lead with years + stack, name 1-2 projects with YOUR specific contribution, close with one concrete technical challenge you solved and its measurable outcome — keep the whole answer under 90 seconds."
Q72
Walk me through a specific project you worked on — architecture, your role, and challenges faced.
✅ Direct Answer
Use a STAR-like structure adapted for Salesforce specifics: Situation (the underlying business problem, e.g., "manual order entry was causing frequent pricing errors"), Task (your specific ownership area within the broader project), Action (the technical solution you built — the object model, automation choice, or integration pattern — AND critically, the WHY behind choosing it over the alternatives you considered), Result (a measurable outcome — time saved, error rate reduced, adoption rate achieved).
💡 Why?
The "WHY behind each technical decision" component is what most candidates skip and what actually differentiates a strong answer — interviewers have heard hundreds of candidates describe WHAT they built, but explaining the reasoning ("I chose Batch Apex over a Record-Triggered Flow specifically because we needed to process 2 million historical records that wouldn't fit within a single transaction's governor limits") demonstrates genuine architectural judgment rather than just implementation ability, which is exactly what separates a senior-level answer from a junior one.
🎤 One-Line Answer
"Use Situation → Task → Action (with the WHY behind each technical decision) → Result with a measurable outcome — interviewers remember the reasoning behind your choices more than the choices themselves."
Q73
SCENARIO: Data security — two users with the exact same profile see different data. Walk through your full debugging approach.
✅ Direct Answer
Systematic order: 1) Confirm the Profile is genuinely identical (double-check by inspecting the actual Profile assignment, not just its display name, since profile names can be misleading). 2) Compare Role position — even with an identical profile, a different position in the Role Hierarchy changes visibility. 3) Compare Permission Set / Permission Set Group assignments — a single extra "View All" grant on one user explains the entire discrepancy instantly. 4) Check Public Group / Queue membership differences that would affect which sharing rules apply. 5) Check for manual sharing granted specifically on individual records. 6) Confirm your diagnosis with Setup → Users → "Why can't I see this?" diagnostic tool, or by using Login As to reproduce the exact experience of the more-restricted user.
💡 Why?
This scenario is deliberately designed to expose whether you understand that "same Profile" is a red herring that most candidates get stuck on — the natural (and wrong) instinct is to keep re-checking the Profile itself when the profile has already been confirmed identical, when the actual answer always lies in the completely separate record-visibility track (Role, Groups, Sharing, Manual Sharing) that has nothing to do with Profile at all. A strong answer moves past the Profile confirmation quickly and systematically works through the record-level factors instead.
🎤 One-Line Answer
"Same Profile ≠ same visibility — systematically check Role position, Permission Set/Group differences, Public Group/Queue membership, and manual sharing, then confirm with Login As or the 'Why can't I see this?' diagnostic tool."
Q74
How do you achieve field-level security via Apex apart from declarative FLS settings?
✅ Direct Answer
WITH SECURITY_ENFORCED appended to SOQL (throws an exception if the running user lacks FLS/CRUD access on any selected field — appropriate for internal admin tools where a hard failure is acceptable), Security.stripInaccessible(AccessType, records) (removes inaccessible fields silently without throwing — safer and more graceful for partial-access, customer-facing UX), or the newer WITH USER_MODE (enforces sharing + FLS + CRUD together in a single query, effectively unifying what previously required combining 'with sharing' declarations with separate FLS enforcement mechanisms).
🎤 One-Line Answer
"WITH SECURITY_ENFORCED (throws on violation), Security.stripInaccessible() (silently strips inaccessible fields), or the newer WITH USER_MODE which enforces sharing + FLS + CRUD together in a single query."
Q75
What are the best practices you follow while writing Apex test classes?
✅ Direct Answer
Minimum 75% org-wide coverage requirement (personally target 85%+ per class), always use meaningful System.assert messages that explain WHAT was expected and why, wrap the specific unit under test in Test.startTest()/stopTest() for fresh governor limits and to force async operations to complete before assertions, use a TestDataFactory pattern instead of duplicating record-creation boilerplate across every test method, never use SeeAllData=true (it couples tests to unpredictable org data), test both positive AND negative/exception paths explicitly, and always mock HTTP callouts with Test.setMock() rather than letting tests fail on real network calls.
🎤 One-Line Answer
"75%+ coverage with meaningful assertions, Test.startTest()/stopTest() around the unit under test, a TestDataFactory for reusable setup, never SeeAllData=true, test both success AND failure paths, and mock all callouts with Test.setMock()."
🚀 Bookmark sfinterviewpro.com
1,500+ free Salesforce interview questions across 25+ topics. No paywall. No signup. Updated regularly with new company-specific posts.
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