Thursday, July 24, 2025

Salesforce Apex interview questions

 

Basic Apex Interview Questions

  1. What is Apex in Salesforce?

  2. What are the different data types in Apex?

  3. What are collections in Apex? Name the types.

  4. What is a trigger? When do you use it?

  5. Difference between before and after triggers?

  6. What are governor limits in Salesforce?

  7. What is the difference between SOQL and SOSL?

  8. Explain the use of Map, List, and Set in Apex.

  9. What is a batch class in Apex?

  10. What is the use of @future annotation?


🔹 Intermediate Apex Interview Questions

  1. What is the order of execution in Salesforce?

  2. What are the different types of triggers?

  3. What is the use of Database.insert(record, false)?

  4. What is the difference between public, private, global, and webService access modifiers in Apex?

  5. What are static and instance methods?

  6. What is a wrapper class? Where is it used?

  7. How can you avoid hitting governor limits in Apex?

  8. Explain Test classes and code coverage requirements.

  9. Difference between Database.query() and normal SOQL?

  10. What is a custom metadata type and how is it used in Apex?


🔹 Advanced Apex Interview Questions

  1. How do you handle recursion in triggers?

  2. What is a Queueable class and how is it different from Batch?

  3. Explain Continuation in Apex.

  4. What is dynamic Apex? When would you use it?

  5. What is Apex Scheduler and how do you implement it?

  6. How do you implement Apex Managed Sharing?

  7. What are best practices for writing scalable Apex code?

  8. How do you manage transactions in Apex?

  9. What is the difference between @future, Queueable, Batch, and Schedulable interfaces?

  10. 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 a trigger? 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:

  1. Load original record

  2. Evaluate validation rules

  3. Execute before triggers

  4. Save record (but not commit)

  5. Execute after triggers

  6. Workflow rules, processes, flows

  7. Escalation/assignment rules

  8. Roll-up summaries

  9. 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 or Batch 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() and Test.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@futureQueueableBatch Apex
Chaining
Complex Data
Callouts✅ (1 per call)
RecordsLimitedMediumUp 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:

apex

trigger 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:

apex

trigger 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:

apex

trigger 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:

apex

public 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:

apex

global 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:

apex

public 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 or Queueable.


🔹 SECURITY & SHARING

7. Share a Custom Record with Specific Users Programmatically

Answer:

apex

Custom_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:

apex

for (Contact c : contacts) { Account a = [SELECT Name FROM Account WHERE Id = :c.AccountId]; }

Good:

apex

Set<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); }