Pages

Saturday, August 1, 2026

Apex Interview Questions — Part 2 (Governor Limits, SOQL/SOSL, Bulkification)

 

1. What are the key governor limits every Apex developer should know?

The most commonly tested ones are: 100 SOQL queries per transaction (200 for async), 150 DML statements per transaction, 10,000 DML rows per transaction, 50,000 SOQL query rows retrieved, and 6MB heap size (12MB for async). These exist because Apex runs on shared, multi-tenant infrastructure.

2. What is the difference between synchronous and asynchronous governor limits?

Asynchronous Apex (Future, Queueable, Batch, Scheduled) generally gets higher limits than synchronous Apex — for example, 200 SOQL queries instead of 100 — because async transactions run separately from the user's request and don't need to complete within the same response window.

3. What happens when a governor limit is exceeded?

Salesforce throws a System.LimitException, which — unlike most other exceptions — cannot always be caught and safely continued from, since it typically indicates the current transaction has already used up a shared platform resource. The entire transaction is rolled back.

4. How can you check how close you are to a limit at runtime?

The Limits class provides methods like Limits.getQueries(), Limits.getLimitQueries(), Limits.getDmlStatements(), and Limits.getLimitDmlStatements(), which let you inspect current usage against the maximum allowed within a transaction.

apex
System.debug('SOQL used: ' + Limits.getQueries() + ' / ' + Limits.getLimitQueries());

5. Why should SOQL queries never be placed inside a for loop?

Each iteration of the loop would count as a separate query against the 100-query-per-transaction limit, so processing a batch of 200 records could exceed the limit almost immediately. The fix is to query once outside the loop and iterate over the results in memory.

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

// Good
Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();
for (Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]) {
    // group in memory
}

6. What is SOQL used for, and what are its main clauses?

SOQL retrieves records from Salesforce's database. It supports SELECT, FROM, WHERE, ORDER BY, LIMIT, GROUP BY, and relationship queries, but doesn't support arbitrary joins the way SQL does — relationships are traversed instead through parent-child or child-parent syntax.

7. How do you write a parent-to-child SOQL query?

You use a subquery with the child relationship name (usually the plural of the child object, or a custom relationship name ending in __r for custom objects):

apex
List<Account> accs = [
    SELECT Name, (SELECT LastName FROM Contacts)
    FROM Account
];

8. How do you write a child-to-parent SOQL query?

You traverse upward using dot notation on the relationship field:

apex
List<Contact> cons = [
    SELECT LastName, Account.Name, Account.Industry
    FROM Contact
];

9. What is SOSL, and when would you use it over SOQL?

SOSL performs a text search across multiple objects and fields at once, useful when you're searching for a term (like a name or keyword) but don't know which object or field it lives in. SOQL is better when you already know the specific object and filtering criteria.

apex
List<List<sObject>> results = [FIND 'Acme' IN ALL FIELDS RETURNING Account(Name), Contact(LastName)];

10. Can SOSL return records from multiple different objects in one query?

Yes — that's one of its main advantages over SOQL. The result is a list of lists, where each inner list corresponds to one of the object types specified in the RETURNING clause.

11. What is bulkification and why is it a best practice?

Bulkification means designing Apex logic to correctly and efficiently process multiple records in a single execution context — for example, a trigger firing on 200 records from a data import — rather than assuming only one record will ever pass through at a time.

12. What's a common real-world bulkification mistake in trigger code?

Placing a SOQL query or DML statement directly inside a trigger's for loop over Trigger.new. This works fine with one test record but fails in production once more than roughly 100-150 records are processed in a single batch.

13. How do you avoid DML statements inside a loop?

Collect the records that need to be inserted, updated, or deleted into a list first, then perform a single DML call after the loop completes:

apex
List<Contact> toUpdate = new List<Contact>();
for (Contact c : contacts) {
    c.Description = 'Reviewed';
    toUpdate.add(c);
}
update toUpdate;

14. What is the difference between upsert and using insert/update separately?

upsert automatically determines whether each record already exists (based on its Id or a specified external Id field) and either inserts or updates it accordingly, in a single DML statement — simplifying logic that would otherwise require separate insert and update handling.

15. What is an external ID field, and how is it used with upsert?

An external ID field is a custom field marked as an External ID in its field-level settings, typically used to match records against an external system's identifier during data integration. upsert can use this field instead of the Salesforce record Id to determine whether a match exists.

apex
upsert accountList External_Id__c;

16. What is the maximum number of records that can be processed by a single DML statement?

Apex allows up to 10,000 records per DML operation within a single transaction, which is why very large data loads are often handled through Batch Apex rather than a single synchronous DML call.

17. What is the difference between Database.insert() and a plain insert statement?

A plain insert statement fails the entire operation if even one record throws an error. Database.insert(records, false) allows partial success — records that fail don't stop the others from being inserted, and you can inspect a Database.SaveResult list to see which succeeded or failed.

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

18. What is a Database.SaveResult, and what information does it provide?

It's the object returned by Database.insert/update/delete calls made with partial-success mode enabled. Each result indicates whether that specific record succeeded (isSuccess()) and, if not, provides error details through getErrors().

19. Why is querying only the fields you need considered a best practice?

Retrieving unnecessary fields increases heap usage and can slow down query performance, especially with large result sets. It also avoids hitting the 50,000-row query limit unnecessarily and keeps code more maintainable by making dependencies explicit.

20. What is LIMIT in SOQL and why should you use it deliberately?

LIMIT restricts the number of records a SOQL query returns. Using it deliberately (rather than relying on default behavior) helps control performance, avoid exceeding row limits, and makes intent clear — for example, LIMIT 1 when you only expect a single matching record.

apex
Account acc = [SELECT Id FROM Account WHERE Name = 'Acme Corp' LIMIT 1];

No comments: