Scenario rounds give you a broken org and watch how you think. A rep can't see a field her colleague sees, a nightly job quietly stopped, a sandbox sent test orders to a live partner, a portal customer can see another company's case. There is rarely one right answer. The interviewer wants the order you check things in, what you would look at first and what would change your mind. This page is for admins and developers, from a first Salesforce job to a senior platform role. Each question shows what is being tested, the shape of a good answer and a sample that thinks out loud. Practice saying your first three checks before the fix.
Search all questions by round, difficulty and level, or save the ones you want to practice.
Permission sets: the rep who sees it may have a permission set or group granting read access to the field.
Layout: page layouts are assigned by profile and record type, so a different record type can mean a different layout.
Lightning page: a Dynamic Forms field or section can have a visibility rule that hides it for some users or values.
Confirm: check field accessibility for the field, then compare the two users' assignments side by side.
“Same profile doesn't mean same access, so I'd compare the two users rather than guess. First, permission sets and permission set groups: the rep who sees the field might have one that grants read on Discount, and field-level security is the sum of the profile and every permission set. Second, the record: if the two opportunities have different record types, they can get different page layouts even on one profile, and the field might only be on one layout. Third, if the page uses Dynamic Forms, a visibility rule could hide the field based on a value or the user. I'd open the field's accessibility view to see where it's visible, then look at both user records. If it turns out to be a missing permission, I'd fix it with a permission set, not by editing the profile.”
Adding the field to the profile's layout straight away without checking whether field-level security or the record type is the real cause.
Object vs record: edit on the object says she may edit opportunities, not this one; record access comes from sharing.
Record access: use the sharing hierarchy on the record to see if she has read-only or no edit access and why.
Locks: a record in a pending approval is locked; only admins, and the approver if the process allows it, can edit.
Fix the right layer: share the record properly or change the process, not her permissions.
“The permission set only answers whether she can edit opportunities in general. Whether she can edit this deal is record access, which comes from ownership, the role hierarchy, sharing rules, teams and manual shares. So first I'd open the record's sharing hierarchy and look her up. It shows whether she has read-only or edit access and the reason. Often she's only got read through a sharing rule, or she's on the opportunity team as read-only. Second, I'd check whether the deal is in an approval. A pending approval locks the record, so only admins, and the current approver if the process allows it, can edit it. If it's sharing, I'd ask whether she should have edit on this kind of deal and fix it with a rule or a team role, not a one-off share. If it's an approval lock, that's working as designed.”
Adding Modify All on Opportunity to her permission set, which gives her far more than this one deal.
Cause: the dashboard runs as a fixed user, so every viewer sees data through that person's access.
Risk: it bypasses the sharing model the business chose; say plainly what reps can now see.
Options: run as the logged-in viewer, split into team and leadership dashboards, or restrict the folder.
Decision: put the choice to the data owner in writing, not only the director.
“The cause is almost certainly the dashboard's running user. It's set to run as a specific person, probably the director or an admin, so everyone who opens it sees data through that person's access. Drilling into the source report would still respect each rep's own sharing, but the charts don't. I'd take the director's view seriously, since sometimes a team leaderboard is meant to be public. But I'd name what's exposed: other teams' pipeline, deal names, maybe customer amounts. If the org made opportunities private on purpose, this quietly undoes it. I'd offer options: set the dashboard to run as the logged-in user, keeping in mind there's a limit on how many of those an org can have, or make a rep-facing dashboard showing totals only and keep the detailed one in a leadership folder. Whoever owns the security model gets the final say, and I'd record it.”
Either leaving it because a director said so, or switching it silently and breaking the view leadership relies on.
Contain: confirm by viewing as that portal user, raise it as an incident, and cut the access even if that limits the portal.
External sharing: check the external default for Case, sharing rules aimed at all portal users, and sharing set mappings.
Code: check custom components on the portal whose Apex runs without sharing or ignores user mode.
After: find who else saw what, tell the owners per company policy, and add access tests to the release.
“This is a data exposure, so I'd treat it as an incident from the first minute and tell our security or privacy contact. I'd confirm it by logging in as that portal user and seeing exactly what she sees. Then I'd work through the layers. The external default for Case should be private. Next, any sharing rule that shares cases with all portal users, which is an easy mistake. Then the sharing set: it should match the case's account to the user's account, not something broader. And custom pages. If a portal component calls Apex that runs without sharing, it can show records the sharing model would hide, whatever the settings say. Once I find it, I'd close it even if that breaks a portal feature for a while. Then work out how many customers could see what, and how long, and hand that to whoever handles notification. Last, I'd add a test that logs in as two customers and checks each sees only their own cases.”
Quietly fixing a setting and replying to the customer without treating it as a security incident.
Cause: users need Apex class access to call a class from a component; admins have permissions that let them run any class.
Fix: grant the class through a permission set assigned to the users who use the component.
Next check: once it runs, confirm the class respects sharing and field access so users see only what they should.
Prevent: test as a real user before release, and ship the permission set with the component.
“That's the classic admin-only test. When a component calls an Apex method, the user needs access to that Apex class, and admins have permissions that let them run any class, so it looked fine for me. The fix is to grant the class in a permission set and assign it to the sales users, and to add that permission set to the same deployment as the component next time. I wouldn't stop there, though. Once users can run the class, I'd check what it returns. If it's declared without sharing, or queries fields without user-mode checks, users might now see records or fields they shouldn't. So I'd make sure it uses with sharing and user-mode queries. And I'd make testing as a real sales user, with login-as, a standard step before any release.”
Solving it by giving sales users broad admin-level permissions so the error goes away.
Cause: a cacheable Apex method called with @wire returns cached data, and nothing tells it the case changed.
Own changes: if the component closes the case, call refreshApex on the wired result afterwards.
Imperative updates: after changing records through Apex, notify Lightning Data Service so other components update.
Changes elsewhere: prefer Lightning Data Service adapters, or add an explicit refresh, rather than polling.
“The Apex method is marked cacheable and called with @wire, so the component gets a cached result. Lightning Data Service keeps standard components in step when a record changes, but my Apex result isn't part of that, so it just keeps the old list. The fix depends on who makes the change. If my component closes the case, I keep a reference to the wired result and call refreshApex after the update succeeds. If I update records through imperative Apex, I also call notifyRecordUpdateAvailable with the record Ids so standard components pick up the change. If the change happens elsewhere on the page, my component can't know about it, so I'd look at using a Lightning Data Service adapter, which shares the cache with standard components, or at least a refresh action. I'd avoid polling on a timer, because it adds server calls for every open page.”
wiredCases;
@wire(getOpenCases, { accountId: '$recordId' })
handleCases(result) {
this.wiredCases = result;
if (result.data) this.cases = result.data;
}
async closeCase(caseId) {
await closeCaseApex({ caseId });
await refreshApex(this.wiredCases);
}
Forcing a full page reload with window.location after every change.
Measure: run the page performance analysis in Lightning App Builder and watch the browser's network calls.
Data growth: nothing deployed but data grew; related lists, rollups and component queries over big child sets slow down.
Components: count what loads up front; move heavy ones into tabs that load when opened.
Apex calls: make read methods cacheable, combine calls, and fix queries that got slower with volume.
“Nothing deployed doesn't mean nothing changed. Data grows, and admins can edit a Lightning page without a deployment. So I'd check the page's change history, then measure. Lightning App Builder has a performance analysis for the page, and the browser's network tab shows which server calls are slow. Usually it's one of three things. First, data volume: key accounts may now have thousands of cases or contacts, so related lists and any component querying children get slow. Second, too much loading up front, like many components each making their own Apex call. I'd move heavy ones into tabs, which load when clicked. Third, a specific Apex method with a query that doesn't use a selective filter any more. I'd confirm with a big account versus a small one: if only big accounts are slow, it's data volume.”
Asking Salesforce support to speed up the org before measuring what the page actually loads.
Sharing: a report only shows the records the person running it can see.
Scope filter: check whether the report is scoped to my records, my team's or all visible records.
User settings: currency and time zone can shift amounts and relative date filters.
Prove it: compare record counts, or view the report as each user, then explain it.
“Most likely they're both right. A report runs as the person viewing it, so it only includes records each manager can see. If they sit in different branches of the role hierarchy, or one is in a public group a sharing rule targets, their totals will differ. Second, I'd look at the report's scope filter. If it says my team's opportunities, each manager gets their own team. Third, personal settings: with more than one currency the amount displays in each user's currency, and a relative filter like this month uses their time zone near the edges. I'd confirm by comparing record counts, or logging in as each of them. Then I'd explain it plainly, and if the business wants one agreed number for everyone, that's a dashboard with a chosen running user or a report scoped to all opportunities.”
Telling one manager their report is broken without checking whose access each report runs under.
Root cause: ask why reps create them; with private accounts they often can't see the existing one.
Prevent: matching and duplicate rules on Account, starting with an alert and moving to block once tuned.
Visibility: decide whether the rule should detect records the rep can't see, and what the rep does then.
Clean up: report on existing duplicates, merge them with the owners, and fix pipeline numbers.
“Before switching anything on, I'd ask why it's happening. Very often accounts are private, so a rep searches, finds nothing, and creates a new one. A block alone would just stop them working. So the plan has three parts. First, matching rules on name, website and billing address, with a duplicate rule that alerts at first, not blocks. I'd watch what it flags for a couple of weeks to tune it, then move to blocking on strong matches. The duplicate rule can detect accounts the rep can't see, so I'd agree what they do then, like requesting access from the owner. Second, the existing mess: I'd use a duplicate report, agree with account owners which record survives, and merge, since Salesforce merges up to three accounts at a time. Third, opportunities move to the surviving account, so duplicate deals end up side by side, and owners close the extra one to fix the pipeline.”
Turning on a hard block on day one with a loose matching rule, so reps can't save real new customers.
Read the error: find which object and which rule or field is failing.
Recent change: check setup audit trail for a new validation rule, required field or automation on Contact.
Field mapping: a custom lead field only reaches the contact if it's mapped in lead field mapping.
Fix: add the lead field and mapping, or adjust the rule to allow conversion.
“Conversion creates an account, a contact and maybe an opportunity. If lead settings require validation for converted leads, those new records must pass their own validation rules and required fields, and triggers and flows run either way. So I'd start with the exact error: which object, which rule. Since it's sudden, I'd check setup audit trail for something new on Contact, like a validation rule requiring a field. Then I'd ask whether the lead even has that data. If there's a custom field on the lead holding it, it only reaches the contact if it's mapped in lead field mapping, and new fields are often not mapped. So the fix is either to add and map the field, or, if the rule makes sense for reps creating contacts by hand but not for conversion, to adjust it. I'd also check triggers and flows on Contact, because their errors show up in the conversion screen too.”
Deactivating the new validation rule without finding out who added it and why.
Lock access: freeze the user today so she can't log in while you work.
Agree the split: get the rule from the manager: which accounts go to whom, and what happens to closed deals and open tasks.
Move records: mass transfer accounts with their open deals, and Data Loader for leads and other objects.
Hidden references: assignment rules, queues, approval steps, default owners, flows and scheduled jobs that name her.
“First, I'd freeze her user, so she can't log in but nothing breaks while I reassign. Then I'd get the rule from the manager in writing: which accounts go to which rep, maybe by territory, and whether closed deals move or stay with her for history and commission reports. Usually open deals, open cases and open activities move with the account, and closed ones stay. For accounts I'd use mass transfer with the option to move open opportunities, and Data Loader for leads and anything else, testing a small batch first. The part people miss is everywhere she's referenced: lead and case assignment rules, queue membership, approval steps with her as approver, a default record owner, flows that email her, dashboards running as her, and jobs she scheduled. Only after all that would I deactivate her.”
Deactivating her first and discovering later that approvals, assignment rules or scheduled jobs pointed at her.
Who can approve: agree with the business who may approve in her absence; that's a policy call, not yours.
Today: reassign the pending approvals to that person, or have her delegated approver act.
Delegate: a delegated approver on her user record can act if the step allows it.
Design fix: route to a queue or a backup approver so absences don't block deals.
“I wouldn't approve anything myself or switch the process off, since the approval is a control someone put there on purpose. First I'd ask the sales leadership or finance who's allowed to approve while she's away, maybe another director or her manager. For today, I can reassign the pending approval requests to that person, and they can clear the queue. If the approval step allows delegates, I'd set that person as the delegated approver on her user record for the two weeks, so new requests don't get stuck either. Then I'd fix the design. A single named approver is fragile, so I'd suggest routing to a queue of directors or adding a backup approver, and a reminder so pending requests don't sit for days. That way leave, travel or someone leaving the company doesn't stall deals.”
Recalling the approvals or removing the approval step to get the deals through.
Get the detail: the flow error email names the element and the error; check who receives those emails.
Reproduce: debug the flow with the same record and the same kind of user.
Common causes: a Get Records step that found nothing, an update the user can't make, or a rule on a related record.
Harden: add null checks and fault paths that log the error and show a clear message.
“First, the real error. That screen message is generic, but Salesforce sends a flow error email with the element that failed and the underlying message. It goes to the last person who modified the flow or to the Apex exception recipients, depending on the automation settings, so I'd check who's getting it. Then I'd reproduce it by debugging the flow with one of the failing cases. My tests probably passed because my test data was tidy and I'm an admin. Common causes are a Get Records element that returned nothing and a later step using that empty value, an update the user doesn't have access to, or a validation rule on a related record the flow updates. Once fixed, I'd add a decision after each lookup to handle no result, and fault paths that log the error and show a message users can act on.”
Deactivating the flow in production without reading the error or telling the business what stops working.
Scheduled Jobs: is the job still there, and when is its next run?
Apex Jobs: did recent runs fail, abort or never start?
Owner: scheduled Apex runs as the user who scheduled it; a deactivated owner stops it.
Prevent: schedule under a dedicated automation user and alert when a run is missed or fails.
“I'd start in Scheduled Jobs to see whether the job exists at all and when it's next due. If it's gone, someone may have deleted it, perhaps to push a deployment through, since changing a class with a pending job can be blocked. If it's there, I'd go to Apex Jobs to see whether the runs failed or aborted, and read the error. A common cause is ownership: scheduled Apex runs as the user who scheduled it, so if that admin left and was deactivated, the job stops. For the fix, I'd reschedule it under a dedicated automation user that won't leave, and add a check, like the finish method writing a log record and a small scheduled check that alerts the team if no successful run happened in the last day. Silent failure is the real bug here.”
Just rescheduling the job under your own user, which sets up the same failure for when you leave.
Scope first: check whether the start query even selected those records.
Partial success: Database.update with allOrNone false saves what it can and returns errors instead of throwing.
Batch status: failures on the Apex Jobs page count chunks that threw, not rows that failed inside a chunk.
Fix: read every SaveResult, log failures, and report a summary in the finish method.
“Two possibilities. Either the job never selected those records, or it tried and failed quietly. So first I'd run the start query myself and check whether the missing records match it. Filters on dates or status are often the culprit. If they do match, I'd look at the DML. If the code uses Database.update with allOrNone set to false, any record that fails a validation rule or hits a lock is skipped, and the error comes back in a SaveResult instead of an exception. If nobody reads those results, the batch shows Completed with no failures, because that count is about chunks that threw, not rows. The fix is to loop through the results, collect each failed Id and message, write them to a log object, and have the finish method send a summary.”
List<Database.SaveResult> results = Database.update(scope, false);
for (Integer i = 0; i < results.size(); i++) {
if (!results[i].isSuccess()) {
for (Database.Error err : results[i].getErrors()) {
failures.add(scope[i].Id + ': ' + err.getMessage());
}
}
}
Rerunning the batch and hoping, without finding out why the records were skipped.
Contain: cut the sandbox off from the partner now by changing the endpoint and pausing the jobs sending data.
Clean up: list what was sent and work with the partner to void it; tell your own team.
Cause: the refresh copied production configuration, including the live endpoint and credentials.
Prevent: one place per environment for endpoints, plus a post-refresh checklist that repoints them.
“Right now, stop the bleeding. I'd point the sandbox's integration at the partner's test endpoint or a dummy one, and pause any scheduled jobs or automation that send orders, so nothing else goes out. Then I'd pull the list of records sent since the refresh and give it to the partner so they can void them, and tell our finance and support teams in case customers are affected. The cause is that a refresh copies production's configuration, so the sandbox woke up with the live endpoint and credentials. Afterwards, I'd make sure endpoints live in named credentials, not in code or custom settings scattered around, so there's one place to change. And I'd write a post-refresh checklist that repoints integrations before anyone uses the sandbox, automated where we can with a post-copy class. Sandbox email is set to system only after a refresh, but integrations get no such safety net.”
Starting with who refreshed the sandbox instead of stopping the orders that are still going out.
Now: put the admin's fix back in production so users can work.
Capture: retrieve that change into source control so the next release keeps it.
Why: production and the repository drifted because a change skipped the pipeline.
Prevent: a fast, documented hotfix path, regular drift checks, and limited setup rights in production.
“First, users are blocked, so I'd reapply the admin's fix in production right away, after checking with them exactly what they changed. Then I'd retrieve that rule from production into source control and commit it, so the next release doesn't undo it again. The real issue isn't the admin. Production and the repository drifted because there was a change that didn't go through the pipeline, probably because the pipeline felt too slow for an urgent fix. So I'd give people a fast hotfix path that still goes through source control, even if it's a same-day deploy. I'd also add a regular check that retrieves production metadata and compares it with the main branch, so drift shows up before a release, and reduce who has rights to change setup in production. A short note to the team on why matters more than a new rule.”
Blaming the admin and adding a rule, without fixing the slow pipeline that made them work around it.
Clarify: they need a stored number for list views and filters, so a report or related list isn't enough.
Events: the count changes on create, delete, undelete, status change and when a case moves to another contact.
Build: recount with an aggregate query per affected contact, from a trigger or record-triggered flows.
Safety net: a scheduled job that recalculates and fixes drift.
“First I'd confirm the need: filtering list views means it has to be a real field, so a report won't do. Since Case looks up to Contact, there's no roll-up summary, so I'd maintain a number field. The trap is thinking only about new cases. The count changes when a case is created, closed, reopened, deleted, undeleted, or moved from one contact to another, and in that last case both the old and new contact need updating. I'd rather recount than add or subtract, so the number fixes itself: collect every affected contact Id, run one aggregate query counting open cases grouped by contact, and update those contacts, all bulk-safe. That could be an Apex trigger, or record-triggered flows if the team is admin-led, though delete and undelete are easier in Apex. And I'd add a nightly job that recalculates, because data loads with automation bypassed will leave it wrong.”
Adding one to the count on case creation and forgetting closes, deletes and cases moved between contacts.
Understand: find out why each team's process differs; they may both be right.
Configure: keep one picklist, and use sales processes with record types so each team sees only its stages.
Shared settings: probability and forecast category belong to each stage value, so agree those together.
Reporting: agree how leadership compares both pipelines before going live.
“I'd start by sitting with both teams, because they're probably both right: winning a new customer and renewing an existing one are different processes. Salesforce handles that without custom code. The Stage field stays one picklist with all the values, and I create two sales processes, one for new business with its six stages and one for renewals with its four, each tied to a record type. Each team then only sees its own stages. The part to agree together is that probability and forecast category are set on each stage value, not per team, so if both share a stage name it has to mean the same thing for forecasting. I'd also check with leadership how they want to see both pipelines together, because reports and dashboards may need grouping by record type. Then pilot it with a few reps from each team.”
Creating a second custom stage field for renewals, which breaks forecasting and standard pipeline reports.
Map and clean: map objects and fields, agree what not to bring, and clean data at the source.
Load order: users first, then accounts, contacts, opportunities and activities, linking with legacy Ids in external Id fields.
Protect the load: bypass automation for the migration user, use the Bulk API, and keep original created dates and owners where allowed.
Rehearse: full trial loads into a full sandbox, reconcile counts and samples, then a cutover plan with a final delta.
“I'd spend the first two weeks on mapping and cleaning, not loading. Map every object and field, agree with the business what's not worth bringing, and fix duplicates and missing owners in the old system. Then load order matters: users first so owners resolve, then accounts, contacts, opportunities, and activities last since they point at everything else. I'd put each record's old Id in an external Id field and upsert, so children link to parents by that Id without lookup tables. For the load itself, a migration user with automation bypassed through a custom permission, the Bulk API, and the permission to set audit fields so original created dates and created-by users come across. Owners map by user, and inactive owners need a decision too. Weeks three to five are rehearsals in a full sandbox: load, reconcile counts, have users spot-check records. Cutover is a freeze on the old system, a final delta, then turning automation back on.”
Loading straight into production with automation running and hoping to fix relationships afterwards.
Cause: saving a child locks its parent; parallel batches touching children of the same account wait on the same lock and time out.
Why nightly: the night file is big and often includes a few accounts with huge numbers of contacts.
Fixes: sort by parent Id so one parent stays in one batch, try serial mode or smaller batches, and retry failed rows.
Also check: other jobs running at the same time and automation that updates the parent.
“UNABLE_TO_LOCK_ROW means a transaction waited too long for a lock another one was holding. When you insert or update a contact, Salesforce locks the parent account for a moment. The Bulk API runs batches in parallel, so if two batches both contain contacts of the same account, one waits and can time out. It shows up at night because that file is large, and a few accounts probably have thousands of contacts, which is data skew. My first fix is cheap: sort the file by account Id so each account's contacts land in the same batch. If that's not enough, smaller batches or serial mode, which is slower but safe. And the integration should retry failed rows. I'd also look at what else runs at that time, like a batch job updating accounts, and any trigger or flow on Contact that updates the account.”
Blaming the Bulk API and asking for longer timeouts instead of looking at how rows are grouped.
Meaning: in a trigger, a query on an object with a very large number of rows must use a selective, indexed filter.
Why the sandbox passed: it had far less data, so the rule never applied.
Find it: check the filter with the Query Plan tool against production-sized data.
Fix: filter on an indexed field, avoid negative and null filters, narrow the scope, or ask for a custom index.
“Salesforce protects shared resources, so in a trigger, a query against an object with more than a couple of hundred thousand rows has to be selective, meaning it uses an index to narrow the rows. Otherwise it throws that error. The sandbox didn't have enough data to hit it. I'd find the query and look at its filter. Common problems are filters like status not equal to closed, or a field equals null, which usually can't use an index, or a filter on a plain custom field with no index. I'd check it in the Query Plan tool in the Developer Console against a full sandbox. Then I'd rewrite it to lead with an indexed field, like the Ids from the trigger, a lookup, or an external Id, or ask support for a custom index. Longer term, performance tests need production-like data.”
Wrapping the query in a try-catch so the trigger stops failing, or moving it to async without making it selective.
Cause: ownership skew; one owner of a huge number of records means a role change forces sharing to recalculate for all of them.
Symptoms: long recalculation and lock contention on the sharing tables, so ordinary saves fail or wait.
Right setup: give the bulk owner no role, or a role at the top of the hierarchy, and spread ownership where people need it.
Process: make big sharing changes out of hours and plan them.
“That's ownership skew. When one user owns a huge number of records, their place in the role hierarchy decides who inherits access to all of them. Moving that user means Salesforce has to recalculate sharing for every one of those accounts and for everyone above the old and new role. That runs in the background, takes hours, and locks the sharing tables, so ordinary saves fail or time out. The standard setup for an integration or holding user that owns lots of records is no role at all, if nobody needs access through the hierarchy, or a role right at the top, so there's little to recalculate. And where people do need access, I'd rather assign real owners or use sharing rules. For the future, I'd make sure role and sharing changes to big owners happen out of hours, and ask support about deferring sharing calculations for large planned changes.”
Treating it as a random platform slowdown and repeating the same change next week.
ClapAssist is an AI interview assistant for Mac and Windows. It listens to the interview on your computer and shows you what to say, in short lines you can read while you talk. Your live interview audio and screen are never stored. Your resume and notes are saved to your account so the app fills them in on any computer. It stays out of screen share on every plan, including Free; only you can see it.