InterviewPitch
Apex interview questions

Apex Interview Questions with Answers

Most Asked Apex Interview Questions for Software Engineer Roles

100+ QuestionsDetailed AnswersCode ExamplesUpdated for 2026

Introduction

This page contains a carefully selected collection of Apex Interview Questions and Answers designed for students, Salesforce beginners, software developers, and experienced professionals preparing for Salesforce developer interviews. The questions cover both theoretical concepts and practical programming examples commonly asked during technical interviews. Apex is Salesforce's strongly typed, object-oriented programming language used to build business logic on the Salesforce Platform. It enables developers to create triggers, classes, asynchronous jobs, REST services, integrations, and custom automation while working securely with Salesforce data. This interview guide covers beginner, intermediate, advanced, and scenario-based Apex interview questions including Apex syntax, triggers, SOQL, SOSL, governor limits, collections, asynchronous Apex, exception handling, testing, batch processing, integrations, and best practices followed by Salesforce developers.

Why Apex?

  • Purpose-built for the Salesforce platform – tightly integrated with CRM data and metadata
  • Strongly typed and object-oriented – ensures reliability and maintainability
  • Built-in governor limits – enforces efficient, scalable code in multi-tenant environments
  • Full support for CRUD, SOQL, SOSL, and DML operations – seamless data manipulation
  • Asynchronous capabilities – batch jobs, queueable, and scheduled Apex for background processing
  • Robust testing framework – built-in support for test classes and code coverage
  • Highly demanded skill – essential for Salesforce developers and architects worldwide

Most Asked Apex Interview Questions

Beginner
1. What is Apex?

Apex is a strongly typed, object-oriented programming language developed by Salesforce for building enterprise applications on the Salesforce Platform. It runs entirely on the Salesforce cloud and is used to implement business logic, automate processes, and integrate with external systems.

  • Object-oriented – supports classes, interfaces, inheritance
  • Strongly typed – compile-time type checking
  • Database integration – native SOQL and DML operations
  • Multitenant aware – designed for Salesforce's shared environment
  • Asynchronous support – future methods, batch, queueable
Apex

public class MyHelloWorldClass {
    public static void sayHello() {
        System.debug('Hello World from Apex!');
    }
}
Beginner
2. What are the key features of Apex?

Apex is object-oriented, strongly typed, multitenant aware, integrated directly with the database, and provides built-in support for transactional triggers as well as complex asynchronous processing loops.

Apex

Account acc = new Account(Name = 'Acme Corp');
insert acc;
Beginner
3. What is a Governor Limit?

Governor limits are runtime limits enforced by the Salesforce multitenant engine to prevent shared execution threads from monopolizing system resources.

Apex

// Bulkified query and list insert
List<Contact> contactsToInsert = new List<Contact>();

for (Account acc : [SELECT Id FROM Account LIMIT 100]) {
    contactsToInsert.add(
        new Contact(
            LastName = 'Doe',
            AccountId = acc.Id
        )
    );
}

insert contactsToInsert;
Beginner
4. What is SOQL?

SOQL (Salesforce Object Query Language) is used to read data structures and records from individual object structures.

Apex

List<Opportunity> opps = [
    SELECT Id, Name, Amount, StageName
    FROM Opportunity
    WHERE CloseDate = TODAY
];
Beginner
5. What is SOSL?

SOSL (Salesforce Object Search Language) is used to scan text patterns across multiple index tables at once.

Apex

List<List<SObject>> searchList = [
    FIND 'Acme*'
    IN ALL FIELDS
    RETURNING
        Account(Name),
        Contact(FirstName, LastName)
];
Beginner
6. Difference between SOQL and SOSL?

SOQL retrieves exact records from a single object index, whereas SOSL performs multi-object keyword searches efficiently across text columns.

Apex

// SOQL (Exact retrieval)
Account acc = [
    SELECT Id
    FROM Account
    WHERE Name = 'Acme'
    LIMIT 1
];

// SOSL (Fuzzy search)
List<List<SObject>> results = [
    FIND 'Acme'
    IN NAME FIELDS
    RETURNING Account(Id, Name)
];
Beginner
7. What is a Trigger in Apex?

A trigger is Apex code that runs dynamically before or after specific DML operations, such as record insertions, edits, or removals.

Apex

trigger AccountTrigger on Account (before insert, before update) {

    for (Account acc : Trigger.new) {

        if (acc.Industry == null) {
            acc.Industry.addError('Industry is required.');
        }

    }

}
Beginner
8. What are DML statements?

Data Manipulation Language statements modify records in the Salesforce database (e.g., insert, update, delete, undelete, upsert).

Apex

List<Account> newAccs = new List<Account>{
    new Account(Name = 'Tech Corp'),
    new Account(Name = 'Media Group')
};

insert newAccs;
Beginner
9. What is a Class in Apex?

A class is a structural template or object-oriented blueprint containing processing methods and properties.

Apex

public class Vehicle {

    private String model;

    public Vehicle(String modelName) {
        this.model = modelName;
    }

}
Beginner
10. What are Access Modifiers in Apex?

Apex provides four levels of visibility control: public, private, protected, and global.

Apex

global class GlobalService {

    public static void performTask() {

        // Shared across namespaces and API integrations

    }

}
Beginner
11. What is a Static variable?

A static variable is scoped to the class itself rather than individual instances, and persists across execution contexts.

Apex

public class Counter {

    public static Integer staticCount = 0; // Class-level shared state

    public Integer instanceCount = 0; // Object-level individual state

}
Beginner
12. What is a Constructor?

A constructor is a special class method called automatically during object instantiation to initialize state.

Apex

public class Employee {

    public String name;

    public Employee(String empName) {
        this.name = empName;
    }

}
Beginner
13. What is Batch Apex?

Batch Apex processes massive datasets asynchronously by splitting operations into smaller, manageable transaction blocks called chunks.

Apex

public class AccountUpdateBatch implements Database.Batchable<SObject> {

    public Database.QueryLocator start(Database.BatchableContext bc) {

        return Database.getQueryLocator(
            'SELECT Id FROM Account'
        );

    }

