Apex is Salesforce’s robust, strongly-typed, object-oriented programming language that allows developers to execute flow and transaction control statements on the Salesforce platform. Writing efficient and maintainable Apex code is crucial for building scalable applications. In this blog, we will cover essential Apex best practices and delve into common issues with the getRecordTypeInfosByName()
method, providing solutions to overcome them.
Apex Best Practices
Following best practices in Apex development ensures code quality, performance, and maintainability. Below are some key practices to adopt:
1. Bulkify Your Code
Why: Salesforce imposes governor limits to ensure efficient resource usage. Bulkifying your code ensures it can handle multiple records simultaneously, preventing limit exceptions.
How:
- Use collections like lists, sets, and maps.
- Iterate over
Trigger.new
orTrigger.old
instead of processing single records.
Example:
public void updateAccounts(List<Account> accounts) {
for (Account acc : accounts) {
acc.Name = acc.Name.toUpperCase();
}
update accounts;
}
2. Avoid SOQL and DML Operations Inside Loops
Why: Executing queries or DML statements inside loops can quickly exceed governor limits.
How:
- Move SOQL queries outside loops.
- Collect data in collections and perform DML operations in bulk.
Example:
Bad Practice:
for (Contact con : contacts) {
Account acc = [SELECT Id FROM Account WHERE Id = :con.AccountId];
}
Good Practice:
Set<Id> accountIds = new Set<Id>();
for (Contact con : contacts) {
accountIds.add(con.AccountId);
}
Map<Id, Account> accounts = new Map<Id, Account>(
[SELECT Id FROM Account WHERE Id IN :accountIds]
);
3. Use Efficient Querying Techniques
Why: Selective queries improve performance and avoid hitting limits.
How:
- Use indexed fields in WHERE clauses.
- Retrieve only necessary fields.
- Limit the number of records returned.
4. Handle Exceptions Properly
Why: Proper exception handling ensures graceful error recovery and better user experience.
How:
- Use try-catch blocks.
- Throw custom exceptions when necessary.
- Log exceptions for debugging.
5. Write Comprehensive Test Classes
Why: Tests validate code functionality and are required for deployment.
How:
- Aim for 75% code coverage.
- Test positive, negative, and bulk scenarios.
- Use
System.assert()
methods to validate outcomes.
6. Utilize Custom Settings and Metadata
Why: Externalizing configuration data enhances flexibility and reduces code modifications.
How:
- Store configurable values in Custom Settings or Custom Metadata Types.
- Access them in Apex to control logic.
7. Follow Consistent Naming Conventions
Why: Improves code readability and maintainability.
How:
- Use meaningful names for classes, methods, and variables.
- Prefix test classes with
Test
.
8. Leverage Platform Features Before Coding
Why: Declarative tools are easier to maintain and upgrade.
How:
- Use Process Builder, Flow, or Validation Rules when possible.
- Resort to Apex for complex logic only.
9. Optimize Trigger Design
Why: Prevents conflicts and ensures efficient execution.
How:
- Use trigger frameworks or handler classes.
- Control trigger order of execution.
- Avoid recursive triggers by using static variables.
10. Enforce Security in Apex
Why: Protects data integrity and complies with security standards.
How:
- Use field-level security checks (
stripInaccessible
method). - Enforce sharing rules with
with sharing
keyword. - Avoid hardcoding user permissions.
Resolving Issues with getRecordTypeInfosByName()
The getRecordTypeInfosByName()
method retrieves record type information by name from an SObject’s describe result. Developers often use it to obtain the RecordTypeId
for record creation or updates. However, issues can arise when this method doesn’t return the expected results.
Common Issues and Solutions
1. Case Sensitivity and Exact Name Matching
Issue: The method is case-sensitive and requires the exact record type name, including spaces and special characters.
Solution:
- Verify the record type name in Salesforce Setup.
- Ensure the name in your code matches exactly.
Example:
Map<String, Schema.RecordTypeInfo> rtMap = Account.SObjectType.getDescribe().getRecordTypeInfosByName();
Schema.RecordTypeInfo rtInfo = rtMap.get('Customer Account'); // Exact name
Id recordTypeId = rtInfo.getRecordTypeId();
2. Record Type Accessibility
Issue: The running user lacks access to the record type, so it’s not included in the describe results.
Solution:
- Assign the record type to the user’s profile or permission set.
- Ensure the record type is active.
3. API Version Compatibility
Issue: Using an older API version where getRecordTypeInfosByName()
isn’t supported or behaves differently.
Solution:
- Update the Apex class to a newer API version (recommended: 50.0 or higher).
4. Namespaces in Managed Packages
Issue: In managed packages, record types might include a namespace prefix, affecting the name.
Solution:
- Include the namespace prefix when retrieving the record type.
- Use
getRecordTypeInfosByDeveloperName()
for consistency.
5. Localization and Translations
Issue: In orgs with multiple languages enabled, record type names might be translated, causing mismatches.
Solution:
- Use
DeveloperName
instead ofName
, asDeveloperName
remains consistent across translations.
Example:
Map<String, Schema.RecordTypeInfo> rtMap = Account.SObjectType.getDescribe().getRecordTypeInfosByDeveloperName();
Schema.RecordTypeInfo rtInfo = rtMap.get('Customer_Account'); // DeveloperName
Id recordTypeId = rtInfo.getRecordTypeId();
6. Incorrect SObject Type
Issue: Retrieving record types from the wrong SObject.
Solution:
- Ensure you’re calling
getDescribe()
on the correct SObject type.
Example:
// Incorrect
Map<String, Schema.RecordTypeInfo> rtMap = Contact.SObjectType.getDescribe().getRecordTypeInfosByName();
// Correct for Account
Map<String, Schema.RecordTypeInfo> rtMap = Account.SObjectType.getDescribe().getRecordTypeInfosByName();
7. Missing Record Type Assignment
Issue: The record type isn’t assigned to any profiles, making it inaccessible.
Solution:
- Assign the record type to the necessary profiles.
- Check if the record type is deactivated.
8. Use of Hardcoded Record Type Names
Issue: Hardcoding names can lead to errors if names change.
Solution:
- Store record type names or IDs in Custom Settings or Custom Metadata Types.
- Reference them dynamically in your code.
9. Sharing Settings Affecting Visibility
Issue: An Apex class declared as with sharing
may restrict access to certain record types.
Solution:
- Use
without sharing
cautiously. - Ensure sharing settings align with business requirements.
10. Platform Cache Not Updated
Issue: Changes to record types aren’t reflected immediately due to caching.
Solution:
- Clear the platform cache if applicable.
- Use fresh describe calls to retrieve updated information.
Best Practices for Accessing Record Types
Use DeveloperName
Over Name
Why:
DeveloperName
is static and not affected by translations.- It’s less prone to errors due to name changes.
Example:
public Id getRecordTypeId(String sObjectName, String developerName) {
Schema.SObjectType objType = Schema.getGlobalDescribe().get(sObjectName);
if (objType != null) {
Map<String, Schema.RecordTypeInfo> rtInfos = objType.getDescribe().getRecordTypeInfosByDeveloperName();
Schema.RecordTypeInfo rtInfo = rtInfos.get(developerName);
if (rtInfo != null) {
return rtInfo.getRecordTypeId();
}
}
// Handle record type not found
throw new ApplicationException('Record Type not found: ' + developerName);
}
Store Record Type Information Externally
– Use Custom Metadata Types to store record type developer names or IDs.
– Reference them in your code to avoid hardcoding.
Check for Nulls and Handle Exceptions
– Always check if RecordTypeInfo
is null
before accessing its properties.
– Implement proper exception handling to catch and log errors.
Conclusion
Adhering to Apex best practices enhances code quality, performance, and maintainability. When working with getRecordTypeInfosByName()
, being mindful of potential issues like case sensitivity, user permissions, and localization is essential. Utilizing DeveloperName
and externalizing configuration data are effective strategies to prevent and resolve common problems. By following these guidelines, developers can write robust Apex code that leverages the full power of the Salesforce platform.