35 Senior Salesforce Developer Interview Scenarios (Post 1)
35 Senior Salesforce Developer Scenarios
Post 1: Production Debugging, Trigger Logic, Batch Failures & Integration Errors
What's inside: Real production Salesforce developer scenarios for 6+ year engineers. Not "what is a trigger" — but "your batch failed at record 300,000, how do you make it resumable?" and "reports on 8M row objects are timing out, walk me through your fix."
Each of the 35 questions follows our signature 7-step interview format: direct answer, root cause, code example (problem + solution), debugging workflow, Salesforce config, real-world XYZ Company example, key interviewer points, and a one-liner you can say directly in the room.
Post 1 covers 4 categories:
• 🔍 Production Debugging & Performance (Q1–10)
• ⚡ Trigger Logic & Data Integrity (Q11–20)
• ⏳ Batch & Async Processing (Q21–28)
• 🔗 Integration Errors & Recovery (Q29–35)
Pair this with our free courses: Agentforce 15-Module Course • Data Cloud 15-Module Course • LWC Zero to Hero • Salesforce Admin M0–M15 • Practice Zone (27 Quizzes, 2,244 MCQs)
Production Debugging & Performance
10 QuestionsReports on 8M Row Object Timing Out — Walk Me Through Your Fix
Salesforce report queries scan every row unless you apply a filter upfront. On 8M rows, that's an 8M-row SOQL execution scanning the entire dataset. Without a date filter or database index, Salesforce's query optimizer does a full table scan. Query time scales with dataset size: 1M rows = 1 second, 8M rows = 8 seconds. Add the 5-minute (300-second) report timeout, and you need aggressive filtering and indexing to stay under it.
Root cause chain: Unfiltered query → full table scan → scans 8M rows → 5+ minute execution → timeout.
- Identify report timing out: User says "Q1 Revenue Report takes forever." Go to Setup > Reports. Run the report manually. Note how long it takes (likely 4:59 before timeout).
- Check report filters: Edit report > Look at "Filters" section. Is there a required date filter? If report has "All Time" or no date filter, it's querying all 8M rows.
- Run SOQL in Dev Console: Copy the exact query the report runs. Use browser DevTools (Network tab, XHR) to capture the SOQL. Run it in Dev Console to measure actual execution time without report rendering overhead.
- Add required date filter to report: Edit report, add "CreatedDate = THIS_QUARTER" as a REQUIRED filter. Resave report. Run it again — execution time should drop from 4:59 to <30 seconds.
- Create custom index: Go to Setup > Object Manager > Order__c > Indexes. Create new index: Name = "Order_DateRecordType", Fields = [CreatedDate, RecordTypeId]. (Max 10 custom indexes per object.)
- Test report again: Re-run report. Execution time should drop further (index helps even with filtered queries). Monitor report run time over next week — should consistently complete in <10 seconds.
- Archive old data (optional): If report queries 2 years of data and mostly recent data matters, archive orders >2 years old to a separate object or external storage. This reduces active dataset from 8M to 2M rows, dramatically improving performance.
- Report Filters (Setup > Reports > Report Name): Required vs. optional filters. Required filters are applied to every report run and reduce initial dataset before rendering.
- Custom Indexes (Setup > Object Manager > Indexes): Up to 10 custom indexes per object. Include the fields your reports filter by most often. Salesforce optimizer uses indexes to seek instead of scan.
- Report Timeout: 5-minute timeout for synchronous report runs. Scheduled reports have up to 30 minutes but still hit performance issues at 8M rows.
- Database Statistics: Salesforce's query optimizer uses row count and field cardinality to choose indexes. If you create an index and the optimizer doesn't use it, go to Indexes > "Update Statistics" to refresh.
- Data Archival: No built-in archive feature; use platform events or external systems to move old data out of Salesforce. Reduces active dataset, improves all query performance.
XYZ Company has an Order__c object with 8.2M orders (spanning 5 years). Their "Q1 Revenue Report" was timing out every time. Root cause: No date filter on the report, so it queried all 8.2M orders. Query executed in 4+ minutes, timed out at 5 minutes.
Fix applied:
1. Added required "Created Date = THIS_QUARTER" filter to report
2. Created custom index on (CreatedDate, RecordTypeId)
3. Result: Report runs in 8 seconds instead of timing out. Batch jobs using the same query also became 10x faster.
- Salesforce does NOT automatically optimize queries — unfiltered queries scan the entire dataset.
- Date filters are the most effective fix because most queries need "recent" data (this month, quarter, year).
- Indexes are not "set and forget" — verify the optimizer is using them via SOQL analyzer or by checking query plan.
- At 8M+ rows, even indexed queries timeout if scope is too broad — consider archiving to reduce active dataset size.
Batch Job Failed at Record 300,000 — How Do You Make It Resumable?
- Observe batch failure: Run batch. At record 300k, exception throws (governor limit, API timeout, etc.). Batch status = "Failed".
- Check Batch Job History: Setup > Batch Jobs. Note the JobId and failure reason (e.g., "SOQL limit exceeded").
- Check checkpoint storage: Query Custom Metadata table (Checkpoint__mdt). See if LastProcessedId__c is populated. If empty, batch never updated checkpoint (failed before finish() ran).
- Manually update checkpoint: Query Order__c for the 1500th batch's last record ID (around record 300k). Manually update Checkpoint__mdt.LastProcessedId__c = that ID.
- Re-run batch: Batch start() queries records WHERE Id > checkpoint. Batch resumes at record 300,001, skipping first 300k.
- Monitor resume: Check batch logs. Batch should complete without re-processing already-synced records. Verify finish() updates checkpoint with final ID.
- Custom Metadata (Checkpoint__mdt): Non-queryable metadata that persists across batch runs. Store LastProcessedId__c here (config, not data).
- Custom Settings: Alternative to metadata (queryable in SOQL, but slower). Use if you need to query checkpoint frequently.
- Database.Stateful: Retains state within ONE batch execution, not across batch restarts. Can't use for persistence across runs.
- Batch Scope: Default 200 records per batch execute(). Smaller scope = more frequent finish() calls = more checkpoint updates.
XYZ Company: OrderSyncBatch processes 500k orders → ERP. Batches 1-1499 succeed (300k records). Batch 1500 fails (API timeout on external ERP). Without checkpoint, restart reprocesses all 300k orders, causing ERP duplicates and wasting 2 hours.
Fix: Added Checkpoint__mdt table with LastProcessedId__c field. On batch failure at record 300k, checkpoint is at 300,000. On restart, batch queries WHERE Id > 300000, resuming from record 300,001. No duplicates, no wasted processing.
- Salesforce batches do NOT have built-in resumability — you must implement it.
- Storing checkpoint in Custom Metadata is better than Custom Settings (metadata = config, not data storage).
- Salesforce generates record IDs sequentially by timestamp, so ID > X works as a safe resume marker.
- Database.Stateful retains state within ONE batch execution, not across batch restarts — use metadata for persistence.
Scheduled Flow Runs Every 5 Minutes but Keeps Timing Out — Diagnosis?
- Check flow execution history: Setup > Flows > Flow Name > Run History. Note if multiple runs are "In Progress" simultaneously.
- Add locking flag in Custom Setting: Create Flow__c Custom Setting with field IsFlowRunning__c (Boolean).
- In flow: check if IsFlowRunning__c = true. If yes, exit (another run is in progress). If no, set to true, run logic, set to false at end.
- Optimize SOQL: Add filters (WHERE Status = 'Draft' AND LastModifiedDate > LAST_N_DAYS:1) to reduce row count from 500k to 10k.
- Re-run and monitor: Check flow history. Should see no overlapping runs, faster completion time.
- Scheduled flows run independently — if interval is shorter than runtime, they overlap.
- Locking is the first defense: prevent overlapping before optimizing queries.
- Query filters (date range, status) often outperform rewriting logic.
Memory Leak in Batch Job — Heap Size Increases Each Batch. Diagnosis?
- Static variables persist for entire transaction (all batches). Avoid static Lists/Maps for bulk data.
- Use local variables scoped to execute() — garbage collected automatically after method ends.
- Limits.getHeapSize() is your diagnostic tool. Print it to track memory across batches.
- 12 MB heap limit per batch. Static collections can exceed this on large datasets.
Database.getQueryLocator() vs. Iterable<sObject> in Batch Start()?
- getQueryLocator() = streaming cursor, 50M+ rows, no heap pressure.
- Iterable = load-all-at-once, max ~50k records before heap limit (12 MB).
- If processing >10k records from DB, always use getQueryLocator().
SOQL Query Takes 2 Seconds, Report Times Out — Why?
- Query time and render time are separate. 2s query + 4s render = 6s total.
- Grouping/summaries on 100k rows add 2-3 seconds each.
- Filtering (date, status) reduces result set size faster than optimizing render logic.
Visualforce Page: 3 Seconds in Sandbox, 45 Seconds in Production
- Check data size: sandbox has 1k orders, prod has 500k (confirmed via COUNT query).
- Run VF page in both: sandbox = 3s, prod = 45s (15x slower).
- Add LIMIT to SOQL: SELECT ... LIMIT 500 (cap at 500 records).
- Add filter: WHERE CreatedDate = THIS_MONTH (reduces 500k to ~40k).
- Use StandardSetController for pagination: loads 100 records per page, only renders visible page.
- Re-test: prod VF now loads in 3-5s (matches sandbox).
- Always test against production-scale data in sandbox.
- Query performance scales linearly with data: 500x data = 500x slower query time.
- Unfiltered queries work on 10k but fail on 1M records.
API Callout Hangs 90 Seconds in Loop — Fix?
- Sync callout: 120s timeout. Sync loop of 4 callouts will timeout.
- Queueable: 3600s timeout, best for sequential callouts (chainable).
- Future: supports 5 parallel callouts per invocation, no chaining.
LWC Grid Rendering 100k Rows Freezes UI
- 100k DOM elements = 10-30s rendering time, UI frozen.
- Pagination: Standard fix, 50-100 rows per page keeps UI responsive.
- Virtual scrolling: Advanced, render only visible rows dynamically.
Custom Index Exists but SOQL Still Slow — Why Not Used?
- Index field order matters: (A, B) index helps WHERE A=x AND B=y, not WHERE B=y AND A=x.
- Optimizer chooses indexes based on estimated query cost, not existence.
- SOQL Analyzer shows which index is used; "Update Statistics" forces recalculation.
Trigger Logic & Data Integrity
10 QuestionsStatic Boolean Recursion Guard Fails — Why?
- Observe: Trigger logic runs on first insert, doesn't run on second insert (should run both times).
- Check trigger code for static Boolean. If found, that's the culprit.
- Test: Insert order 1 (logic runs). Check log — flag=true. Insert order 2 (logic skipped). Verify flag still=true.
- Fix: Replace Boolean with Set<Id> of processedIds. Each record ID is added to Set after processing.
- Retest: Insert order 1 (added to Set). Insert order 2 (not in Set, logic runs). Set is cleared after transaction ends.
- Static Boolean = process-scoped, persists across transactions (dangerous).
- Static Set<Id> = safer, tracks specific records, cleared after transaction.
- For bulk operations (100 inserts), Set<Id> prevents re-processing same record within transaction.
- Always reset static variables if using in batch (reset in finish() or use non-static pattern).
XYZ Company: OrderTrigger used static Boolean preventRecursion. Order update triggered Account update, which updated Order (two-way loop). First order insert: flag=false, logic ran. After first transaction, flag=true. Second order inserted 5 seconds later (different transaction): flag still=true (persisted from first transaction), logic skipped. Order didn't sync to ERP. Took 2 days to discover flag was persisting across transactions.
Fix: Switched to Set<Id> to track processed Order IDs within single transaction. Set cleared after transaction ended. All subsequent orders processed correctly.
Trigger Modifies Record Inside Loop — Order of Execution Problem?
- Before-trigger: modify Trigger.new before save (changes persist).
- After-trigger: don't modify Trigger.new (changes don't persist). Query and update separately if needed.
- Order of execution: Before Triggers → Validate → Save → After Triggers → Child Records → After-Child Triggers.
Trigger Updates Parent Record Inside After-Trigger — Does Parent Trigger Fire Again?
- After-trigger updates don't re-fire same after-trigger (built-in prevention).
- But child/parent triggers can still fire, creating cascades (one-way loops).
- Document trigger execution flow; it's easy to miss cascade points.
Trigger Queries Data that Just Changed — Dirty Read Risk?
- SOQL in trigger sees data at transaction start, not in-flight changes.
- Prevents dirty reads, but query may miss recent updates within same transaction.
- Use Trigger.new/old for in-flight data, SOQL for stable data outside transaction.
Bulk Insert 1000 Orders — Trigger Processes Them Individually?
- Trigger fires once per DML, receives list of records (up to 200 in batch).
- Bulkify: process list in one query/update, not per record.
- Nested loops (loop in trigger, inside loop in handler) cause O(n²) performance.
Field Update Flow Fires Trigger — How Do You Distinguish?
- Trigger doesn't know if update came from flow vs. API vs. UI.
- Use a flag field (e.g., FlowRun__c) to mark flow updates.
- Trigger checks flag; if true, skip logic (prevent feedback loop).
Validation Rule Fails, But Trigger Still Fires — Why?
- Validation rules fire before triggers (order: Validation → Before-Trigger → Save).
- Validation failure blocks save and trigger doesn't fire.
- Validation can be suppressed via API; trigger may fire with empty/partial data.
Before-Trigger Modifies Trigger.new, But Change Never Persists?
- Before-trigger changes persist if field has no default or formula.
- Default values and formulas recalculate after trigger, overriding trigger changes.
- Check field setup: if Default Value or Formula is set, it takes precedence over trigger.
Trigger on A, Updates B, Updates A — Cascade Causes Data Corruption?
- Two-way cascades create infinite loops (A→B→A→B...).
- Guard BOTH triggers with Set<Id>; prevent same record from reprocessing.
- Document trigger dependencies (A depends on B, B depends on A) in code comments.
Test Class Doesn't Fire Trigger — Missing Permissions?
- @isTest runs as SYSTEM (bypasses FLS, permissions). Trigger should fire.
- If trigger doesn't fire: check if trigger is active (Setup > Triggers).
- @isTest(SeeAllData=true) may skip some triggers on historical data.
Batch & Async Processing
8 QuestionsScheduled Batch Job Queues 2 Copies — Why?
- Scheduled job fires independently of batch status (doesn't check if previous batch is running).
- If batch runtime > schedule interval, batches queue up (overlap).
- Solution: increase schedule interval OR optimize batch runtime.
- Monitor Batch Jobs history; if you see 2 jobs starting close together, confirm overlap.
Queueable Job Fails Silently — No Error Logged?
- Queueable exceptions don't auto-log. You must wrap in try-catch and log manually.
- Queueable never retries (unlike Batch). Log and monitor manually.
- Use custom Error__c object to track all async failures centrally.
Future() Method Limit: 50k per 24 Hours — How to Monitor?
- Future limit: 50k per 24 hours (org-wide, all users).
- Query AsyncApexJob to monitor usage (count JobType='Future' in last 24hr).
- If approaching limit, queue calls in custom table for retry or move to batch.
Batch Chaining in finish() — How Many Times Can You Chain?
- Batch chaining = batch in finish() enqueues next batch.
- No hard limit, but practical limit ~5-10 (delays compound).
- For massive jobs, use single batch with checkpoint resume (faster than 20 chained batches).
Trigger Enqueues Queueable, Then Dequeues If Error — How?
- Queueable is async; trigger can't catch its exceptions directly.
- Store Queueable state in custom object (pending → success/failed).
- Scheduled batch can check failed jobs and retry or alert.
ScheduledAction Apex: Retries on Failure?
- Scheduled Apex = one-time execution per schedule (no retries on failure).
- Log all errors to custom object for manual or automated retry.
- Separate job monitors errors and retries.
Database.executeBatch() vs. Database.executeBatchAsync()?
- executeBatch() = sync (blocks trigger, older).
- executeBatchAsync() = async (returns immediately, newer).
- Use executeBatchAsync() to avoid blocking trigger.
Batch Job OOM (Out of Memory) at 400k Records — Fix?
- Batch heap limit: 12 MB. Large record size × batch size can exceed limit.
- Reduce batch size (100 instead of 200) to halve heap usage.
- Query only needed fields (SELECT Id, Name, Amount instead of *).
- Clear collections after processing (don't accumulate in static variables).
Integration Errors & Recovery
7 QuestionsAPI Callout Timeout (120s) — Retry Strategy?
- Sync callout: 120s timeout. Don't retry synchronously.
- Store timeout errors in queue, retry asynchronously via Queueable.
- Exponential backoff: 2s, 4s, 8s prevents overwhelming external API.
- Max 3 retries. Move to DLQ after failures for manual review.
API Returns HTTP 429 (Rate Limit) — How to Handle?
- HTTP 429 = rate limited. Read Retry-After header for wait time.
- Queueable queue (serialized) naturally rate-limits by processing one at a time.
- Don't hammer API on 429 (will fail more).
API Response Partially Fails (400 Bad Request on 1 of 100 Records) — Handling?
- Parse API response to identify which records failed (usually in response body).
- Retry only failed records with corrected data or manual review.
- Mark successful records as synced to avoid reprocessing.
Webhook Delivery Guarantee: At Most Once or At Least Once?
- Webhooks = at-least-once delivery (duplicates possible on retries).
- Use idempotency key to de-duplicate (unique ID per webhook from sender).
- Store processed keys, skip reprocessing on duplicate.
External API Down — System Should Degrade Gracefully?
- Don't block user workflows on external API. Queue syncs, retry later.
- Feature flag to disable sync if API is known-down (prevent useless retries).
- Alert admins when external API is down (automated, not manual).
Data Corruption from API Sync — Detect and Rollback?
- No built-in rollback. Prevent corruption via validation before update.
- Version records: store old value before update for manual revert.
- Recycle Bin: 15-day retention. Backup service for longer recovery window.
Integration Monitoring: How to Alert on Failures?
- Custom Error__c object = central error log for all integrations.
- Scheduled job monitors errors, sends alerts to admin (email, Slack, Teams).
- Mark alert sent to avoid duplicate notifications.
- Dashboard shows error trend (visual monitoring, not just log dumps).
📚 Level Up Your Salesforce Interview Prep
Complete Post 1 (35 questions). Continue with Posts 2 & 3 for the full 105-question series:
✅ Post 1 Complete: 35 Scenarios Mastered
Production debugging, trigger logic, batch processing, integration recovery — all covered.
Ready for more? Posts 2 & 3 cover advanced patterns, architecture, and war stories. All 105 questions free, no signup.
Practice with real people
Join the free Mock Interview Community — practice with peers, get honest feedback, and walk into your real interview confident.
Join the Community ↗