🔒

Unlock Interview Q&A

Join the GoSalesforce community to access expert interview questions, career guides, and premium resources.

Q&A Hub

Interview Questions

Master your Salesforce skills with our curated list of interview questions and expert answers.

🔍

Apex Development

1. User has View All on Profile, OWD is Private — can they delete records owned by others?

Answer :
View All allows reading all records. To delete, the user also needs 'Delete' on the object and 'Modify All' or 'Modify All Data' permission. 'View All' alone does not grant delete access. The user must own the record OR have 'Modify All' on the object OR have 'Modify All Data' system permission to delete other users' records.

2. What are Governor Limits in Salesforce? Give examples.

Answer :
Governor Limits prevent any single tenant from monopolising shared resources in Salesforce's multi-tenant environment. SOQL queries: 100 (sync) / 200 (async) SOQL rows returned: 50,000 DML statements: 150 DML rows: 10,000 Heap size: 6 MB (sync) / 12 MB (async) CPU time: 10,000 ms (sync) / 60,000 ms (async) Callouts: 100 per transaction, 120 sec total wait Future methods per transaction: 50 Batch apex jobs in queue: 5 (100 with Flex Queue) Collections (Maps, Sets, Lists) help avoid limits — store data in memory and avoid repeated queries. Bulkification avoids hitting DML/SOQL limits.

3. Best practices for writing Apex code.

Answer :
One trigger per object — delegate to a handler class Bulkify all code — never put SOQL/DML inside loops Use Collections (Maps, Sets, Lists) to batch operations Use Database.insert/update/delete with allOrNone=false for partial success handling Use with sharing to respect record-level security Avoid hardcoding IDs — use Custom Metadata or Custom Settings Write test classes with 75%+ coverage, use Test.startTest/stopTest for async Handle exceptions gracefully with try-catch-finally Use static variables to prevent trigger recursion Prefer SOQL for loops when processing large datasets to avoid heap issues Use @AuraEnabled(cacheable=true) only for read-only operations

4. What is Trigger.new and Trigger.newMap?

Answer :
Trigger.new: A List<SObject> of the new versions of records being inserted/updated. Available in: before insert, before update, after insert, after update, after undelete. Values are read-only in after context. Trigger.newMap: A Map<Id, SObject> of the same records, keyed by record Id. NOT available in before insert (records have no Id yet). Available in: before update, after insert, after update, after undelete.

5. What is Trigger.oldMap?

Answer :
Trigger.oldMap: A Map<Id, SObject> of the old (previous) versions of records. Available in: before update, after update, before delete, after delete. Used to detect changes: if (trigger.newMap.get(id).Status != trigger.oldMap.get(id).Status) { ... }

6. How do you handle trigger recursion?

Answer :
Common approach: Static Boolean variable in a handler class. The static variable persists for the lifetime of the transaction. public class TriggerHelper { public static Boolean isRunning = false; } In trigger: if(!TriggerHelper.isRunning){ TriggerHelper.isRunning = true; ... } Better approach for bulk scenarios: Use a Set<Id> of already-processed IDs instead of a simple Boolean, so only unprocessed records are handled in recursive calls. This handles scenarios with 200+ records correctly where Boolean might skip legitimate reprocessing.

7. What is Mixed DML Exception? How to resolve?

Answer :
Mixed DML occurs when you try to perform DML on setup objects (User, UserRole, Group, etc.) AND non-setup objects (Account, Contact, etc.) in the same transaction. Solutions: (1) Use @future method to perform one of the DML operations asynchronously; (2) Use System.runAs() in test classes; (3) Use a separate transaction via Queueable Apex. // Solution using @future @future public static void updateUserAsync(Id userId, String email) { User u = new User(Id = userId, Email = email); update u; }

8. Difference between insert and Database.insert?

Answer :
insert (DML statement): All-or-nothing — if one record fails, the entire transaction rolls back. Throws an exception on failure. Database.insert(records, false): Partial success allowed — failed records are tracked in Database.SaveResult[]. Transaction does not roll back for individual failures. Best for bulk data processing where partial success is acceptable.

9. What is Database.SavePoint and rollback?

Answer :
Savepoints allow partial rollback within a transaction. Use Database.setSavepoint() to mark a point, then Database.rollback(sp) to revert all DML done after that point without rolling back the entire transaction. Cannot rollback across async transactions.

10. What is the use of @future annotation?

