Ace Your Salesforce Developer Interview
Apex Programming
Writing Backend Logic with Apex
Apex is Salesforce's proprietary, object-oriented programming language. If you have a background in Java or C#, you'll find its syntax familiar. Apex runs on Salesforce's servers, allowing you to add custom business logic to most system events, including button clicks, record updates, and even calls from external web services.
Apex is strongly-typed and object-oriented, similar to Java, making it familiar for developers with Java experience.
Code is compiled, stored, and run entirely in the cloud. Let's look at a simple Apex class. A class is a blueprint for creating objects. This one defines a single method, helloWorld, which can be called to return a string.
public class MyFirstClass {
// This is a method that returns a String.
public static String helloWorld() {
return 'Hello World';
}
}
You can execute this code in an anonymous window in the Developer Console to see the output. Anonymous execution is a great way to test small snippets of logic without saving them as a class or trigger.
Data Types and Control Flow
Like any language, Apex has variables to store data. It includes basic data types like Integer, Double, String, and Boolean. It also has types that are unique to Salesforce.
sObject
noun
A generic or specific Salesforce object, such as an Account, a Contact, or a custom object. It's the primary way you interact with record data in Apex.
When you need to handle multiple pieces of data, you use collections. Apex supports three types of collections:
- Lists: An ordered collection of elements of the same data type. Duplicates are allowed.
- Sets: An unordered collection of unique elements of the same data type.
- Maps: A collection of key-value pairs. Keys must be unique, and can be any primitive data type, while values can be any data type.
// A List of strings
List<String> colors = new List<String>{'Red', 'Green', 'Blue'};
// A Set of integers, duplicates are ignored
Set<Integer> uniqueNumbers = new Set<Integer>{1, 2, 2, 3};
// A Map from an ID to an Account sObject
Map<Id, Account> accountMap = new Map<Id, Account>();
To control the flow of your code, you'll use conditional statements and loops. if-else statements execute different blocks of code based on a boolean condition. for loops iterate over collections to process multiple records efficiently.
// Example of an if-else statement
Integer leadScore = 75;
if (leadScore > 80) {
System.debug('This is a hot lead!');
} else {
System.debug('This lead needs nurturing.');
}
// Example of a for loop iterating over a list of Accounts
List<Account> newAccounts = [SELECT Name FROM Account WHERE CreatedDate = TODAY];
for (Account acc : newAccounts) {
acc.Description = 'This account was created today.';
}
// The DML update is outside the loop for bulkification
update newAccounts;
Automating with Triggers
Triggers are Apex scripts that execute before or after data manipulation language (DML) events occur. Think of them as automated actions that fire when records are inserted, updated, deleted, or undeleted. They are the primary tool for implementing custom logic around record operations.
A trigger runs in a specific context. For example, a trigger on the Account object can fire before an account is inserted (before insert) or after it's updated (after update). Inside the trigger, you have access to context variables like Trigger.new (a list of the new versions of records) and Trigger.old (a list of the old versions of records in an update or delete scenario).
/*
This trigger runs before new Accounts are inserted.
It sets a standard description for any Account
with a 'Technology' industry.
*/
trigger AccountTrigger on Account (before insert) {
for (Account acc : Trigger.new) {
if (acc.Industry == 'Technology') {
acc.Description = 'This is a key tech account.';
}
}
}
A common best practice is to have only one trigger per sObject. This helps you control the order of execution and makes your logic easier to manage. You can create a helper class to contain the logic and call it from the trigger.
Bulkification and Error Handling
The Salesforce platform is a shared environment, so it enforces governor limits to ensure no single process monopolizes resources. A critical concept for staying within these limits is bulkification. This means writing your code to handle multiple records at once, not just one.
The most common mistake is placing SOQL queries or DML statements inside a loop. If a trigger fires with 200 records, a query inside a loop would execute 200 times, quickly hitting the limit of 100 SOQL queries per transaction. The correct pattern is to query once, process the data, and perform DML once.
// BAD: SOQL query inside a loop
for (Contact con : Trigger.new) {
// This will run for every contact, hitting governor limits fast!
Account acc = [SELECT Id, Name FROM Account WHERE Id = :con.AccountId];
}
// GOOD: Bulk-safe pattern
Set<Id> accountIds = new Set<Id>();
for (Contact con : Trigger.new) {
accountIds.add(con.AccountId);
}
// Query once with all the necessary IDs
Map<Id, Account> accountMap = new Map<Id, Account>([SELECT Id, Name FROM Account WHERE Id IN :accountIds]);
// Now process the contacts with the account data
for (Contact con : Trigger.new) {
Account parentAccount = accountMap.get(con.AccountId);
// ... do something with the parentAccount ...
}
Finally, you must anticipate and handle errors. What if a DML operation fails because of a validation rule? Unhandled exceptions can cause the entire transaction to roll back. You can use try-catch blocks to gracefully manage errors, log them, and provide clear feedback to the user.
try {
// DML operation that might fail
insert contactList;
} catch (DmlException e) {
// Catch the specific exception
System.debug('A DML error occurred: ' + e.getMessage());
// You could add error handling logic here, like creating a log record
// or adding an error message to the page for the user to see.
}
Now that you've seen the fundamentals of Apex, you can start building more complex automations.