    public void execute(
        Database.BatchableContext bc,
        List<Account> scope
    ) {

        // Process each chunk safely

    }

    public void finish(Database.BatchableContext bc) {

        // Post-processing logic

    }

}
Beginner
14. What is a Future Method?

A future method (annotated with @future) executes asynchronously in its own thread block when resources become available.

Apex

public class AsyncHelper {

    @future
    public static void callExternalWebService(String payload) {

        // Async processing

    }

}
Beginner
15. What is a Test Class?

A test class contains verification logic to test Apex code functionality, and is required to hit a minimum 75% code coverage threshold for production deployments.

Apex

@isTest
private class AccountServiceTest {

    @isTest
    static void testAccountCreation() {

        Account acc = new Account(Name = 'Test Acc');

        insert acc;

        System.assertNotEquals(
            null,
            acc.Id,
            'Account ID should be generated'
        );

    }

}
Intermediate
16. What is Bulkification?

Bulkification is the practice of designing code to handle sets of records efficiently in a single operation, rather than processing individual records one-by-one inside loop blocks.

Apex

// BAD (Trigger with nested query)

// for (Account a : Trigger.new) {
//     List<Contact> c = [
//         SELECT Id
//         FROM Contact
//         WHERE AccountId = :a.Id
//     ];
// }

// GOOD (Bulkified query mapping)

Set<Id> accIds = new Set<Id>();

for (Account a : Trigger.new) {
    accIds.add(a.Id);
}

List<Contact> contacts = [
    SELECT Id, LastName
    FROM Contact
    WHERE AccountId IN :accIds
];
Intermediate
17. What is a Wrapper Class?

A wrapper class is a custom container object used to bind multiple distinct data types or sObjects together into a single logical structure.

Apex

public class TableWrapper {

    public Account accRecord { get; set; }

    public Boolean isSelected { get; set; }

    public TableWrapper(Account a) {

        this.accRecord = a;
        this.isSelected = false;

    }

}
Intermediate
18. What is a Map in Apex?

A map is an un-ordered collection that stores data in key-value pairs, where each key uniquely maps to a single corresponding value.

Apex

Map<Id, Account> accountMap =
    new Map<Id, Account>([
        SELECT Id, Name
        FROM Account
        LIMIT 10
    ]);

Account target =
    accountMap.get('0018000000GvNX0AAN');
Intermediate
19. What is a Set in Apex?

A set is an unordered collection of elements that enforces uniqueness, preventing duplicate entries from being added to the collection.

Apex

Set<String> uniqueCodes =
    new Set<String>{
        'US',
        'UK',
        'CA',
        'US'
    };

System.debug(uniqueCodes.size()); // Outputs 3
Intermediate
20. What is a List in Apex?

A list is an ordered collection of elements indexed by position. It can contain duplicate values and functions like a standard dynamic array.

Apex

List<String> names = new List<String>();

names.add('Akash');
names.add('Rahul');

String first = names.get(0);
Intermediate
21. What is Queueable Apex?

Queueable Apex is an asynchronous design pattern that builds upon future methods, allowing you to monitor job status and chain sequential asynchronous execution flows.

Apex

public class AsyncProcessor implements Queueable {

    public void execute(QueueableContext context) {

        // Chainable async operation
        System.enqueueJob(new SecondaryProcessor());

    }

}
Intermediate
22. What is SObject?

An sObject is a generic data type that can represent any standard or custom Salesforce object record in memory.

Apex

sObject genericRecord =
    new Account(Name = 'Generic Corp');

String objName =
    genericRecord
        .getSObjectType()
        .getDescribe()
        .getName();
Intermediate
23. What is Schema Builder?

Schema Builder is a visual design utility inside Salesforce used to manage data models, define objects, and establish relationship connections. At the code level, you describe these schemas programmatically.

Apex

Map<String, Schema.SObjectType> gd =
    Schema.getGlobalDescribe();

Schema.DescribeSObjectResult descResult =
    gd.get('Account').getDescribe();
Intermediate
24. What is With Sharing?

The with sharing keyword enforces the organization-wide defaults and sharing rules of the running user for all database queries and transactions in that class.

Apex

public with sharing class SecureRecordController {

    // Respects sharing rules
    // of current logged-in user

}
Intermediate
25. What is Without Sharing?

The without sharing keyword executes class logic in system mode, ignoring the current user's sharing rules and record permissions.

Apex

public without sharing class AdminDataOverrider {

    // Skips sharing rules
    // and operates with system-level access

}
Intermediate
26. What is Upsert?

The upsert command checks if a record exists based on an ID or unique external ID field; it updates the record if found, or inserts a new record if it is not.

Apex

Account acc = new Account(
    Name = 'Upsert Corp',
    External_ID__c = 'EXT-101'
);

upsert acc External_ID__c;
Intermediate
27. What is Database Class?

The Database class provides system methods for database manipulation, supporting partial successes via the allOrNone parameter option.

Apex

Database.SaveResult[] srList =
    Database.insert(accountList, false);

// false allows partial success
Intermediate
28. What is Trigger Context Variable?

Trigger context variables (e.g., Trigger.new, Trigger.oldMap, Trigger.isInsert) provide operational state and data from the triggering transaction.

Apex

trigger ContactTrigger on Contact (before update) {

    for (Contact newCon : Trigger.new) {

        Contact oldCon =
            Trigger.oldMap.get(newCon.Id);

        if (newCon.Email != oldCon.Email) {

            System.debug(
                'Email changed from '
                + oldCon.Email
            );

        }

    }

}
Intermediate
29. What is Callout in Apex?

A callout executes HTTP requests from Apex to integrate and exchange payload data with external web services.

Apex\

Http http = new Http();

HttpRequest request = new HttpRequest();

request.setEndpoint(
    'https://api.example.com/data'
);

request.setMethod('GET');

HttpResponse response =
    http.send(request);
Intermediate
30. What is Exception Handling?

Exception handling uses try-catch-finally blocks to intercept runtime errors, handle failures gracefully, and execute mandatory cleanup actions.