Answer :
@future marks a static method to run asynchronously in a separate thread with its own governor limits. Used for: (1) Making callouts from triggers (requires @future(callout=true)); (2) Avoiding Mixed DML errors; (3) Offloading heavy processing. Limitations: Cannot accept sObject parameters (use Ids instead), cannot return values, cannot be called from another future, limit of 50 per transaction.

11. Can a future method call another future method?

Answer :
No. Calling a future method from another future method throws a System.AsyncException. The workaround is to use Queueable Apex, which supports chaining.

12. Can we call a future method from a Batch class?

Answer :
Future methods cannot be called from the start() or execute() methods of Batch Apex. They CAN be called from the finish() method. Alternatively, use Queueable Apex from execute() — one Queueable per execute() call.

13. What is Queueable Apex? Differences from Future?

Answer :
Queueable Apex implements the Queueable interface. Advantages over @future: (1) Can accept sObject and complex types as parameters; (2) Supports job chaining (call another Queueable from execute()); (3) Returns a Job Id for monitoring; (4) Can run up to 50 chained jobs (1 from synchronous context, 50 from async). Use Queueable when you need sObject params, chaining, or monitoring.

14. How does Batch Apex process large datasets?

Answer :
Batch Apex implements Database.Batchable<sObject>. Has 3 methods: start(): Returns QueryLocator or Iterable — defines records to process. Called once. execute(): Called once per batch chunk (default 200 records). Each execute() is a separate transaction with fresh governor limits. finish(): Called once after all batches complete. Used for post-processing (send email, chain another batch). Database.executeBatch(new MyBatch(), 200) — 2nd param sets batch size (max 2000, min 1). For callouts, implement Database.AllowsCallouts. For state across batches, implement Database.Stateful.

15. Can we call a batch from another batch?

Answer :
You can call a new batch from the finish() method only. Calling Database.executeBatch() from start() or execute() throws an AsyncException. Best practice: chain batches in finish() or use Queueable chaining.

16. What is Database.Stateful?

Answer :
By default, Batch Apex does not persist instance variables between execute() calls — each chunk starts fresh. Implementing Database.Stateful preserves instance variable values across all execute() calls, allowing you to aggregate counts, totals, error lists, etc. across batches.

17. Can we make callouts from Batch Apex?

Answer :
Yes — implement Database.AllowsCallouts interface on your batch class. Callouts cannot be made from start() or finish(), only from execute(). Each execute() can make up to 100 callouts. Note: You cannot have both DML and callouts in the same execute() call if there are uncommitted DML statements (uncommitted work pending error).

18. How to schedule a Batch class every 30 minutes?

Answer :
Salesforce's minimum cron schedule is hourly (you cannot set 30-min via cron directly). Workaround: Create 2 schedulable classes or schedule at :00 and :30 using 2 different scheduled jobs with cron expressions: '0 0 * * * ?' and '0 30 * * * ?'. In finish() method you can also reschedule the next run.

19. What is CPU Time Limit? How to resolve?

Answer :
CPU time limit is 10,000 ms (sync) / 60,000 ms (async). It only counts Apex execution time — NOT SOQL, DML, or callout wait times. To fix: Move logic to async (Queueable/Batch/Future) for higher limit Remove unnecessary loops or nested loops Use Maps instead of nested iterations Move complex calculations to Flow or off-platform Use SOQL aggregate functions instead of Apex aggregation Profile using Salesforce Apex Profiling logs

20. What is Heap Size error? How to resolve?

Answer :
Heap size limit: 6 MB (sync) / 12 MB (async). Occurs when too much data is held in memory. Fix by: Using SOQL for loops: for(Account a : [SELECT Id FROM Account]) — processes one batch at a time Clearing collections after use: listVar.clear() Selecting only needed fields in SOQL Moving large data processing to Batch Apex (async has higher limit)

21. SOQL 101 Error — what is it and how to fix?

Answer :
'Too many SOQL queries: 101' occurs when you exceed 100 SOQL queries in a synchronous transaction. Common cause: SOQL inside a loop. Fix: Move SOQL outside loops — collect Ids first, then query in bulk Use Map<Id, SObject> populated before the loop If 101 error in test class: ensure test data is set up in @testSetup and SOQL is not duplicated Check if managed package triggers are consuming some of the 100 limit

22. What is the use of 'finally' keyword?

