Best practices for writing Apex code in Salesforce

Here are some best practices for writing Apex code in Salesforce:

  1. Use governor limits wisely: Apex has limits on the number of records that can be processed and the number of database calls that can be made in a single transaction. Be mindful of these limits and design your code accordingly.
  2. Write efficient and optimized code: Use efficient algorithms and data structures, avoid nested loops, and perform bulk operations whenever possible to minimize processing time.
  3. Use Design Patterns: Use design patterns like Factory, Singleton, and Decorator to write reusable and maintainable code.
  4. Write Test Cases: Write test cases to verify that your code is working correctly and to avoid regressions during future deployments.
  5. Use Exception Handling: Use exception handling to catch and handle errors in your code. This makes your code more robust and less prone to bugs.
  6. Use bulkification: For processing large amounts of data, bulkify your code to handle bulk data efficiently and within governor limits.
  7. Debug Logs: Use Debug Logs to identify and resolve issues in your code.
  8. Document Code: Document your code to make it easier for others to understand and maintain.

Governor limits are limits set by Salesforce on the amount of resources an Apex code can consume in a single transaction. The purpose of governor limits is to prevent a single transaction from monopolizing the available resources, thereby affecting the performance of the system as a whole.

For example, the Apex code governor limit for the maximum number of records that can be processed in a single transaction is 50,000 records. If your Apex code tries to process more than 50,000 records in a single transaction, you will receive the following error message: “System.LimitException: Too many records”.

To avoid hitting governor limits, you can design your code to break down the processing into smaller batches of records and process each batch in separate transactions. This way, you can stay within the limits imposed by Salesforce and maintain the performance of the system.

Here is a simple example that demonstrates how to process a large number of records in batches:

List<Account> accounts = [SELECT Id FROM Account];

Integer batchSize = 2000;

for (Integer i=0; i<accounts.size(); i+=batchSize) {
List<Account> subList = accounts.subList(i, Math.min(i+batchSize, accounts.size()));

// process the sublist of accounts in a single transaction
// ...
}

In this example, the list of accounts is broken down into smaller batches of 2000 records each, and each batch is processed in a separate transaction. This way, the code stays within the governor limit of 50,000 records and the performance of the system is maintained.


Writing efficient and optimized code is important in Salesforce, as it ensures that your code runs quickly and does not exceed governor limits. Some tips to write efficient and optimized code in Salesforce include

  1. Minimize database calls: Avoid making unnecessary database calls, as each database call consumes resources and counts towards the governor limit for a maximum number of database calls. Instead, use a single query to retrieve all the data you need, and then process it in memory.
  2. Avoid SOQL and DML statements in loops: SOQL and DML statements should be avoided within loops, as each iteration of the loop consumes resources and counts towards the governor limit for a maximum number of database calls. Instead, bulkify your code to perform the database operations in bulk, outside of the loop.
  3. Use collections: Use collections such as lists, maps, and sets to store and manipulate data, as they are more efficient than traditional arrays and can help you minimize the number of database calls.
  4. Avoid nested loops: Nested loops are less efficient than single loops, as they consume more resources and take longer to execute. Instead, use a single loop and perform the necessary calculations within the loop.
  5. Use the limits methods: Use the limits.getHeapSize() and limits.getLimitHeapSize() methods to monitor the heap size during execution, and optimize your code to minimize the heap size.
  6. Use the Profiler: Use the Apex Profiler to identify the performance bottlenecks in your code and optimize the code accordingly.

By following these tips, you can write efficient and optimized code in Salesforce, ensuring that your code runs quickly and within governor limits.


Design patterns are proven solutions to common programming problems. In Apex, design patterns provide a structured approach to solving complex problems, making your code more maintainable and reusable. Here are some examples of design patterns in Apex

Singleton Pattern: The Singleton pattern is used to ensure that a class has only one instance, and provides a single point of access to that instance. Here is an example:

public class SingletonExample {
private static SingletonExample instance;
private SingletonExample() {}

public static SingletonExample getInstance() {
if (instance == null) {
instance = new SingletonExample();
}
return instance;
}
}

In this example, the SingletonExample class ensures that only one instance of the class is created, and provides a single point of access to that instance through the getInstance() method.

Factory Pattern: The Factory pattern is used to create objects without specifying the exact class of object that will be created. Here is an example:

public interface Shape {
void draw();
}

public class Circle implements Shape {
public void draw() {
System.debug('Drawing Circle');
}
}

public class Square implements Shape {
public void draw() {
System.debug('Drawing Square');
}
}

public class ShapeFactory {
public static Shape getShape(String shapeType) {
if (shapeType.equalsIgnoreCase('CIRCLE')) {
return new Circle();
} else if (shapeType.equalsIgnoreCase('SQUARE')) {
return new Square();
}
return null;
}
}

In this example, the Shape interface defines the behavior for drawing shapes, and the Circle and Square classes implement the Shape interface. The ShapeFactory class provides a method getShape() for creating the appropriate shape object based on the shapeType argument.