Apex

try {

    insert new Lead(LastName = 'Smith');

}
catch (DmlException e) {

    System.debug(
        'DML failed: ' + e.getMessage()
    );

}
finally {

    System.debug(
        'Transaction finished processing.'
    );

}
Advanced
31. What is Lightning Web Component integration with Apex?

Apex methods annotated with @AuraEnabled allow client-side Lightning Web Components (LWC) to call server-side actions, retrieve database records, or trigger business logic.

Apex

public class ContactController {

    @AuraEnabled(cacheable=true)
    public static List<Contact> getContactList() {

        return [
            SELECT Id, FirstName, LastName
            FROM Contact
            LIMIT 10
        ];

    }

}
Advanced
32. What is Platform Event?

Platform Events support an event-driven architecture, enabling applications to run independently by publishing and subscribing to real-time notification streams.

Apex

Order_Event__e eventObj =
    new Order_Event__e(
        Order_Id__c = 'ORD-009',
        Status__c = 'Shipped'
    );

Database.SaveResult sr =
    EventBus.publish(eventObj);
Advanced
33. What is Mixed DML Error?

A Mixed DML error occurs when you try to modify a setup object (like User) and a non-setup object (like Account) in the same transaction context.

Apex

User u = [
    SELECT Id
    FROM User
    WHERE Alias = 'admin'
];

System.runAs(u) {

    // Update User (Setup Object)

}

// Update Account (Non-Setup Object)
// outside block
Advanced
34. What is Apex Scheduler?

The Apex Scheduler executes specific classes at scheduled intervals by implementing the Schedulable interface.

Apex

public class CleanupScheduler
    implements Schedulable {

    public void execute(SchedulableContext sc) {

        // Delete orphaned logs dynamically

    }

}
Advanced
35. What is Custom Metadata Type?

Custom Metadata Types are application configurations that can be packaged and deployed between environments. They are read from cache without consuming SOQL query limits.

Apex

App_Setting__mdt setting =
    App_Setting__mdt.getInstance(
        'Global_Config'
    );

Boolean featureEnabled =
    setting.Is_Active__c;
Advanced
36. What is Field-Level Security?

Field-Level Security (FLS) manages user access to specific fields on objects. It should be validated dynamically in Apex before querying or mutating field data.

Apex

if (
    Schema.sObjectType.Contact
        .fields.Email.isAccessible()
) {

    // Query/Read field safely

}
Advanced
37. What is Dynamic SOQL?

Dynamic SOQL allows you to construct and execute SOQL query strings at runtime using the Database.query() method.

Apex

String dynamicField = 'Phone, Email';

String queryStr =
    'SELECT Id, '
    + dynamicField
    + ' FROM Lead LIMIT 5';

List<Lead> leads =
    Database.query(queryStr);
Advanced
38. What is Savepoint?

A Savepoint defines a specific state in a transaction that you can roll back to in case of errors, without reverting the entire transaction.

Apex

Savepoint sp =
    Database.setSavepoint();

try {

    insert new Account(
        Name = 'Success'
    );

    insert new Contact();

    // Fails due to missing LastName

}
catch (Exception e) {

    Database.rollback(sp);

    // Reverts transaction

}
Advanced
39. What is a Custom Setting?

Custom Settings are application configurations cached in memory. They provide fast read access without using SOQL query limits.

Apex

Discount_Setting__c discount =
    Discount_Setting__c.getInstance();

Decimal rate =
    discount.Global_Rate__c;
Advanced
40. What is CRUD and FLS in Apex?

CRUD permissions control user access to entire objects, while FLS permissions control user access to specific fields on those objects.

Apex

if (
    Schema.sObjectType.Account.isCreateable()
    &&
    Schema.sObjectType.Account
        .fields.Rating.isCreateable()
) {

    insert new Account(
        Name = 'Protected Inc',
        Rating = 'Hot'
    );

}
Coding Round
41. Query Accounts using SOQL
Apex

List<Account> accs = [
    SELECT Id, Name
    FROM Account
];
Coding Round
42. Insert a record
Apex

Account acc =
    new Account(Name='Test');

insert acc;
Coding Round
43. Update a record
Apex

acc.Name = 'Updated';

update acc;
Coding Round
44. Delete a record
Apex

delete acc;
Coding Round
45. Future method example
Apex

@future
public static void asyncMethod() {

    // Long running asynchronous process

}
Coding Round
46. Batch Apex structure
Apex

global class MyBatch
    implements Database.Batchable<SObject> {

    global Database.QueryLocator start(
        Database.BatchableContext BC
    ) {

        return Database.getQueryLocator(
            'SELECT Id FROM Account'
        );

    }

    global void execute(
        Database.BatchableContext BC,
        List<Account> scope
    ) {

        // Batch chunks executed here

    }

    global void finish(
        Database.BatchableContext BC
    ) {

        // Cleanup actions

    }

}
Coding Round
47. Trigger example
Apex

trigger AccountTrigger on Account (
    before insert
) {

    for (Account acc : Trigger.new) {

        // Before insert logic

    }

}
Coding Round
48. Queueable Apex
Apex

public class MyQueue
    implements Queueable {

    public void execute(
        QueueableContext context
    ) {

        // Async processing

    }

}
Coding Round
49. Dynamic SOQL example
Apex

String q =
    'SELECT Id FROM Account';

List<Account> accs =
    Database.query(q);
Coding Round
50. Exception handling
Apex

try {

    // Execution with risk

}
catch (DmlException e) {

    System.debug(
        'Exception: ' + e.getMessage()
    );

}
Advanced
51. What are some advanced SOQL techniques?

Advanced SOQL includes using aggregate functions (COUNT(), SUM(), AVG()), GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET, and SOQL for loops to process large result sets efficiently.

  • Aggregate queries: SELECT COUNT(Id) FROM Account
  • GROUP BY: SELECT Type, COUNT(Id) FROM Account GROUP BY Type
  • SOQL for loop: for (Account acc : [SELECT Id FROM Account])
  • Relationship queries: SELECT Name, (SELECT LastName FROM Contacts) FROM Account
  • Date literals: WHERE CreatedDate = TODAY
