Basic Apex Interview Questions
-
What is Apex in Salesforce?
-
What are the different data types in Apex?
-
What are collections in Apex? Name the types.
-
What is a trigger? When do you use it?
-
Difference between
before
andafter
triggers? -
What are governor limits in Salesforce?
-
What is the difference between SOQL and SOSL?
-
Explain the use of
Map
,List
, andSet
in Apex. -
What is a batch class in Apex?
-
What is the use of
@future
annotation?
🔹 Intermediate Apex Interview Questions
-
What is the order of execution in Salesforce?
-
What are the different types of triggers?
-
What is the use of
Database.insert(record, false)
? -
What is the difference between
public
,private
,global
, andwebService
access modifiers in Apex? -
What are static and instance methods?
-
What is a wrapper class? Where is it used?
-
How can you avoid hitting governor limits in Apex?
-
Explain Test classes and code coverage requirements.
-
Difference between
Database.query()
and normal SOQL? -
What is a custom metadata type and how is it used in Apex?
🔹 Advanced Apex Interview Questions
-
How do you handle recursion in triggers?
-
What is a Queueable class and how is it different from Batch?
-
Explain
Continuation
in Apex. -
What is dynamic Apex? When would you use it?
-
What is Apex Scheduler and how do you implement it?
-
How do you implement Apex Managed Sharing?
-
What are best practices for writing scalable Apex code?
-
How do you manage transactions in Apex?
-
What is the difference between
@future
,Queueable
,Batch
, andSchedulable
interfaces? -
Explain how you can handle large data volumes in Apex.
🔹 Bonus Practical Scenarios
-
How do you prevent duplicate record insertion in triggers?
-
Can we call a
batch class
from atrigger
? How? -
How to perform error logging in Apex?
-
Describe a situation where you had to refactor poorly written Apex code.
-
How do you ensure bulkification in a trigger?
🔹 Basic Apex Interview Questions & Answers
1. What is Apex in Salesforce?
Answer: Apex is a strongly-typed, object-oriented programming language developed by Salesforce. It allows developers to execute flow and transaction control statements on the Salesforce platform.
2. What are the data types in Apex?
Answer: Apex supports:
-
Primitive:
Integer
,Double
,Long
,String
,Boolean
,Date
,Datetime
,ID
-
Collections:
List
,Set
,Map
-
Enums, sObjects, Interfaces, and user-defined types.
3. What is a Trigger?
Answer: A trigger is Apex code that executes before or after specific DML operations (insert, update, delete, undelete) on Salesforce records.
4. Difference between before
and after
triggers?
Answer:
-
before
: Used for validation or setting default values before the record is saved to the database. -
after
: Used when data is committed, typically for updating related records or sending emails.
5. What are Governor Limits?
Answer: Salesforce enforces limits to ensure efficient use of resources. Examples:
-
100 SOQL queries per transaction.
-
150 DML operations per transaction.
-
50,000 records returned by SOQL.
6. What is the difference between SOQL and SOSL?
Answer:
-
SOQL (Salesforce Object Query Language): Query records from a single object or related objects.
-
SOSL (Salesforce Object Search Language): Search for text across multiple objects and fields.
7. What is a Collection in Apex?
Answer:
-
List: Ordered collection of elements.
-
Set: Unordered, unique values.
-
Map: Key-value pair storage.
🔹 Intermediate Apex Interview Questions & Answers
8. What is the order of execution in Salesforce?
Answer:
-
Load original record
-
Evaluate validation rules
-
Execute
before
triggers -
Save record (but not commit)
-
Execute
after
triggers -
Workflow rules, processes, flows
-
Escalation/assignment rules
-
Roll-up summaries
-
Commit record
9. What is a Batch Apex?
Answer: Batch Apex is used to process large volumes of data in chunks asynchronously. It implements Database.Batchable
interface.
10. What is a Wrapper Class?
Answer: A wrapper class is a custom object defined by a developer where multiple objects or values can be wrapped into a single unit. Useful in UI and visualforce pages.
11. What is a Static Method?
Answer: A method that belongs to a class rather than an instance of a class. Can be called without instantiating the class.
12. How can you avoid hitting governor limits?
Answer:
-
Use bulkified code
-
Use collections
-
Avoid SOQL/DML inside loops
-
Use
@future
,Queueable
orBatch Apex
when needed
13. How do you write a test class in Apex?
Answer:
-
Use
@isTest
annotation -
Cover at least 75% of code
-
Use
Test.startTest()
andTest.stopTest()
-
Assert expected results
14. What is Custom Metadata Type?
Answer: Custom metadata types allow developers to define custom data structures for app configuration and deploy them across orgs.
🔹 Advanced Apex Interview Questions & Answers
15. What is the use of @future
annotation?
Answer: Marks a method for asynchronous execution. Cannot return data or take complex types like sObjects as parameters.
16. What is Queueable Apex?
Answer: A more flexible version of @future
, allows chaining and processing complex data. Implements Queueable
interface.
17. What is Dynamic Apex?
Answer: Allows the ability to access sObject schema, fields, objects dynamically at runtime using Schema
class and Type
.
18. Difference between Batch
, Queueable
, and @future
?
Feature | @future | Queueable | Batch Apex |
---|---|---|---|
Chaining | ❌ | ✅ | ✅ |
Complex Data | ❌ | ✅ | ✅ |
Callouts | ✅ (1 per call) | ✅ | ✅ |
Records | Limited | Medium | Up to 50 million |
19. What is Apex Managed Sharing?
Answer: It allows developers to share records programmatically based on business logic using custom sharing rules.
20. How do you handle recursion in triggers?
Answer: Use a static variable in an Apex class to track whether a trigger has already run to prevent infinite loops.
🔹 Bonus Tips
-
Always bulkify your triggers and logic.
-
Use custom settings/metadata for configurable logic.
-
Write meaningful test cases with assert statements.
-
Know how to use
try-catch
for error handling.
🔹 Apex Trigger Scenarios
1. Prevent Duplicate Contacts on Account
Scenario: You need to write a trigger to prevent inserting duplicate contact records (same Email) under the same Account.
🧠Follow-up: How would you bulkify this trigger?
2. Roll-Up Contact Count on Account
Scenario: Create a custom roll-up summary (using Apex) to count the number of contacts related to each account when contact is inserted, updated, or deleted.
3. Trigger Recursion Prevention
Scenario: You have a trigger that updates related records, which in turn fires another trigger on the same object. How would you prevent infinite recursion?
4. Account Rating Based on Opportunity Value
Scenario: If the total amount of all related Opportunities > 1M, mark the Account’s Rating as “Hot”. Implement logic using Apex.
5. Prevent Deletion of Parent If Children Exist
Scenario: Prevent an Account from being deleted if it has active Contacts or Opportunities.
🔹 Asynchronous Apex Scenarios
6. Send Emails to All Contacts
Scenario: On Account update, you need to send an email to all related contacts asynchronously.
🧠Follow-up: Would you use @future
, Queueable
, or Batch
? Why?
7. Process 100,000 Records
Scenario: You need to update a field for 100,000 Contact records based on custom logic. What Apex solution would you use and why?
8. Chaining Asynchronous Jobs
Scenario: After finishing one batch job (e.g., data cleanup), you want to start another job (e.g., data sync). How would you achieve this?
🔹 Integration & API Scenarios
9. Call External System from Apex
Scenario: On a case update, an external system must be notified via REST API. How would you implement this with callout?
🧠Follow-up: Can callouts be made from triggers?
10. Retry Failed API Callouts
Scenario: Some external API calls are failing due to timeout. How do you design a retry mechanism in Apex?
🔹 Testing & Deployment Scenarios
11. Test Class for Asynchronous Code
Scenario: You wrote a Queueable class that updates Opportunities. How would you test it, including asynchronous behavior?
12. Test Error Handling
Scenario: Your trigger handles errors using try-catch and logs them. How would you write a test class to ensure the error handling works?
🔹 Security & Sharing Scenarios
13. Restrict Trigger Logic Based on Profile
Scenario: Trigger should only run for users with a specific profile. How would you implement that?
14. Programmatic Sharing
Scenario: A custom object record needs to be shared programmatically with a specific user group. How do you handle this?
🔹 Best Practice Scenarios
15. Avoid SOQL/DML in Loops
Scenario: A junior developer wrote a trigger that performs SOQL and DML inside for
loops. How would you refactor it?
16. Dynamic SOQL Use Case
Scenario: Based on user input, the fields to query are dynamic. How would you construct and execute a dynamic SOQL query securely?
🔹 TRIGGERS
1. Prevent Duplicate Contacts on the Same Account
Question: Prevent creating a Contact with the same Email under the same Account.
Answer:
apextrigger PreventDuplicateContacts on Contact (before insert) { Set<String> emailSet = new Set<String>(); Map<Id, Set<String>> accountEmailMap = new Map<Id, Set<String>>(); for (Contact c : Trigger.new) { if (c.AccountId != null && c.Email != null) { if (!accountEmailMap.containsKey(c.AccountId)) { accountEmailMap.put(c.AccountId, new Set<String>()); } accountEmailMap.get(c.AccountId).add(c.Email.toLowerCase()); } } List<Contact> existingContacts = [SELECT Email, AccountId FROM Contact WHERE AccountId IN :accountEmailMap.keySet() AND Email != null]; for (Contact c : existingContacts) { if (accountEmailMap.containsKey(c.AccountId) && accountEmailMap.get(c.AccountId).contains(c.Email.toLowerCase())) { for (Contact newC : Trigger.new) { if (newC.AccountId == c.AccountId && newC.Email != null && newC.Email.equalsIgnoreCase(c.Email)) { newC.addError('Duplicate contact email under the same Account.'); } } } } }
2. Custom Roll-up Summary: Contact Count on Account
Question: Update a custom field Contact_Count__c
on Account when Contacts are inserted or deleted.
Answer:
apextrigger ContactRollupTrigger on Contact (after insert, after delete, after undelete) { Set<Id> accountIds = new Set<Id>(); if (Trigger.isInsert || Trigger.isUndelete) { for (Contact c : Trigger.new) { if (c.AccountId != null) accountIds.add(c.AccountId); } } else if (Trigger.isDelete) { for (Contact c : Trigger.old) { if (c.AccountId != null) accountIds.add(c.AccountId); } } List<Account> accountsToUpdate = new List<Account>(); for (AggregateResult ar : [ SELECT AccountId, COUNT(Id) count FROM Contact WHERE AccountId IN :accountIds GROUP BY AccountId ]) { accountsToUpdate.add(new Account( Id = (Id)ar.get('AccountId'), Contact_Count__c = (Integer)ar.get('count') )); } update accountsToUpdate; }
3. Prevent Deletion of Account if Active Contacts Exist
Answer:
apextrigger PreventAccountDeletion on Account (before delete) { Set<Id> accountIds = new Set<Id>(); for (Account acc : Trigger.old) { accountIds.add(acc.Id); } Map<Id, Integer> accountContactMap = new Map<Id, Integer>(); for (AggregateResult ar : [ SELECT AccountId, COUNT(Id) count FROM Contact WHERE AccountId IN :accountIds GROUP BY AccountId ]) { accountContactMap.put((Id)ar.get('AccountId'), (Integer)ar.get('count')); } for (Account acc : Trigger.old) { if (accountContactMap.containsKey(acc.Id)) { acc.addError('You cannot delete this Account because it has related Contacts.'); } } }
🔹 ASYNCHRONOUS APEX
4. Send Email to All Contacts on Account Update (Queueable)
Answer:
apexpublic class SendEmailToContactsQueueable implements Queueable { private Id accountId; public SendEmailToContactsQueueable(Id accountId) { this.accountId = accountId; } public void execute(QueueableContext context) { List<Contact> contacts = [SELECT Email FROM Contact WHERE AccountId = :accountId AND Email != null]; for (Contact c : contacts) { Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage(); mail.setToAddresses(new String[]{c.Email}); mail.setSubject('Account Updated'); mail.setPlainTextBody('The related Account has been updated.'); Messaging.sendEmail(new Messaging.SingleEmailMessage[]{mail}); } } }
apex// Call this from a trigger System.enqueueJob(new SendEmailToContactsQueueable(accountId));
5. Process 100,000 Records Using Batch Apex
Answer:
apexglobal class ContactBatchUpdate implements Database.Batchable<SObject> { global Database.QueryLocator start(Database.BatchableContext bc) { return Database.getQueryLocator('SELECT Id, Status__c FROM Contact'); } global void execute(Database.BatchableContext bc, List<Contact> scope) { for (Contact c : scope) { c.Status__c = 'Processed'; } update scope; } global void finish(Database.BatchableContext bc) { System.debug('Batch Completed'); } }
🔹 INTEGRATION SCENARIOS
6. Call External API on Case Update
Answer:
apexpublic class CaseCallout { @future(callout=true) public static void notifyExternalSystem(Id caseId) { Case c = [SELECT Id, Subject FROM Case WHERE Id = :caseId]; HttpRequest req = new HttpRequest(); req.setEndpoint('https://example.com/api/case'); req.setMethod('POST'); req.setBody(JSON.serialize(c)); req.setHeader('Content-Type', 'application/json'); Http http = new Http(); HttpResponse res = http.send(req); System.debug(res.getBody()); } }
⚠️ Cannot call web services from triggers directly — use
@future
orQueueable
.
🔹 SECURITY & SHARING
7. Share a Custom Record with Specific Users Programmatically
Answer:
apexCustom_Object__Share shareRec = new Custom_Object__Share(); shareRec.ParentId = 'a0Dxxxxxxxxxxxx'; // Record Id shareRec.UserOrGroupId = '005xxxxxxxxxxxx'; // User Id shareRec.AccessLevel = 'Edit'; shareRec.RowCause = Schema.Custom_Object__Share.RowCause.Manual; insert shareRec;
🔹 TESTING & BEST PRACTICES
8. Write a Test Class for Queueable Class
Answer:
apex@isTest private class TestSendEmailQueue { @isTest static void testQueueable() { Account acc = new Account(Name='Test'); insert acc; Contact con = new Contact(LastName='Tester', Email='test@example.com', AccountId=acc.Id); insert con; Test.startTest(); System.enqueueJob(new SendEmailToContactsQueueable(acc.Id)); Test.stopTest(); } }
9. Avoid SOQL/DML in Loops
Bad:
apexfor (Contact c : contacts) { Account a = [SELECT Name FROM Account WHERE Id = :c.AccountId]; }
Good:
apexSet<Id> accountIds = new Set<Id>(); for (Contact c : contacts) accountIds.add(c.AccountId); Map<Id, Account> accountMap = new Map<Id, Account>( [SELECT Name FROM Account WHERE Id IN :accountIds]); for (Contact c : contacts) { Account a = accountMap.get(c.AccountId); }