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 or Trigger.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 of Name, as DeveloperName 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.