Apex

// Aggregate query: count accounts by type
AggregateResult[] results = [
    SELECT Type, COUNT(Id) total
    FROM Account
    GROUP BY Type
];

for (AggregateResult ar : results) {
    System.debug('Type: ' + ar.get('Type') + ' Count: ' + ar.get('total'));
}
Advanced
52. How do you query parent-child relationships in SOQL?

Use dot notation to traverse relationships. For parent-to-child, use a subquery inside the main query.

  • Parent-to-child: SELECT Name, (SELECT LastName FROM Contacts) FROM Account
  • Child-to-parent: SELECT Account.Name, LastName FROM Contact
  • Multiple levels: SELECT Account.Owner.Name FROM Contact
Apex

// Parent-to-child subquery
List<Account> accountsWithContacts = [
    SELECT Name, (SELECT LastName FROM Contacts)
    FROM Account
    WHERE Id IN :accountIds
];

// Child-to-parent dot notation
List<Contact> contacts = [
    SELECT Account.Name, LastName
    FROM Contact
    WHERE AccountId IN :accountIds
];
Advanced
53. What are advanced SOSL features?

SOSL supports searching across multiple objects, field‑specific search, fuzzy search, and returning snippets of matched text.

  • Search scope: FIND {searchTerm} IN ALL FIELDS
  • Returning specific fields: RETURNING Account(Name, Phone)
  • Search groups: FIND {searchTerm} IN NAME FIELDS RETURNING Contact
Apex

// SOSL search across multiple objects
List<List<SObject>> searchResults = [
    FIND 'Acme*' IN ALL FIELDS
    RETURNING Account(Name, Phone), Contact(FirstName, LastName)
];
Advanced
54. What are advanced DML operations?

Advanced DML includes Database class methods for partial success, upsert with external IDs, merge, and lead convert.

  • Database.insert(records, false) – allow partial success
  • upsert – insert or update based on external ID
  • merge – merge duplicate records
  • Database.convertLead – convert leads
Apex

// Database.insert with partial success
Database.SaveResult[] srList = Database.insert(accountList, false);

for (Database.SaveResult sr : srList) {
    if (!sr.isSuccess()) {
        for (Database.Error err : sr.getErrors()) {
            System.debug(err.getMessage());
        }
    }
}

// Upsert using external ID
Account acc = new Account(Name = 'Upsert Corp', ExtId__c = 'EXT-001');
upsert acc ExtId__c;
Advanced
55. What are best practices for writing triggers?

Best practices include one trigger per object, bulkification, using trigger context variables correctly, avoiding SOQL/DML inside loops, and delegating logic to handler classes.

  • Single trigger per object
  • Bulkify – process collections, not single records
  • Use helper classes for business logic
  • Avoid SOQL in loops – use maps
  • Validate before DML – use addError
Apex

// Best practice: one trigger per object, bulkified, using helper class
trigger AccountTrigger on Account (before insert, before update) {
    AccountHandler.handleTrigger(Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap, Trigger.operationType);
}
Advanced
56. What is the Trigger Handler pattern?

The Trigger Handler pattern is a design pattern where you centralize all trigger logic in a separate class (handler), making triggers lean and maintainable.

  • Handler class – contains methods for each trigger event
  • Trigger – only calls the handler
  • Testable – handler logic can be unit‑tested independently
Apex

// Handler class for trigger logic
public class AccountHandler {
    public static void handleTrigger(List<Account> oldList, List<Account> newList, Map<Id, Account> oldMap, Map<Id, Account> newMap, System.TriggerOperation op) {
        if (op == System.TriggerOperation.BEFORE_INSERT) {
            validateAccounts(newList);
        } else if (op == System.TriggerOperation.BEFORE_UPDATE) {
            validateAccounts(newList);
        }
    }
    private static void validateAccounts(List<Account> accs) {
        for (Account a : accs) {
            if (a.Name == null) a.Name.addError('Name required');
        }
    }
}
Advanced
57. How do you prevent SOQL injection?

Use bind variables (:variable) in dynamic SOQL instead of concatenating strings.

  • Bind variables: Database.query('SELECT Id FROM Account WHERE Name = :name')
  • Escape special characters – avoid raw string concatenation
  • Use with sharing to enforce security
Apex

// Using bind variables prevents injection
String searchName = 'Acme';
List<Account> accounts = Database.query('SELECT Id FROM Account WHERE Name = :searchName');
Advanced
58. What are advanced Batch Apex techniques?

Advanced techniques include stateful batch (using Database.Stateful), chaining batch jobs, and using start() to query large datasets.

  • Stateful – maintain instance variables across chunks
  • Chain – call Database.executeBatch(nextJob) in finish()
  • Scope – set scope parameter for chunk size
Apex

// Stateful batch example
global class StatefulBatch implements Database.Batchable<sObject>, Database.Stateful {
    global Integer recordCount = 0;
    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator('SELECT Id FROM Account');
    }
    global void execute(Database.BatchableContext bc, List<Account> scope) {
        recordCount += scope.size();
    }
    global void finish(Database.BatchableContext bc) {
        System.debug('Total processed: ' + recordCount);
    }
}
Advanced
59. How do you chain Queueable jobs?

Use System.enqueueJob inside the execute method to chain another Queueable job.

  • Chain: System.enqueueJob(new SecondQueueable());
  • Monitor: AsyncApexJob to track status
Apex

public class FirstQueueable implements Queueable {
    public void execute(QueueableContext ctx) {
        // do work
        System.enqueueJob(new SecondQueueable());
    }
}
Advanced
60. How do you schedule Apex classes dynamically?

Use System.schedule with a cron expression to schedule a class implementing Schedulable.

  • Cron expression: '0 0 2 * * ?' – daily at 2 AM
  • Schedule: System.schedule('Job Name', cronExp, new MySchedulable());
  • Manage: CronTrigger to view scheduled jobs
Apex