Decorator Pattern: The Decorator pattern is used to add additional behavior to an existing object, without affecting the behavior of other objects from the same class. Here is an example:

public abstract class Component {
public abstract void operation();
}

public class ConcreteComponent extends Component {
public void operation() {
System.debug('ConcreteComponent operation');
}
}

public abstract class Decorator extends Component {
protected Component component;

public void setComponent(Component component) {
this.component = component;
}

public void operation() {
if (component != null) {
component.operation();
}
}
}

public class ConcreteDecoratorA extends Decorator {
public void operation() {
super.operation();
System.debug('ConcreteDecoratorA operation');
}
}

public class ConcreteDecoratorB extends Decorator {
public void operation() {
super.operation();
System.debug('ConcreteDecoratorB operation');
}
}

In this example, the Component class defines the basic behavior for a component and the ConcreteComponent class provides an implementation for that behavior. The Decorator class adds additional behavior to the Component class, and the `ConcreteDecoratorA

Template Method Pattern: The Template Method Pattern is used to define the basic steps of an algorithm, and allow subclasses to provide their own implementation of the steps. Here’s an example in Apex:

public abstract class PizzaMakingProcess {
public final void makePizza() {
prepareDough();
addSauce();
addToppings();
bake();
}

protected abstract void prepareDough();
protected abstract void addSauce();
protected abstract void addToppings();
protected abstract void bake();
}

public class MargheritaPizza extends PizzaMakingProcess {
protected void prepareDough() {
System.debug('Preparing Dough for Margherita Pizza');
}

protected void addSauce() {
System.debug('Adding Tomato Sauce for Margherita Pizza');
}

protected void addToppings() {
System.debug('Adding Mozzarella and Basil Toppings for Margherita Pizza');
}

protected void bake() {
System.debug('Baking Margherita Pizza');
}
}

public class PepperoniPizza extends PizzaMakingProcess {
protected void prepareDough() {
System.debug('Preparing Dough for Pepperoni Pizza');
}

protected void addSauce() {
System.debug('Adding Tomato Sauce for Pepperoni Pizza');
}

protected void addToppings() {
System.debug('Adding Mozzarella, Pepperoni and Olive Toppings for Pepperoni Pizza');
}

protected void bake() {
System.debug('Baking Pepperoni Pizza');
}
}

In this example, the PizzaMakingProcess class defines the basic steps of making a pizza (preparing the dough, adding sauce, adding toppings, and baking). The MargheritaPizza and PepperoniPizza classes extend the PizzaMakingProcess class and provide their own implementation of the steps. The makePizza() method in the PizzaMakingProcess class is defined as final, which means that subclasses cannot override it, but they can provide their own implementation of the individual steps. This ensures that the basic steps of making a pizza are followed while allowing for customization based on the type of pizza being made.


Write test cases to verify that your code is working correctly and to avoid regressions during future deployments.

In Apex, writing test cases is an important part of the development process. Test cases help ensure that your code is working as expected, and provide a way to validate that changes to your code do not break existing functionality.

Here’s an example of how to write test cases in Apex:

@isTest
private class AccountTest {
static testMethod void testCreateAccount() {
Account a = new Account();
a.Name = 'Test Account';
insert a;

Account insertedAccount = [SELECT Name FROM Account WHERE Id = :a.Id];
System.assertEquals(insertedAccount.Name, 'Test Account');
}

static testMethod void testUpdateAccount() {
Account a = new Account();
a.Name = 'Test Account';
insert a;

a.Name = 'Updated Test Account';
update a;

Account updatedAccount = [SELECT Name FROM Account WHERE Id = :a.Id];
System.assertEquals(updatedAccount.Name, 'Updated Test Account');
}
}

In this example, the test cases are defined in a class annotated with the @isTest annotation. The test methods, testCreateAccount and testUpdateAccount, are defined using the static testMethod keyword.

The testCreateAccount method creates a new Account record, inserts it into the database, and then query for the inserted account to verify that it was created successfully.

The testUpdateAccount method does the same thing as the testCreateAccount method, but also updates the Account record and queries for the updated record to verify that the update was successful.

To run the test cases, you can use the Salesforce user interface or the Salesforce CLI. When you run the test cases, the results will be displayed in the interface, showing which test cases passed and which test cases failed. If a test case fails, the error message will be displayed, allowing you to debug the issue and make any necessary changes to your code.


Exception handling is an important aspect of Apex programming, as it allows you to handle unexpected conditions in your code. Here’s an example of how to use exception handling in Apex:

public class DivideNumbers {
public static Integer divide(Integer num1, Integer num2) {
try {
return num1 / num2;
} catch (Exception e) {
System.debug('An error occurred: ' + e.getMessage());
return null;
}
}
}

In this example, the divide method takes two Integer inputs and returns the result of dividing the first input by the second. The division operation is enclosed in a try-catch block. If an exception occurs during the division (such as a DivideByZeroException), the catch block will catch the exception and print an error message to the debug log. The method will then return null to indicate that an error occurred.

To use the DivideNumbers class, you can call the divide method as follows:

Integer result = DivideNumbers.divide(10, 2);
System.debug(result); // Output: 5

result = DivideNumbers.divide(10, 0);
System.debug(result); // Output: null

In this example, the first call to the divide method returns the expected result of 5, while the second call results in a DivideByZeroException, which is caught by the catch block and handled appropriately.


Bulkification is a programming technique in Apex that allows you to handle large amounts of data efficiently. In Apex, governor limits are in place to prevent excessive use of system resources and ensure stable performance for all customers. However, these limits can make it difficult to process large amounts of data in a single transaction.

Bulkification helps to overcome these limitations by breaking up large operations into smaller, more manageable chunks. Here’s an example of how to bulkify Apex code:

public class ContactHandler {
public static void updateContacts(List<Contact> contacts) {
Map<Id, Contact> contactMap = new Map<Id, Contact>(contacts);
List<Contact> updateContacts = new List<Contact>();

for (Contact c : [SELECT Id, LastName, Email FROM Contact WHERE Id IN :contactMap.keySet()]) {
c.LastName = contactMap.get(c.Id).LastName;
c.Email = contactMap.get(c.Id).Email;
updateContacts.add(c);
}

if (!updateContacts.isEmpty()) {
update updateContacts;
}
}
}

In this example, the updateContacts method takes a list of Contact records as input and updates their LastName and Email fields. The method uses a map to associate each contact record with its corresponding updates, reducing the number of SOQL queries required to retrieve the data. The method then updates the records in bulk using a single update statement, rather than making individual update calls for each record.

By bulkifying the code, you can reduce the number of database transactions required to process large amounts of data, which can help to avoid exceeding governor limits and improve performance. Additionally, bulkifying your code can make it easier to maintain and scale as your application grows.


Debug logs are a useful tool for debugging Apex code in Salesforce. They allow you to log detailed information about the execution of your code and view the logs in the Salesforce UI. Here’s an example of how to use debug logs in Apex:

public class DebugExample {
public static void doSomething() {
System.debug('Starting doSomething');
List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 10];
for (Account a : accounts) {
System.debug('Account: ' + a);
}
System.debug('Finished doSomething');
}
}

In this example, the doSomething method retrieves a list of 10 Account records and logs some debug messages using the System.debug method. To enable debug logging for this class, you’ll need to set up a trace flag in the Salesforce UI.

To view the debug logs, go to the Debug Logs page in the Salesforce UI (Setup > Monitoring > Debug Logs). You can filter the logs by user, start time, and other criteria to find the logs you’re interested in. Once you’ve found the logs you need, you can view the messages logged by the System.debug method and use them to debug your code.

Debug logs can be especially useful for debugging complex logic or for tracking the flow of execution through your code. By logging meaningful messages at key points in your code, you can get a clearer picture of what’s happening and quickly identify and resolve issues.


Documenting code is an important part of the software development process. It helps to make your code more readable, understandable, and maintainable, both for yourself and for others who may need to work with your code in the future. Here’s an example of how to document Apex code:

/**
* This class provides a method for updating a contact's last name and email address.
*
* @author John Doe
* @version 1.0
*/

public class ContactHandler {
/**
* Updates the last name and email address of a list of contacts.
*
* @param contacts A list of Contact records to update.
*/

public static void updateContacts(List<Contact> contacts) {
// Implementation details go here...
}
}

In this example, the class and method have been documented using JavaDoc-style comments. The class comment provides an overview of what the class does, while the method comment provides details on how to use the method, including what parameters it takes and what it returns.

Documenting your code using comments like this can help to make it easier for others to understand how your code works and how it can be used. This can be especially helpful for code that is complex, or for code that will be used by others who may not be familiar with your implementation.

Additionally, well-documented code can also make it easier for you to pick up where you left off if you haven’t worked on a project in a while, or if you need to make changes to someone else’s code. By having clear, concise documentation, you can more quickly get up to speed on how the code works and what changes need to be made.

In conclusion, writing high-quality Apex code requires following best practices that can improve the performance, reliability, and maintainability of your code. Some of the key best practices for writing Apex code are:

  1. Use governor limits to ensure your code runs within the limits of the Salesforce platform.
  2. Write efficient and optimized code to reduce processing time and minimize the impact on system performance.
  3. Use design patterns to structure your code in a way that makes it easy to understand, maintain, and reuse.
  4. Write comprehensive test cases to validate the behavior of your code and ensure it works as expected.
  5. Use exception handling to handle errors and unexpected conditions gracefully, without impacting the stability of your code.
  6. Use bulkification to handle large amounts of data and reduce the impact on system performance.
  7. Use debug logs to help diagnose and resolve issues in your code.

By following these best practices, you can write high-quality Apex code that meets the requirements of your organization and provides value to your users.