Salesforce Apex is a strong, object-oriented programming language that lets developers create complicated business logic, automate tasks, and connect to other systems. To write Apex code that is efficient, scalable, and easy to maintain, you must follow best practices very closely. If you write Apex code that isn’t good, it can cause governor limit exceptions, slow down performance, create security holes, and make maintenance a nightmare.
1. Governor Limits & Bulkification in Salesforce:
In a multi-tenant environment, Salesforce uses Governor Limits to make sure that shared resources are used efficiently. To avoid hitting these limits, bulkification means writing code that can handle more than one record at a time.
Here is an example of Governor Limits & Bulkification that uses DML inside a loop, which is also a common mistake.
Bad Practice (DML in Loop):
Insert happens once per record, and can hit the DML limit of 150 operations.
Good Practice (Bulkified DML):
DML operation is done once outside the loop, respecting governor limits.
2. Avoid SOQL &DML Inside Loops in Salesforce:
Putting SOQL or DML statements inside loops can quickly go over Salesforce governor limits, which are 100 SOQL queries or 150 DML statements per transaction. Instead, use collections to gather data and do DML operations outside of the loop.
Bad Practice (SOQL & DML in Loop):
- Performs SOQL and DML in every iteration.
Good Practice (Bulkified):
Uses 1 SOQL and 1 DML for all records, which is efficient and scalable.
3. Exception Handling & Logging in Salesforce:
Proper exception handling makes sure that failures happen smoothly and helps with debugging. Don’t just ignore exceptions; instead, log them with System. debug, a custom object, or Platform Events.
Bad Practice (No Exception Handling):
If the insert fails (e.g., due to validation rules), the transaction crashes without any clue why it has failed.
Good Practice (With Try-Catch and Logging):
Catches and logs the exception, allowing for smoother debugging and better fault tolerance.
Implement The Custom Error Log Object
Error handling in Apex means writing code that can deal with problems like bad data, database issues, or network failures. Instead of letting the app crash, it helps the code respond smartly.
Custom logging takes this a step further. It lets you save error details in a clear and organised way, so you can find and fix issues more easily, especially in production, where regular debug logs can be hard to access or disappear quickly.
Here’s a small Apex example that shows both error handling and custom logging:
Make sure you create a custom object called Error_Log__c with fields like:
- Class_Name__c (Text)
- Method_Name__c (Text)
- Error_Message__c (Long Text Area)
4. One Trigger Per Object in Salesforce:
Having a single trigger for every object enhances maintainability, avoids duplicating logic, and facilitates more control through a centralised handler class. Having multiple triggers on the same object causes indeterminate results.
Bad Practice (Multiple Triggers on Same Object):
Hard to control execution order and increases debugging complexity.
Good Practice (Single Trigger + Handler):
Centralised logic in one trigger and handler class — clean, modular, and easy to test.
5. Testing & Test Coverage with Assertions in Salesforce:
Writing test classes in Salesforce ensures code stability and achieves the deployment minimum 75% coverage required. Assertions (System.assert) ensure the code works as intended.
Good Practice (Test Class with Assertions):
- Covers the code and validates expected behaviour with System.assertEquals.
- Bad practice would be having no assertions and only inserting records without verifying outcomes.
6. Use Collections and Maps
Utilising collections such as Lists, Sets, and Maps enhances performance by dealing with multiple records efficiently, particularly in bulk. Maps are best for rapid lookups using IDs, eliminating frequent queries and iterations.
Best Practice:
Use Maps for efficient record lookups, especially when matching related records.
Code Example:
7. Use Descriptive Naming and Comments
Meaningful and descriptive variable, method, and class naming makes code easier to read and maintain. Including helpful comments makes others (and yourself later) comprehend the flow and intent of your code.
Best Practice:
Use meaningful variable/method names and comment complex logic.
Code Example:
8. Use Custom Settings or Custom Metadata for Configurations
Using Custom Metadata or Custom Settings for configurations in Apex is one of those habits that separates quick hacks from maintainable solutions. Hardcoding values like URLs, feature flags, record limits, or API keys directly into Apex code might seem fine at first, but it quickly becomes a problem when you need to change those values across environments or for different orgs. Every change means a code update, testing, and deployment even if you’re just switching an API endpoint.
Best Practice:
Imagine you’re integrating with an external API and the base URL can change between environments (sandbox, production).
- Create Custom Metadata Type “Integration_Config__mdt”
- Create Fields are Name (like “Production”, “Sandbox”) Base_URL__c (Text)
Code Example:
Now, instead of hardcoding the API URL, you’re pulling it from a centralised, admin-editable config.
Also Read – Apex Design Patterns in Salesforce
FAQs
Q1. Is it okay to use System.debug() extensively? Will it hit governor limits?
While System.debug() doesn’t directly consume SOQL/DML limits, excessive use can contribute to CPU governor limits and make your debug logs very large and slow to download. It’s best practice to use System.debug() is primarily for development and debugging.
Q2. I’m getting a ‘Too many SOQL queries: 101’ error. What’s the immediate fix, and what’s the underlying best practice?
The immediate fix is usually to identify the SOQL query inside a loop and move it outside the loop. The underlying best practice is avoiding SOQL queries (and DML operations) inside for loops. Instead, collect IDs into a Set or List, and then query/DML all related records in a single operation after the loop
Q3. What is bulkification in Apex?
Bulkification means writing Apex code (especially triggers and DML operations) to handle lists or collections of records at once, rather than processing them one by one. It’s critical because Salesforce has strict Governor Limits (like a maximum number of SOQL queries, DML operations, or CPU time in a single transaction). If code isn’t bulkified, it will quickly hit these limits when multiple records are processed simultaneously (e.g., mass data uploads, multiple record saves via UI).
Conclusion
It is critical to write clean, efficient, and scalable Apex code in order to achieve long-term success on the Salesforce platform. By following best practices like bulkification, staying away from SOQL/DML in loops, using secure exception handling, grouping triggers, collections, and Custom Metadata for configuration, the developer is able to construct stable applications that honour governor limits and are simpler to maintain.