// Schedule a job dynamically
String cronExp = '0 0 2 * * ?'; // 2 AM daily
System.schedule('Daily Cleanup', cronExp, new CleanupScheduler());
Advanced
61. What are common assertions used in Apex tests?

Use System.assertEquals, System.assertNotEquals, System.assert, and System.assertThrows to validate expected behavior.

  • assertEquals: System.assertEquals(expected, actual)
  • assertNotEquals: System.assertNotEquals(expected, actual)
  • assert: System.assert(condition)
  • assertThrows: System.assertThrows(Exception.class, () => { ... })
Apex

@isTest
static void testMethod() {
    Integer expected = 5;
    Integer actual = someMethod();
    System.assertEquals(expected, actual, 'Values should match');
    System.assertNotEquals(0, actual);
    System.assert(actual > 0);
    System.assertThrows(Exception.class, () => { throw new Exception('test'); });
}
Advanced
62. What is a Test Data Factory pattern?

A Test Data Factory is a reusable class that creates test data (records) with consistent defaults, making tests easier to write and maintain.

  • Factory class – static methods to create sObjects
  • Consistency – ensures valid data for tests
  • Reusability – share across test classes
Apex

@isTest
public class TestDataFactory {
    public static Account createAccount(String name) {
        return new Account(Name = name);
    }
    public static List<Contact> createContacts(Integer count, Id accountId) {
        List<Contact> cons = new List<Contact>();
        for (Integer i = 0; i < count; i++) {
            cons.add(new Contact(LastName = 'Test' + i, AccountId = accountId));
        }
        return cons;
    }
}
Advanced
63. What does `@IsTest(SeeAllData=true)` mean?

It allows a test method to access all org data, not just test data. Use sparingly – it makes tests less isolated and more fragile.

  • Default: tests see only data created in test context
  • When to use: when you need reference data that cannot be created in tests
  • Drawback: can cause flaky tests if org data changes
Apex

@isTest(SeeAllData=true)
static void testWithAllData() {
    // This test can see real org data
    // Use sparingly
}
Advanced
64. How do you enforce CRUD and FLS in Apex?

Use Schema.sObjectType methods like isCreateable(), isReadable(), etc., to check permissions before performing DML or queries.

  • CRUD: Schema.Account.isAccessible()
  • FLS: Schema.Account.Name.isAccessible()
  • Without Sharing bypasses these checks – handle with care
Apex

if (Schema.sObjectType.Account.isCreateable() &&
    Schema.sObjectType.Account.fields.Name.isCreateable()) {
    insert new Account(Name = 'Safe');
}
Advanced
65. What is the difference between `with sharing` and `without sharing`?

with sharing enforces the user's sharing rules; without sharing runs in system mode, ignoring sharing rules.

  • with sharing – respects record-level access
  • without sharing – bypasses sharing, used for administrative operations
  • Inherited sharing – uses sharing rules of the calling class
Apex

public with sharing class SharingClass {
    // enforces sharing rules
}
public without sharing class SystemClass {
    // bypasses sharing
}
public inherited sharing class InheritedClass {
    // inherits from caller
}
Advanced
66. How do you make HTTP callouts in Apex?

Use Http, HttpRequest, and HttpResponse classes to send HTTP requests to external services.

  • Create request: HttpRequest req = new HttpRequest();
  • Set endpoint: req.setEndpoint('https://api.example.com');
  • Set method: req.setMethod('GET');
  • Send: HttpResponse res = new Http().send(req);
  • Handle response: res.getStatusCode(), res.getBody()
Apex

HttpRequest req = new HttpRequest();
req.setEndpoint('https://api.example.com/data');
req.setMethod('GET');
req.setHeader('Content-Type', 'application/json');
HttpResponse res = new Http().send(req);
if (res.getStatusCode() == 200) {
    String body = res.getBody();
}
Advanced
67. How do you test HTTP callouts?

Use HttpCalloutMock to mock the callout response in tests.

  • Implement: class MyMock implements HttpCalloutMock
  • Response: return new HttpResponse();
  • Set: Test.setMock(HttpCalloutMock.class, new MyMock());
Apex

@isTest
class MyMock implements HttpCalloutMock {
    public HttpResponse respond(HttpRequest req) {
        HttpResponse res = new HttpResponse();
        res.setStatusCode(200);
        res.setBody('{"status":"ok"}');
        return res;
    }
}

@isTest
static void testCallout() {
    Test.setMock(HttpCalloutMock.class, new MyMock());
    // call your method that does callout
}
Advanced
68. How do you expose Apex as a REST API?

Use the @RestResource annotation to define a REST service, with methods annotated @HttpGet, @HttpPost, etc.

  • Class: @RestResource(urlMapping='/myresource/*')
  • GET: @HttpGet – retrieve data
  • POST: @HttpPost – create records
  • Request/Response: RestContext.request, RestContext.response
Apex

@RestResource(urlMapping='/myresource/*')
global class MyRestResource {
    @HttpGet
    global static List<Account> getAccounts() {
        return [SELECT Id, Name FROM Account LIMIT 10];
    }
    @HttpPost
    global static String createAccount(String name) {
        Account a = new Account(Name = name);
        insert a;
        return a.Id;
    }
}
Advanced
69. What is `@RemoteAction` in Apex?

@RemoteAction allows Visualforce pages (and JavaScript) to call Apex methods asynchronously via JavaScript remoting.

  • Annotation: @RemoteAction on a static method
  • Client‑side: Visualforce.remoting.Manager.invokeAction
  • Alternatives: LWC uses @AuraEnabled instead
Apex

public class RemoteActionExample {
    @RemoteAction
    public static String sayHello(String name) {
        return 'Hello ' + name;
    }
}
Advanced
70. How do you publish Platform Events in Apex?

Use EventBus.publish to send a platform event. The event must be defined as a custom object with the __e suffix.

  • Create event: My_Event__e evt = new My_Event__e(Field__c = 'value');
  • Publish: EventBus.publish(evt);
  • Subscribe: @AuraEnabled or trigger on the event
Apex

