Null Handling and Safe Navigation

Safe Navigation Operator (?.)

The safe navigation operator (?.) returns null if the left operand is null, otherwise it evaluates the right operand. This prevents NullPointerException.

Usage Guidelines:

  • Use safe navigation for chaining: When accessing properties or methods on potentially null objects
  • Use explicit null checks for validation: When you need to validate input or handle null as an error condition

Examples:

// Good: Safe navigation for chaining
String objectName = record?.getSObjectType()?.getDescribe()?.getName();
List<SObject> results = queryResult?.getRecords();

// Good: Explicit check for validation
if (recordId == null) {
    throw new ACME_ValidationException('Record ID is required');
}

// Good: Context-dependent usage
if (String.isNotBlank(querySort?.field)) {
    // Process field
}

Null Coalescing Patterns

Use the null-coalescing operator (??) as the default fallback pattern for nullable values. Use ternary expressions when you need more complex branching logic.

Example:

// Preferred: Null-coalescing operator
String status = account.Status ?? 'Active';
Integer count = recordCount ?? 0;

// Use ternary when conditions are more complex than null fallback
String displayStatus = account.Status != null && account.Status != '' ? account.Status : 'Active';

Collection Initialization

Always initialize empty collections in constructors to avoid null pointer exceptions.

Example:

public class ACME_Response {
    public List<SObject> results { get; set; }
    public List<String> messages { get; set; }

    public ACME_Response() {
        results = new List<SObject>();
        messages = new List<String>();
    }
}

Error Handling

Exception Naming Conventions

  • Use PascalCase with "Exception" suffix
  • Be descriptive about the exception type
  • Examples: ValidationException, SecurityException, DataAccessException

Custom Exception Classes

Use custom exceptions for domain-specific failures where callers need a clear, actionable error type. Standard exceptions are acceptable for platform/system failures.

Current patterns in the codebase include:

  • TypeException - For type validation errors
  • FieldException - For field-related errors
  • PermissionException - For permission-related errors
  • ValidationException - For general validation errors

Example:

public class ACME_ValidationException extends Exception { }

public static void validateRecord(Account acc) {
    if (acc.Name == null) {
        throw new ACME_ValidationException('Account name is required');
    }
}

SOQL Result Handling

Always check isEmpty() before accessing query results to prevent index out of bounds exceptions.

Example:

List<Account> accounts = [SELECT Id, Name FROM Account LIMIT 10];

if (!accounts.isEmpty()) {
    Account firstAccount = accounts[0];
    // Process account
} else {
    // Handle empty result
}

Try-Catch Patterns

  • Always handle exceptions appropriately
  • Never use empty catch blocks (violates Code Analyzer rules)
  • Log errors appropriately (see Logging and Debugging)
  • Return appropriate error responses for frontend methods

Example:

public static ACME_Response processRecord(Id recordId) {
    ACME_Response response = new ACME_Response();
    try {
        Account acc = [SELECT Id, Name FROM Account WHERE Id = :recordId];
        // Process account
        response.results.add(acc);
    } catch (QueryException e) {
        System.debug('Error querying record: ' + e.getMessage());
        response.success = false;
        response.messages.add('Record not found');
    } catch (Exception e) {
        System.debug('Unexpected error: ' + e.getMessage());
        response.success = false;
        response.messages.add('An error occurred processing the record');
    }
    return response;
}

Error Message Best Practices

  • Use clear, user-friendly error messages
  • Include context when helpful (field name, record ID, etc.)
  • Avoid exposing sensitive information
  • Log detailed error information for debugging

Example:

throw new ACME_ValidationException('Account name is required');
throw new ACME_SecurityException('User does not have permission to access this record');

Change History

Version 1.0 - 2026-03-26

  • Added initial version history tracking for this document.

Last Updated: 2026-03-26 Version: 1.0