Answer :
The finally block always executes regardless of whether an exception was thrown or caught. Used for cleanup operations: closing connections, resetting state variables, logging. Syntax: try { ... } catch(Exception e) { ... } finally { // always runs }

23. What is dynamic SOQL? SOQL Injection and how to prevent?

Answer :
Dynamic SOQL builds query strings at runtime using Database.query(String). Risk: if user input is directly concatenated, attackers can inject malicious SOQL. Prevention: Use String.escapeSingleQuotes() on all user-supplied input before including in the query string, or use bind variables where possible.

24. Difference between Database.QueryLocator and Iterable in Batch?

Answer :
Database.QueryLocator: Returns up to 50 million records (bypasses the normal 50,000 SOQL row limit for start()). Best for simple SOQL queries. Iterable<SObject>: Allows complex processing logic in start() to build the list. Limited to 50,000 records. Used when you need to pre-process or filter records programmatically before batching.

25. What are best practices for Test Classes?

Answer :
Use @TestSetup for shared test data — runs once, rolled back after each test method Use Test.startTest() / Test.stopTest() to reset governor limits and force async execution Never use SeeAllData=true unless absolutely necessary (e.g., standard price books) Assert expected outcomes — don't just run code without assertions Test bulk scenarios with 200 records to verify bulkification Test positive, negative, and edge cases Use @isTest(SeeAllData=false) — default for all test classes Custom Metadata records are accessible in test classes without SeeAllData=true; Custom Settings require test data creation Use Test.setMock() for HTTP callout testing

26. Why use Test.startTest() and Test.stopTest()?

Answer :
Test.startTest() resets governor limits for the code inside the test boundary — giving fresh limits. Test.stopTest() forces all asynchronous operations (future, batch, queueable) started within the block to complete synchronously. This allows you to test async code results.

27. Which exceptions cannot be caught in Apex?

Answer :
System.LimitException (governor limits exceeded) cannot be caught — the transaction terminates immediately. Most other exceptions (DMLException, QueryException, NullPointerException, etc.) can be caught with catch(Exception e) or specific exception types.

28. What annotations are used in test classes?

Answer :
@isTest — marks a class or method as a test @TestSetup — runs once before all test methods in the class to set up shared data @isTest(SeeAllData=true) — allows access to real org data (avoid) @isTest(IsParallel=true) — allows test to run in parallel with other tests

29. What is the use case where SeeAllData=true is acceptable?

Answer :
When testing against standard price books (Pricebook2) which require real data. In most other cases, create your own test data. Alternatively, use Test.getStandardPricebookId() instead of querying.

30. How to write test class for HTTP callouts?

Answer :
Implement HttpCalloutMock interface and use Test.setMock(): @isTest global class MockHttpResponse implements HttpCalloutMock { global HTTPResponse respond(HTTPRequest req) { HTTPResponse res = new HTTPResponse(); res.setStatusCode(200); res.setBody('{"id":"123"}'); return res; } } // In test method: Test.setMock(HttpCalloutMock.class, new MockHttpResponse());

31. What are two methods specifically needed for Batch Apex test class?

Answer :
Test.startTest() and Test.stopTest(). The Database.executeBatch() call must be between these two. stopTest() forces the batch to complete synchronously so you can assert results.

32. How to write test class for @future method?

Answer :
Wrap the future call in Test.startTest() / Test.stopTest(). The stopTest() forces the future method to complete before assertions. Test.startTest(); MyClass.myFutureMethod(recordId); Test.stopTest(); // Assert results after stopTest()

33. Can we pass an sObject to a future method?

Answer :
No — future methods only accept primitive data types and collections of primitives (String, Integer, List<Id>, etc.) as parameters. Workaround: Pass a List<Id> and re-query inside the future method, OR serialize the sObject to JSON (String) and deserialize inside the future method.

34. What is a Wrapper Class? When to use?

Answer :
A wrapper class is a custom Apex class used to group related data together — often combining data from multiple objects or adding UI-specific properties not present in sObjects. Use cases: returning complex data to LWC/Aura, combining Account with its related Contacts and Opportunities in one structure, adding UI state like isSelected checkbox for datatables.

35. Difference between virtual, abstract methods and interfaces?

Answer :
Virtual class/method: Can be instantiated directly AND extended/overridden by subclasses. Use 'override' keyword to override virtual methods. Abstract class/method: Cannot be instantiated. Subclasses MUST implement abstract methods. Can contain both abstract and non-abstract methods. Interface: A contract — all methods must be implemented by the implementing class. Supports multiple interface implementation (unlike single inheritance in Apex).

💬 Community Chatter

Loading...
🔐

Join the Community Discussion

Login to ask questions, share answers, and connect with other Salesforce professionals.

Welcome Back

OR
Don't have an account? Sign up