My_Event__e evt = new My_Event__e(Message__c = 'Hello');
EventBus.publish(evt);
Advanced
71. What is Change Data Capture (CDC) and how is it used with Apex?

CDC captures changes to records and exposes them via Platform Events. Apex can listen to CDC events using triggers on the associated event object.

  • Event object: AccountChangeEvent for Account changes
  • Trigger: trigger on AccountChangeEvent
  • Use cases: real‑time integration, auditing
Apex

trigger AccountChangeTrigger on AccountChangeEvent (after insert) {
    for (AccountChangeEvent evt : Trigger.new) {
        // process change event
    }
}
Advanced
72. What is the difference between Custom Settings and Custom Metadata Types?

Custom Settings are per‑user or per‑org data stored in memory; Custom Metadata Types are deployable configuration that can be packaged and is read‑only at runtime.

  • Custom Settings: editable in UI, per‑user/org, cached
  • Custom Metadata: packaged, deployable, org‑wide, not editable at runtime
  • Performance: both are cached, but Custom Metadata is read‑only and more static
Apex

// Custom Setting
Decimal rate = CustomSetting__c.getInstance().Rate__c;

// Custom Metadata
App_Config__mdt config = App_Config__mdt.getInstance('Global');
Boolean enabled = config.Is_Active__c;
Advanced
73. What is a Custom Label and how do you use it in Apex?

Custom Labels are multilingual text strings stored in Salesforce. In Apex, you retrieve them using System.Label.LabelName.

  • Define: Setup → Custom Labels
  • Access: String msg = System.Label.MyLabel;
  • Benefits: easy translations, centralized text
Apex

String welcome = System.Label.WelcomeMessage;
Advanced
74. How do you send emails from Apex?

Use Messaging.SingleEmailMessage for simple emails, or Messaging.sendEmail for mass emails.

  • Single: Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
  • Set fields: mail.setToAddresses(['email@example.com']);
  • Send: Messaging.sendEmail(new Messaging.Email[] { mail });
  • Email Template: use setTemplateId to use a Visualforce or HTML email template
Apex

Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setToAddresses(new String[] { 'user@example.com' });
mail.setSubject('Hello');
mail.setPlainTextBody('Body text');
Messaging.sendEmail(new Messaging.Email[] { mail });
Advanced
75. How do you work with Attachments or ContentVersion in Apex?

Use ContentVersion (Files) for modern document management. Insert a ContentVersion record and link it to a parent record via ContentDocumentLink.

  • ContentVersion: ContentVersion cv = new ContentVersion(ContentLocation = 'S', PathOnClient = 'file.pdf', ...);
  • Insert: insert cv;
  • Link: ContentDocumentLink cdl = new ContentDocumentLink(LinkedEntityId = parentId, ContentDocumentId = cv.ContentDocumentId);
Apex

ContentVersion cv = new ContentVersion();
cv.ContentLocation = 'S'; // stored in Salesforce
cv.PathOnClient = 'file.pdf';
cv.Title = 'My File';
cv.VersionData = Blob.valueOf('File content');
insert cv;

// Link to parent
ContentDocumentLink cdl = new ContentDocumentLink();
cdl.LinkedEntityId = parentRecordId;
cdl.ContentDocumentId = cv.ContentDocumentId;
insert cdl;
Advanced
76. What is a SOQL for loop and why use it?

A SOQL for loop automatically queries records in batches of 200, preventing heap size errors when processing large datasets.

  • Syntax: for (Account acc : [SELECT Id FROM Account])
  • Optimization: uses internal query locator to avoid bulk memory usage
  • Best practice: use for DML on large sets
Apex

// Processes in batches of 200
for (Account acc : [SELECT Id, Name FROM Account WHERE CreatedDate = TODAY]) {
    // Do something with each account
}
Advanced
77. What are aggregate queries and when to use them?

Aggregate queries use functions like COUNT(), SUM(), AVG(), MIN(), MAX() to summarize data. They are ideal for reports and dashboards.

  • Example: SELECT Type, COUNT(Id) FROM Account GROUP BY Type
  • Group by: use GROUP BY for category summaries
  • Having: HAVING COUNT(Id) > 10
Apex

AggregateResult[] res = [
    SELECT Account.Type, COUNT(Id) total
    FROM Account
    GROUP BY Account.Type
    HAVING COUNT(Id) > 5
];
Advanced
78. What is Dynamic DML?

Dynamic DML uses Database.insert, Database.update, etc., with the SObject type and field names as strings, allowing runtime flexibility.

  • Use: when object/field names are not known at compile time
  • Example: Database.insert(new SObject('Account'));
  • Drawback: loses compile‑time type safety
Apex

// Dynamic DML with SObject type
SObject sobj = Schema.getGlobalDescribe().get('Account').newSObject();
sobj.put('Name', 'Dynamic Account');
Database.insert(sobj);
Advanced
79. What is `Schema.getGlobalDescribe()` used for?

It returns a map of all SObject types accessible to the user. Used for dynamic code that needs to discover objects and fields at runtime.

  • Usage: Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
  • Check object existence: gd.containsKey('Account')
  • Performance: call sparingly, as it's expensive
Apex

Map<String, Schema.SObjectType> gd = Schema.getGlobalDescribe();
if (gd.containsKey('Account')) {
    Schema.DescribeSObjectResult dsr = gd.get('Account').getDescribe();
    System.debug('Account fields: ' + dsr.fields.getMap().keySet());
}
Advanced
80. What is a Query Locator in Batch Apex?

The Database.QueryLocator is used in the start() method of a batch class to define the scope of records to be processed. It allows large datasets to be processed in chunks.

  • Return type: Database.QueryLocator
  • Usage: return Database.getQueryLocator('SELECT Id FROM Account');
  • Optimization: efficiently handles large query results
Apex

global Database.QueryLocator start(Database.BatchableContext bc) {
    return Database.getQueryLocator('SELECT Id FROM Account WHERE CreatedDate = TODAY');
}
Advanced
81. What does `Database.Stateful` do in Batch Apex?

It marks a batch class as stateful, allowing instance variables to retain their values across chunk executions. Useful for accumulating totals or counters.

  • Annotation: global class MyBatch implements Database.Batchable<sObject>, Database.Stateful
  • Use case: counting total records processed across all chunks
  • Caution: state can be large and cause heap issues
Apex

global class MyBatch implements Database.Batchable<sObject>, Database.Stateful {
    global Integer total = 0;
    // ...
    global void execute(Database.BatchableContext bc, List<sObject> scope) {
        total += scope.size();
    }
    // total is preserved across chunks
}
Advanced
82. Can Queueable Apex run with `without sharing`?

Yes. You can specify without sharing on the Queueable class to bypass sharing rules, or with sharing to enforce them.

  • Default: if not specified, uses sharing rules of the calling context
  • Override: explicitly declare public without sharing class MyQueueable
  • Best practice: explicitly define to avoid surprises
Apex

public without sharing class MyQueueable implements Queueable {
    public void execute(QueueableContext ctx) {
        // runs in system mode
    }
}
Advanced
83. How do you create a custom exception in Apex?

Define a class that extends Exception. Then throw it using throw new MyException('message');

  • Definition: class MyException extends Exception
  • Throw: throw new MyException('Error occurred');
  • Catch: catch (MyException e) { ... }
Apex

public class MyException extends Exception {}
// throw new MyException('Something went wrong');
Advanced
84. How do you debug Apex code?

Use System.debug statements, check debug logs in the Developer Console, and use checkpoints or Apex debugger.

  • System.debug: System.debug('Value: ' + variable);
  • Log levels: System.debug(LoggingLevel.INFO, 'message');
  • Developer Console: execute anonymous, query editor, and logs
Apex

System.debug(LoggingLevel.INFO, 'Variable value: ' + myVar);
// Check debug logs in Developer Console
Advanced
85. What are some performance best practices in Apex?

Minimize SOQL queries, avoid DML in loops, use collections, bulkify code, and leverage batch/queueable for large operations.

  • SOQL: query outside loops; use maps to avoid repeated queries
  • DML: use collections, not single records
  • Bulkification: design for multirecord operations
  • Asynchronous: use future/queueable/batch for heavy lifting
Apex

// BAD: SOQL in loop
// for (Account a : accounts) {
//     List<Contact> cons = [SELECT Id FROM Contact WHERE AccountId = :a.Id];
// }

// GOOD: bulk query
Set<Id> accIds = new Map<Id, Account>(accounts).keySet();
Map<Id, List<Contact>> contactMap = new Map<Id, List<Contact>>();
for (Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accIds]) {
    if (!contactMap.containsKey(c.AccountId)) contactMap.put(c.AccountId, new List<Contact>());
    contactMap.get(c.AccountId).add(c);
}
Advanced
86. Why is it bad to have SOQL inside a loop?

It causes multiple SOQL queries, quickly hitting governor limits. Always extract data beforehand and use maps for lookups.

  • Problem: each iteration consumes a query
  • Fix: query once, store in map, then iterate
  • Governor limit: max 100 SOQL queries per transaction
Apex

// BAD: query inside loop
for (Account a : Trigger.new) {
    List<Contact> cons = [SELECT Id FROM Contact WHERE AccountId = :a.Id];
}

// GOOD: query once, use map
Set<Id> accIds = new Map<Id, Account>(Trigger.new).keySet();
List<Contact> allContacts = [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accIds];
Map<Id, List<Contact>> contactsByAcc = new Map<Id, List<Contact>>();
for (Contact c : allContacts) {
    if (!contactsByAcc.containsKey(c.AccountId)) contactsByAcc.put(c.AccountId, new List<Contact>());
    contactsByAcc.get(c.AccountId).add(c);
}
Advanced
87. How do you use maps for efficient lookups?

Query related records once, put them into a map keyed by the lookup field, then retrieve in a loop.

  • Example: Map<Id, Account> accountMap = new Map<Id, Account>([SELECT Id FROM Account]);
  • Usage: Account acc = accountMap.get(contact.AccountId);
  • Benefit: O(1) lookups, no extra queries
Apex

Map<Id, Account> accountMap = new Map<Id, Account>([SELECT Id, Name FROM Account WHERE Id IN :accountIds]);
for (Contact c : contacts) {
    Account acc = accountMap.get(c.AccountId);
    // use acc
}
Advanced
88. Why avoid DML inside loops?

DML statements inside loops cause multiple DML operations, consuming governor limits quickly and reducing performance.

  • Problem: each iteration is a separate DML call
  • Fix: collect records in a list and DML outside the loop
  • Governor limit: max 150 DML statements per transaction
Apex

// BAD: DML inside loop
for (Account a : accounts) {
    update a;
}

// GOOD: collect in list
List<Account> toUpdate = new List<Account>();
for (Account a : accounts) {
    toUpdate.add(a);
}
update toUpdate;
Advanced
89. What are some bulkification patterns?

Use maps for lookups, sets for distinct values, and lists for DML operations. Process data in collections rather than individual records.

  • Pattern 1: collect IDs in set, query once
  • Pattern 2: use map to store related records
  • Pattern 3: accumulate records in list, DML once
Apex

// Use set for distinct IDs
Set<Id> ids = new Set<Id>();
for (Account a : accounts) ids.add(a.Id);

// Use map for lookups
Map<Id, Account> accMap = new Map<Id, Account>([SELECT Id FROM Account]);

// Use list for DML
List<Contact> toInsert = new List<Contact>();
// fill list
insert toInsert;
Advanced
90. Can you have multiple triggers on the same object?

Yes, but it's not recommended. Best practice is one trigger per object, with logic delegated to handler classes.

  • Why one? – avoids unpredictable execution order
  • If multiple: order is not guaranteed
  • Best practice: single trigger, handler pattern
Apex

// Not recommended to have multiple triggers
// Best: one trigger per object
// Trigger on Account (before insert)
trigger AccountTrigger on Account (before insert) {
    AccountHandler.handleInsert(Trigger.new);
}
Advanced
91. What is the order of execution in a trigger?

Order: Before triggers, After triggers, Assignment rules, Auto‑response rules, Workflow rules, Process Builder, Escalation rules, and finally Commit.

  • Before insert/update – modify fields before saving
  • After insert/update – use updated record IDs for related operations
  • System order: see Salesforce docs for full sequence
Apex

// Order: Before triggers → After triggers → Assignment rules → Auto-response → Workflow → Process Builder → Escalation → Commit.
// Example showing before/after usage
trigger OrderTrigger on Order (before insert, after insert) {
    if (Trigger.isBefore) {
        // modify fields
    } else if (Trigger.isAfter) {
        // create related records
    }
}
Advanced
92. What are the main trigger context variables?
  • Trigger.new – new records (before/after insert, after update)
  • Trigger.old – old records (before/after update, before delete)
  • Trigger.newMap – map of new records by ID (after insert, after update)
  • Trigger.oldMap – map of old records by ID (before update, before delete)
  • Trigger.isInsert, Trigger.isUpdate, Trigger.isDelete, Trigger.isUndelete
Apex

// Trigger.new - new records
// Trigger.old - old records
// Trigger.newMap - map of new by Id
// Trigger.oldMap - map of old by Id
// Trigger.isInsert, isUpdate, isDelete, isUndelete, isBefore, isAfter
if (Trigger.isInsert && Trigger.isBefore) {
    for (Account a : Trigger.new) {
        a.Name = a.Name.toUpperCase();
    }
}
Advanced
93. How do you prevent record saving in a trigger?

Use addError on a record or on a field to prevent the DML operation and display a user‑friendly error message.

  • Record error: record.addError('Invalid data');
  • Field error: record.fieldName.addError('Invalid value');
  • Works in before/after triggers
Apex

if (Trigger.isBefore) {
    for (Account a : Trigger.new) {
        if (a.Name == null) {
            a.Name.addError('Name cannot be null');
        }
        // field-level error
        if (a.Industry == null) {
            a.Industry.addError('Industry required');
        }
    }
}
Advanced
94. When to use before vs after triggers?

Before triggers are used to modify field values before they are saved. After triggers are used when you need the record ID (e.g., for creating related records).

  • Before: validation, field updates, cross‑object calculations
  • After: creating child records, sending emails, updating external systems
Apex

// Before: modify fields before save
// After: need record IDs, create related records
trigger ContactTrigger on Contact (before insert) {
    for (Contact c : Trigger.new) {
        c.Description = 'New';
    }
}
trigger AccountTrigger on Account (after insert) {
    for (Account a : Trigger.new) {
        // Create a default contact using the new Account Id
        insert new Contact(LastName = 'Default', AccountId = a.Id);
    }
}
Advanced
95. What is a Trigger Framework?

A trigger framework is a structured approach (often using a base class) to handle trigger events, enforce order, and improve maintainability.

  • Base class – handles trigger context variables and event dispatching
  • Handler classes – implement specific logic for each event
  • Benefits: consistent pattern, easier testing, less code duplication
Apex

// Base trigger framework (simplified)
public abstract class TriggerHandler {
    public static void run() {
        // handle context, invoke specific handlers
    }
}
// Then each trigger calls TriggerHandler.run();
Advanced
96. What is the minimum code coverage required for deployment?

Salesforce requires at least 75% overall code coverage for Apex classes and triggers to be deployed to production. Triggers must have 1% coverage on their own.

  • Overall: 75% of all Apex code must be covered
  • Triggers: must have at least 1% coverage
  • Best practice: aim for 100% to avoid deployment issues
Apex

// Minimum 75% overall coverage
// Triggers need at least 1% coverage
// Write test classes to cover all logic
@isTest
class MyTest {
    @isTest static void testMethod() {
        // cover code
    }
}
Advanced
97. How do you isolate test data?

Use @IsTest annotations and the Test class methods like Test.startTest() and Test.stopTest() to create test data that is automatically rolled back.

  • Test.startTest() – resets governor limits for the code block
  • Test.stopTest() – executes asynchronous calls
  • Data isolation: test data doesn't commit to the org
Apex

@isTest
static void testIsolation() {
    Test.startTest();
    // perform DML, async calls
    Test.stopTest(); // executes async code
    // assertions after
}
Advanced
98. What are mock frameworks in Apex testing?

Mock frameworks like stub (using System.StubProvider) allow you to create test doubles for dependencies, isolating the class under test.

  • Stub: implement System.StubProvider to return mock data
  • Test.setMock: used for HTTP callouts and other external dependencies
  • Benefits: fast, reliable, isolated unit tests
Apex

// Using StubProvider (example)
public class MyMock implements System.StubProvider {
    public Object handleMethodCall(Object stubbedObject, String stubbedMethodName, Type returnType, List<Type> listOfParamTypes, List<String> listOfParamNames, List<Object> listOfArgs) {
        if (stubbedMethodName == 'getData') {
            return 'Mock Data';
        }
        return null;
    }
}
// Use Test.createStub() to apply
Advanced
99. Can you make HTTP callouts from a trigger?

No, directly calling out from a trigger is not allowed. Instead, you must call a @future or Queueable method that performs the callout.

  • Why? – triggers are synchronous, callouts require async
  • Solution: use @future(callout=true) or Queueable
  • Best practice: use Queueable for better monitoring
Apex

// In trigger, call a future method
@future(callout=true)
public static void doCallout(String param) {
    // make callout
}
// Trigger: MyClass.doCallout('test');
Advanced
100. What are the top 5 Apex best practices?

1. Bulkify – always design for multirecord operations.
2. Avoid SOQL/DML in loops – use maps and collections.
3. Use with sharing unless you have a strong reason not to.
4. Handle exceptions – try-catch and addError where appropriate.
5. Write test classes – aim for 100% coverage and use data factories.

Apex

// Top 5 best practices summarized:
// 1. Bulkify – process collections, not single records.
// 2. Avoid SOQL/DML in loops – use maps and sets.
// 3. Use with sharing (unless system mode needed).
// 4. Handle exceptions properly (try-catch, addError).
// 5. Write comprehensive test classes (>=75% coverage).