diff --git a/docs/README.instructions.md b/docs/README.instructions.md
index 2bd07b9f..88b097b6 100644
--- a/docs/README.instructions.md
+++ b/docs/README.instructions.md
@@ -20,6 +20,7 @@ Team and project-specific instructions to enhance GitHub Copilot's behavior for
| [AI Prompt Engineering & Safety Best Practices](../instructions/ai-prompt-engineering-safety-best-practices.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fai-prompt-engineering-safety-best-practices.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fai-prompt-engineering-safety-best-practices.instructions.md) | Comprehensive best practices for AI prompt engineering, safety frameworks, bias mitigation, and responsible AI usage for Copilot and LLMs. |
| [Angular Development Instructions](../instructions/angular.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fangular.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fangular.instructions.md) | Angular-specific coding standards and best practices |
| [Ansible Conventions and Best Practices](../instructions/ansible.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fansible.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fansible.instructions.md) | Ansible conventions and best practices |
+| [Apex Development](../instructions/apex.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fapex.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fapex.instructions.md) | Guidelines and best practices for Apex development on the Salesforce Platform |
| [ASP.NET REST API Development](../instructions/aspnet-rest-apis.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Faspnet-rest-apis.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Faspnet-rest-apis.instructions.md) | Guidelines for building REST APIs with ASP.NET |
| [Astro Development Instructions](../instructions/astro.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fastro.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fastro.instructions.md) | Astro development standards and best practices for content-driven websites |
| [Azure DevOps Pipeline YAML Best Practices](../instructions/azure-devops-pipelines.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-devops-pipelines.instructions.md)
[](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fazure-devops-pipelines.instructions.md) | Best practices for Azure DevOps Pipeline YAML files |
diff --git a/instructions/apex.instructions.md b/instructions/apex.instructions.md
new file mode 100644
index 00000000..00584a10
--- /dev/null
+++ b/instructions/apex.instructions.md
@@ -0,0 +1,1294 @@
+---
+description: 'Guidelines and best practices for Apex development on the Salesforce Platform'
+applyTo: '**/*.cls, **/*.trigger'
+---
+
+# Apex Development
+
+## General Instructions
+
+- Always use the latest Apex features and best practices for the Salesforce Platform.
+- Write clear and concise comments for each class and method, explaining the business logic and any complex operations.
+- Handle edge cases and implement proper exception handling with meaningful error messages.
+- Focus on bulkification - write code that handles collections of records, not single records.
+- Be mindful of governor limits and design solutions that scale efficiently.
+- Implement proper separation of concerns using service layers, domain classes, and selector classes.
+- Document external dependencies, integration points, and their purposes in comments.
+
+## Naming Conventions
+
+- **Classes**: Use `PascalCase` for class names. Name classes descriptively to reflect their purpose.
+ - Controllers: suffix with `Controller` (e.g., `AccountController`)
+ - Trigger Handlers: suffix with `TriggerHandler` (e.g., `AccountTriggerHandler`)
+ - Service Classes: suffix with `Service` (e.g., `AccountService`)
+ - Selector Classes: suffix with `Selector` (e.g., `AccountSelector`)
+ - Test Classes: suffix with `Test` (e.g., `AccountServiceTest`)
+ - Batch Classes: suffix with `Batch` (e.g., `AccountCleanupBatch`)
+ - Queueable Classes: suffix with `Queueable` (e.g., `EmailNotificationQueueable`)
+
+- **Methods**: Use `camelCase` for method names. Use verbs to indicate actions.
+ - Good: `getActiveAccounts()`, `updateContactEmail()`, `deleteExpiredRecords()`
+ - Avoid abbreviations: `getAccs()` → `getAccounts()`
+
+- **Variables**: Use `camelCase` for variable names. Use descriptive names.
+ - Good: `accountList`, `emailAddress`, `totalAmount`
+ - Avoid single letters except for loop counters: `a` → `account`
+
+- **Constants**: Use `UPPER_SNAKE_CASE` for constants.
+ - Good: `MAX_BATCH_SIZE`, `DEFAULT_EMAIL_TEMPLATE`, `ERROR_MESSAGE_PREFIX`
+
+- **Triggers**: Name triggers as `ObjectName` + trigger event (e.g., `AccountTrigger`, `ContactTrigger`)
+
+## Best Practices
+
+### Bulkification
+
+- **Always write bulkified code** - Design all code to handle collections of records, not individual records.
+- Avoid SOQL queries and DML statements inside loops.
+- Use collections (`List<>`, `Set<>`, `Map<>`) to process multiple records efficiently.
+
+```apex
+// Good Example - Bulkified
+public static void updateAccountRating(List accounts) {
+ for (Account acc : accounts) {
+ if (acc.AnnualRevenue > 1000000) {
+ acc.Rating = 'Hot';
+ }
+ }
+ update accounts;
+}
+
+// Bad Example - Not bulkified
+public static void updateAccountRating(Account account) {
+ if (account.AnnualRevenue > 1000000) {
+ account.Rating = 'Hot';
+ update account; // DML in a method designed for single records
+ }
+}
+```
+
+### Maps for O(1) Lookup
+
+- **Use Maps for efficient lookups** - Convert lists to maps for O(1) constant-time lookups instead of O(n) list iterations.
+- Use `Map` constructor to quickly convert query results to a map.
+- Ideal for matching related records, lookups, and avoiding nested loops.
+
+```apex
+// Good Example - Using Map for O(1) lookup
+Map accountMap = new Map([
+ SELECT Id, Name, Industry FROM Account WHERE Id IN :accountIds
+]);
+
+for (Contact con : contacts) {
+ Account acc = accountMap.get(con.AccountId);
+ if (acc != null) {
+ con.Industry__c = acc.Industry;
+ }
+}
+
+// Bad Example - Nested loop with O(n²) complexity
+List accounts = [SELECT Id, Name, Industry FROM Account WHERE Id IN :accountIds];
+
+for (Contact con : contacts) {
+ for (Account acc : accounts) {
+ if (con.AccountId == acc.Id) {
+ con.Industry__c = acc.Industry;
+ break;
+ }
+ }
+}
+
+// Good Example - Map for grouping records
+Map> contactsByAccountId = new Map>();
+for (Contact con : contacts) {
+ if (!contactsByAccountId.containsKey(con.AccountId)) {
+ contactsByAccountId.put(con.AccountId, new List());
+ }
+ contactsByAccountId.get(con.AccountId).add(con);
+}
+```
+
+### Governor Limits
+
+- Be aware of Salesforce governor limits: SOQL queries (100), DML statements (150), heap size (6MB), CPU time (10s).
+- **Monitor governor limits proactively** using `System.Limits` class to check consumption before hitting limits.
+- Use efficient SOQL queries with selective filters and appropriate indexes.
+- Implement **SOQL for loops** for processing large data sets.
+- Use **Batch Apex** for operations on large data volumes (>50,000 records).
+- Leverage **Platform Cache** to reduce redundant SOQL queries.
+
+```apex
+// Good Example - SOQL for loop for large data sets
+public static void processLargeDataSet() {
+ for (List accounts : [SELECT Id, Name FROM Account]) {
+ // Process batch of 200 records
+ processAccounts(accounts);
+ }
+}
+
+// Good Example - Using WHERE clause to reduce query results
+List accounts = [SELECT Id, Name FROM Account WHERE IsActive__c = true LIMIT 200];
+```
+
+### Security and Data Access
+
+- **Always check CRUD/FLS permissions** before performing SOQL queries or DML operations.
+- Use `WITH SECURITY_ENFORCED` in SOQL queries to enforce field-level security.
+- Use `Security.stripInaccessible()` to remove fields the user cannot access.
+- Implement `WITH SHARING` keyword for classes that enforce sharing rules.
+- Use `WITHOUT SHARING` only when necessary and document the reason.
+- Use `INHERITED SHARING` for utility classes to inherit the calling context.
+
+```apex
+// Good Example - Checking CRUD and using stripInaccessible
+public with sharing class AccountService {
+ public static List getAccounts() {
+ if (!Schema.sObjectType.Account.isAccessible()) {
+ throw new SecurityException('User does not have access to Account object');
+ }
+
+ List accounts = [SELECT Id, Name, Industry FROM Account WITH SECURITY_ENFORCED];
+
+ SObjectAccessDecision decision = Security.stripInaccessible(
+ AccessType.READABLE, accounts
+ );
+
+ return decision.getRecords();
+ }
+}
+
+// Good Example - WITH SHARING for sharing rules
+public with sharing class AccountController {
+ // This class enforces record-level sharing
+}
+```
+
+### Exception Handling
+
+- Always use try-catch blocks for DML operations and callouts.
+- Create custom exception classes for specific error scenarios.
+- Log exceptions appropriately for debugging and monitoring.
+- Provide meaningful error messages to users.
+
+```apex
+// Good Example - Proper exception handling
+public class AccountService {
+ public class AccountServiceException extends Exception {}
+
+ public static void safeUpdate(List accounts) {
+ try {
+ if (!Schema.sObjectType.Account.isUpdateable()) {
+ throw new AccountServiceException('User does not have permission to update accounts');
+ }
+ update accounts;
+ } catch (DmlException e) {
+ System.debug(LoggingLevel.ERROR, 'DML Error: ' + e.getMessage());
+ throw new AccountServiceException('Failed to update accounts: ' + e.getMessage());
+ }
+ }
+}
+```
+
+### SOQL Best Practices
+
+- Use selective queries with indexed fields (`Id`, `Name`, `OwnerId`, custom indexed fields).
+- Limit query results with `LIMIT` clause when appropriate.
+- Use `LIMIT 1` when you only need one record.
+- Avoid `SELECT *` - always specify required fields.
+- Use relationship queries to minimize the number of SOQL queries.
+- Order queries by indexed fields when possible.
+- **Always use `String.escapeSingleQuotes()`** when using user input in SOQL queries to prevent SOQL injection attacks.
+- **Check query selectivity** - Aim for >10% selectivity (filters reduce results to <10% of total records).
+- Use **Query Plan** to verify query efficiency and index usage.
+- Test queries with realistic data volumes to ensure performance.
+
+```apex
+// Good Example - Selective query with indexed fields
+List accounts = [
+ SELECT Id, Name, (SELECT Id, LastName FROM Contacts)
+ FROM Account
+ WHERE OwnerId = :UserInfo.getUserId()
+ AND CreatedDate = THIS_MONTH
+ LIMIT 100
+];
+
+// Good Example - LIMIT 1 for single record
+Account account = [SELECT Id, Name FROM Account WHERE Name = 'Acme' LIMIT 1];
+
+// Good Example - escapeSingleQuotes() to prevent SOQL injection
+String searchTerm = String.escapeSingleQuotes(userInput);
+List accounts = Database.query('SELECT Id, Name FROM Account WHERE Name LIKE \'%' + searchTerm + '%\'');
+
+// Bad Example - Direct user input without escaping (SECURITY RISK)
+List accounts = Database.query('SELECT Id, Name FROM Account WHERE Name LIKE \'%' + userInput + '%\'');
+
+// Good Example - Selective query with indexed fields (high selectivity)
+List accounts = [
+ SELECT Id, Name FROM Account
+ WHERE OwnerId = :UserInfo.getUserId()
+ AND CreatedDate = TODAY
+ LIMIT 100
+];
+
+// Bad Example - Non-selective query (scans entire table)
+List accounts = [
+ SELECT Id, Name FROM Account
+ WHERE Description LIKE '%test%' // Non-indexed field
+];
+
+// Check query performance in Developer Console:
+// 1. Enable 'Use Query Plan' in Developer Console
+// 2. Run SOQL query and review 'Query Plan' tab
+// 3. Look for 'Index' usage vs 'TableScan'
+// 4. Ensure selectivity > 10% for optimal performance
+```
+
+### Trigger Best Practices
+
+- Use **one trigger per object** to maintain clarity and avoid conflicts.
+- Implement trigger logic in handler classes, not directly in triggers.
+- Use a trigger framework for consistent trigger management.
+- Leverage trigger context variables: `Trigger.new`, `Trigger.old`, `Trigger.newMap`, `Trigger.oldMap`.
+- Check trigger context: `Trigger.isBefore`, `Trigger.isAfter`, `Trigger.isInsert`, etc.
+
+```apex
+// Good Example - Trigger with handler pattern
+trigger AccountTrigger on Account (before insert, before update, after insert, after update) {
+ new AccountTriggerHandler().run();
+}
+
+// Handler Class
+public class AccountTriggerHandler extends TriggerHandler {
+ private List newAccounts;
+ private List oldAccounts;
+ private Map newAccountMap;
+ private Map oldAccountMap;
+
+ public AccountTriggerHandler() {
+ this.newAccounts = (List) Trigger.new;
+ this.oldAccounts = (List) Trigger.old;
+ this.newAccountMap = (Map) Trigger.newMap;
+ this.oldAccountMap = (Map) Trigger.oldMap;
+ }
+
+ public override void beforeInsert() {
+ AccountService.setDefaultValues(newAccounts);
+ }
+
+ public override void afterUpdate() {
+ AccountService.handleRatingChange(newAccountMap, oldAccountMap);
+ }
+}
+```
+
+### Code Quality Best Practices
+
+- **Use `isEmpty()`** - Check if collections are empty using built-in methods instead of size comparisons.
+- **Use Custom Labels** - Store user-facing text in Custom Labels for internationalization and maintainability.
+- **Use Constants** - Define constants for hardcoded values, error messages, and configuration values.
+- **Use `String.isBlank()` and `String.isNotBlank()`** - Check for null or empty strings properly.
+- **Use `String.valueOf()`** - Safely convert values to strings to avoid null pointer exceptions.
+- **Use safe navigation operator `?.`** - Access properties and methods safely without null pointer exceptions.
+- **Use null-coalescing operator `??`** - Provide default values for null expressions.
+- **Avoid using `+` for string concatenation in loops** - Use `String.join()` for better performance.
+- **Use Collection methods** - Leverage `List.clone()`, `Set.addAll()`, `Map.keySet()` for cleaner code.
+- **Use ternary operators** - For simple conditional assignments to improve readability.
+- **Use switch expressions** - Modern alternative to if-else chains for better readability and performance.
+- **Use SObject clone methods** - Properly clone SObjects when needed to avoid unintended references.
+
+```apex
+// Good Example - Switch expression (modern Apex)
+String rating = switch on account.AnnualRevenue {
+ when 0 { 'Cold'; }
+ when 1, 2, 3 { 'Warm'; }
+ when else { 'Hot'; }
+};
+
+// Good Example - Switch on SObjectType
+String objectLabel = switch on record {
+ when Account a { 'Account: ' + a.Name; }
+ when Contact c { 'Contact: ' + c.LastName; }
+ when else { 'Unknown'; }
+};
+
+// Bad Example - if-else chain
+String rating;
+if (account.AnnualRevenue == 0) {
+ rating = 'Cold';
+} else if (account.AnnualRevenue >= 1 && account.AnnualRevenue <= 3) {
+ rating = 'Warm';
+} else {
+ rating = 'Hot';
+}
+
+// Good Example - SObject clone methods
+Account original = new Account(Name = 'Acme', Industry = 'Technology');
+
+// Shallow clone with ID and relationships
+Account clone1 = original.clone(true, true);
+
+// Shallow clone without ID or relationships
+Account clone2 = original.clone(false, false);
+
+// Deep clone with all relationships
+Account clone3 = original.deepClone(true, true, true);
+
+// Good Example - isEmpty() instead of size comparison
+if (accountList.isEmpty()) {
+ System.debug('No accounts found');
+}
+
+// Bad Example - size comparison
+if (accountList.size() == 0) {
+ System.debug('No accounts found');
+}
+
+// Good Example - Custom Labels for user-facing text
+final String ERROR_MESSAGE = System.Label.Account_Update_Error;
+final String SUCCESS_MESSAGE = System.Label.Account_Update_Success;
+
+// Bad Example - Hardcoded strings
+final String ERROR_MESSAGE = 'An error occurred while updating the account';
+
+// Good Example - Constants for configuration values
+public class AccountService {
+ private static final Integer MAX_RETRY_ATTEMPTS = 3;
+ private static final String DEFAULT_INDUSTRY = 'Technology';
+ private static final String ERROR_PREFIX = 'AccountService Error: ';
+
+ public static void processAccounts() {
+ // Use constants
+ if (retryCount > MAX_RETRY_ATTEMPTS) {
+ throw new AccountServiceException(ERROR_PREFIX + 'Max retries exceeded');
+ }
+ }
+}
+
+// Good Example - isBlank() for null and empty checks
+if (String.isBlank(account.Name)) {
+ account.Name = DEFAULT_NAME;
+}
+
+// Bad Example - multiple null checks
+if (account.Name == null || account.Name == '') {
+ account.Name = DEFAULT_NAME;
+}
+
+// Good Example - String.valueOf() for safe conversion
+String accountId = String.valueOf(account.Id);
+String revenue = String.valueOf(account.AnnualRevenue);
+
+// Good Example - Safe navigation operator (?.)
+String ownerName = account?.Owner?.Name;
+Integer contactCount = account?.Contacts?.size();
+
+// Bad Example - Nested null checks
+String ownerName;
+if (account != null && account.Owner != null) {
+ ownerName = account.Owner.Name;
+}
+
+// Good Example - Null-coalescing operator (??)
+String accountName = account?.Name ?? 'Unknown Account';
+Integer revenue = account?.AnnualRevenue ?? 0;
+String industry = account?.Industry ?? DEFAULT_INDUSTRY;
+
+// Bad Example - Ternary with null check
+String accountName = account != null && account.Name != null ? account.Name : 'Unknown Account';
+
+// Good Example - Combining ?. and ??
+String email = contact?.Email ?? contact?.Account?.Owner?.Email ?? 'no-reply@example.com';
+
+// Good Example - String concatenation in loops
+List accountNames = new List();
+for (Account acc : accounts) {
+ accountNames.add(acc.Name);
+}
+String result = String.join(accountNames, ', ');
+
+// Bad Example - String concatenation in loops
+String result = '';
+for (Account acc : accounts) {
+ result += acc.Name + ', '; // Poor performance
+}
+
+// Good Example - Ternary operator
+String status = isActive ? 'Active' : 'Inactive';
+
+// Good Example - Collection methods
+List accountsCopy = accountList.clone();
+Set accountIds = new Set(accountMap.keySet());
+```
+
+### Recursion Prevention
+
+- **Use static variables** to track recursive calls and prevent infinite loops.
+- Implement a **circuit breaker** pattern to stop execution after a threshold.
+- Document recursion limits and potential risks.
+
+```apex
+// Good Example - Recursion prevention with static variable
+public class AccountTriggerHandler extends TriggerHandler {
+ private static Boolean hasRun = false;
+
+ public override void afterUpdate() {
+ if (!hasRun) {
+ hasRun = true;
+ AccountService.updateRelatedContacts(Trigger.newMap.keySet());
+ }
+ }
+}
+
+// Good Example - Circuit breaker with counter
+public class OpportunityService {
+ private static Integer recursionCount = 0;
+ private static final Integer MAX_RECURSION_DEPTH = 5;
+
+ public static void processOpportunity(Id oppId) {
+ recursionCount++;
+
+ if (recursionCount > MAX_RECURSION_DEPTH) {
+ System.debug(LoggingLevel.ERROR, 'Max recursion depth exceeded');
+ return;
+ }
+
+ try {
+ // Process opportunity logic
+ } finally {
+ recursionCount--;
+ }
+ }
+}
+```
+
+### Method Visibility and Encapsulation
+
+- **Use `private` by default** - Only expose methods that need to be public.
+- Use `protected` for methods that subclasses need to access.
+- Use `public` only for APIs that other classes need to call.
+- **Use `final` keyword** to prevent method override when appropriate.
+- Mark classes as `final` if they should not be extended.
+
+```apex
+// Good Example - Proper encapsulation
+public class AccountService {
+ // Public API
+ public static void updateAccounts(List accounts) {
+ validateAccounts(accounts);
+ performUpdate(accounts);
+ }
+
+ // Private helper - not exposed
+ private static void validateAccounts(List accounts) {
+ for (Account acc : accounts) {
+ if (String.isBlank(acc.Name)) {
+ throw new IllegalArgumentException('Account name is required');
+ }
+ }
+ }
+
+ // Private implementation - not exposed
+ private static void performUpdate(List accounts) {
+ update accounts;
+ }
+}
+
+// Good Example - Final keyword to prevent extension
+public final class UtilityHelper {
+ // Cannot be extended
+ public static String formatCurrency(Decimal amount) {
+ return '$' + amount.setScale(2);
+ }
+}
+
+// Good Example - Final method to prevent override
+public virtual class BaseService {
+ // Can be overridden
+ public virtual void process() {
+ // Implementation
+ }
+
+ // Cannot be overridden
+ public final void validateInput() {
+ // Critical validation that must not be changed
+ }
+}
+```
+
+### Design Patterns
+
+- **Service Layer Pattern**: Encapsulate business logic in service classes.
+- **Circuit Breaker Pattern**: Prevent repeated failures by stopping execution after threshold.
+- **Selector Pattern**: Create dedicated classes for SOQL queries.
+- **Domain Layer Pattern**: Implement domain classes for record-specific logic.
+- **Trigger Handler Pattern**: Use a consistent framework for trigger management.
+- **Builder Pattern**: Use for complex object construction.
+- **Strategy Pattern**: For implementing different behaviors based on conditions.
+
+```apex
+// Good Example - Service Layer Pattern
+public class AccountService {
+ public static void updateAccountRatings(Set accountIds) {
+ List accounts = AccountSelector.selectByIds(accountIds);
+
+ for (Account acc : accounts) {
+ acc.Rating = calculateRating(acc);
+ }
+
+ update accounts;
+ }
+
+ private static String calculateRating(Account acc) {
+ if (acc.AnnualRevenue > 1000000) {
+ return 'Hot';
+ } else if (acc.AnnualRevenue > 500000) {
+ return 'Warm';
+ }
+ return 'Cold';
+ }
+}
+
+// Good Example - Circuit Breaker Pattern
+public class ExternalServiceCircuitBreaker {
+ private static Integer failureCount = 0;
+ private static final Integer FAILURE_THRESHOLD = 3;
+ private static DateTime circuitOpenedTime;
+ private static final Integer RETRY_TIMEOUT_MINUTES = 5;
+
+ public static Boolean isCircuitOpen() {
+ if (circuitOpenedTime != null) {
+ // Check if retry timeout has passed
+ if (DateTime.now() > circuitOpenedTime.addMinutes(RETRY_TIMEOUT_MINUTES)) {
+ // Reset circuit
+ failureCount = 0;
+ circuitOpenedTime = null;
+ return false;
+ }
+ return true;
+ }
+ return failureCount >= FAILURE_THRESHOLD;
+ }
+
+ public static void recordFailure() {
+ failureCount++;
+ if (failureCount >= FAILURE_THRESHOLD) {
+ circuitOpenedTime = DateTime.now();
+ System.debug(LoggingLevel.ERROR, 'Circuit breaker opened due to failures');
+ }
+ }
+
+ public static void recordSuccess() {
+ failureCount = 0;
+ circuitOpenedTime = null;
+ }
+
+ public static HttpResponse makeCallout(String endpoint) {
+ if (isCircuitOpen()) {
+ throw new CircuitBreakerException('Circuit is open. Service unavailable.');
+ }
+
+ try {
+ HttpRequest req = new HttpRequest();
+ req.setEndpoint(endpoint);
+ req.setMethod('GET');
+ HttpResponse res = new Http().send(req);
+
+ if (res.getStatusCode() == 200) {
+ recordSuccess();
+ } else {
+ recordFailure();
+ }
+ return res;
+ } catch (Exception e) {
+ recordFailure();
+ throw e;
+ }
+ }
+
+ public class CircuitBreakerException extends Exception {}
+}
+
+// Good Example - Selector Pattern
+public class AccountSelector {
+ public static List selectByIds(Set accountIds) {
+ return [
+ SELECT Id, Name, AnnualRevenue, Rating
+ FROM Account
+ WHERE Id IN :accountIds
+ WITH SECURITY_ENFORCED
+ ];
+ }
+
+ public static List selectActiveAccountsWithContacts() {
+ return [
+ SELECT Id, Name, (SELECT Id, LastName FROM Contacts)
+ FROM Account
+ WHERE IsActive__c = true
+ WITH SECURITY_ENFORCED
+ ];
+ }
+}
+```
+
+### Configuration Management
+
+#### Custom Metadata Types vs Custom Settings
+
+- **Prefer Custom Metadata Types (CMT)** for configuration data that can be deployed.
+- Use **Custom Settings** for user-specific or org-specific data that varies by environment.
+- CMT is packageable, deployable, and can be used in validation rules and formulas.
+- Custom Settings support hierarchy (Org, Profile, User) but are not deployable.
+
+```apex
+// Good Example - Using Custom Metadata Type
+List configs = [
+ SELECT Endpoint__c, Timeout__c, Max_Retries__c
+ FROM API_Configuration__mdt
+ WHERE DeveloperName = 'Production_API'
+ LIMIT 1
+];
+
+if (!configs.isEmpty()) {
+ String endpoint = configs[0].Endpoint__c;
+ Integer timeout = Integer.valueOf(configs[0].Timeout__c);
+}
+
+// Good Example - Using Custom Settings (user-specific)
+User_Preferences__c prefs = User_Preferences__c.getInstance(UserInfo.getUserId());
+Boolean darkMode = prefs.Dark_Mode_Enabled__c;
+
+// Good Example - Using Custom Settings (org-level)
+Org_Settings__c orgSettings = Org_Settings__c.getOrgDefaults();
+Integer maxRecords = Integer.valueOf(orgSettings.Max_Records_Per_Query__c);
+```
+
+#### Named Credentials and HTTP Callouts
+
+- **Always use Named Credentials** for external API endpoints and authentication.
+- Avoid hardcoding URLs, tokens, or credentials in code.
+- Use `callout:NamedCredential` syntax for secure, deployable integrations.
+- **Always check HTTP status codes** and handle errors gracefully.
+- Set appropriate timeouts to prevent long-running callouts.
+- Use `Database.AllowsCallouts` interface for Queueable and Batchable classes.
+
+```apex
+// Good Example - Using Named Credentials
+public class ExternalAPIService {
+ private static final String NAMED_CREDENTIAL = 'callout:External_API';
+ private static final Integer TIMEOUT_MS = 120000; // 120 seconds
+
+ public static Map getExternalData(String recordId) {
+ HttpRequest req = new HttpRequest();
+ req.setEndpoint(NAMED_CREDENTIAL + '/api/records/' + recordId);
+ req.setMethod('GET');
+ req.setTimeout(TIMEOUT_MS);
+ req.setHeader('Content-Type', 'application/json');
+
+ try {
+ Http http = new Http();
+ HttpResponse res = http.send(req);
+
+ if (res.getStatusCode() == 200) {
+ return (Map) JSON.deserializeUntyped(res.getBody());
+ } else if (res.getStatusCode() == 404) {
+ throw new NotFoundException('Record not found: ' + recordId);
+ } else if (res.getStatusCode() >= 500) {
+ throw new ServiceUnavailableException('External service error: ' + res.getStatus());
+ } else {
+ throw new CalloutException('Unexpected response: ' + res.getStatusCode());
+ }
+ } catch (System.CalloutException e) {
+ System.debug(LoggingLevel.ERROR, 'Callout failed: ' + e.getMessage());
+ throw new ExternalAPIException('Failed to retrieve data', e);
+ }
+ }
+
+ public class ExternalAPIException extends Exception {}
+ public class NotFoundException extends Exception {}
+ public class ServiceUnavailableException extends Exception {}
+}
+
+// Good Example - POST request with JSON body
+public static String createExternalRecord(Map data) {
+ HttpRequest req = new HttpRequest();
+ req.setEndpoint(NAMED_CREDENTIAL + '/api/records');
+ req.setMethod('POST');
+ req.setTimeout(TIMEOUT_MS);
+ req.setHeader('Content-Type', 'application/json');
+ req.setBody(JSON.serialize(data));
+
+ HttpResponse res = new Http().send(req);
+
+ if (res.getStatusCode() == 201) {
+ Map result = (Map) JSON.deserializeUntyped(res.getBody());
+ return (String) result.get('id');
+ } else {
+ throw new CalloutException('Failed to create record: ' + res.getStatus());
+ }
+}
+```
+
+### Common Annotations
+
+- `@AuraEnabled` - Expose methods to Lightning Web Components and Aura Components.
+- `@AuraEnabled(cacheable=true)` - Enable client-side caching for read-only methods.
+- `@InvocableMethod` - Make methods callable from Flow and Process Builder.
+- `@InvocableVariable` - Define input/output parameters for invocable methods.
+- `@TestVisible` - Expose private members to test classes only.
+- `@SuppressWarnings('PMD.RuleName')` - Suppress specific PMD warnings.
+- `@RemoteAction` - Expose methods for Visualforce JavaScript remoting (legacy).
+- `@Future` - Execute methods asynchronously.
+- `@Future(callout=true)` - Allow HTTP callouts in future methods.
+
+```apex
+// Good Example - AuraEnabled for LWC
+public with sharing class AccountController {
+ @AuraEnabled(cacheable=true)
+ public static List getAccounts() {
+ return [SELECT Id, Name FROM Account WITH SECURITY_ENFORCED LIMIT 10];
+ }
+
+ @AuraEnabled
+ public static void updateAccount(Id accountId, String newName) {
+ Account acc = new Account(Id = accountId, Name = newName);
+ update acc;
+ }
+}
+
+// Good Example - InvocableMethod for Flow
+public class FlowActions {
+ @InvocableMethod(label='Send Email Notification' description='Sends email to account owner')
+ public static List sendNotification(List requests) {
+ List results = new List();
+
+ for (Request req : requests) {
+ Result result = new Result();
+ try {
+ // Send email logic
+ result.success = true;
+ result.message = 'Email sent successfully';
+ } catch (Exception e) {
+ result.success = false;
+ result.message = e.getMessage();
+ }
+ results.add(result);
+ }
+ return results;
+ }
+
+ public class Request {
+ @InvocableVariable(required=true label='Account ID')
+ public Id accountId;
+
+ @InvocableVariable(label='Email Template')
+ public String templateName;
+ }
+
+ public class Result {
+ @InvocableVariable
+ public Boolean success;
+
+ @InvocableVariable
+ public String message;
+ }
+}
+
+// Good Example - TestVisible for testing private methods
+public class AccountService {
+ @TestVisible
+ private static Boolean validateAccountName(String name) {
+ return String.isNotBlank(name) && name.length() > 3;
+ }
+}
+```
+
+### Asynchronous Apex
+
+- Use **@future** methods for simple asynchronous operations and callouts.
+- Use **Queueable Apex** for complex asynchronous operations that require chaining.
+- Use **Batch Apex** for processing large data volumes (>50,000 records).
+ - Use `Database.Stateful` to maintain state across batch executions (e.g., counters, aggregations).
+ - Without `Database.Stateful`, batch classes are stateless and instance variables reset between batches.
+ - Be mindful of governor limits when using stateful batches.
+- Use **Scheduled Apex** for recurring operations.
+ - Create a separate **Schedulable class** to schedule batch jobs.
+ - Never implement both `Database.Batchable` and `Schedulable` in the same class.
+- Use **Platform Events** for event-driven architecture and decoupled integrations.
+ - Publish events using `EventBus.publish()` for asynchronous, fire-and-forget communication.
+ - Subscribe to events using triggers on platform event objects.
+ - Ideal for integrations, microservices, and cross-org communication.
+- **Optimize batch size** based on processing complexity and governor limits.
+ - Default batch size is 200, but can be adjusted from 1 to 2000.
+ - Smaller batches (50-100) for complex processing or callouts.
+ - Larger batches (200) for simple DML operations.
+ - Test with realistic data volumes to find optimal size.
+
+```apex
+// Good Example - Platform Events for decoupled communication
+public class OrderEventPublisher {
+ public static void publishOrderCreated(List orders) {
+ List events = new List();
+
+ for (Order ord : orders) {
+ Order_Created__e event = new Order_Created__e(
+ Order_Id__c = ord.Id,
+ Order_Amount__c = ord.TotalAmount,
+ Customer_Id__c = ord.AccountId
+ );
+ events.add(event);
+ }
+
+ // Publish events
+ List results = EventBus.publish(events);
+
+ // Check for errors
+ for (Database.SaveResult result : results) {
+ if (!result.isSuccess()) {
+ for (Database.Error error : result.getErrors()) {
+ System.debug('Error publishing event: ' + error.getMessage());
+ }
+ }
+ }
+ }
+}
+
+// Good Example - Platform Event Trigger (Subscriber)
+trigger OrderCreatedTrigger on Order_Created__e (after insert) {
+ List tasksToCreate = new List();
+
+ for (Order_Created__e event : Trigger.new) {
+ Task t = new Task(
+ Subject = 'Follow up on order',
+ WhatId = event.Order_Id__c,
+ Priority = 'High'
+ );
+ tasksToCreate.add(t);
+ }
+
+ if (!tasksToCreate.isEmpty()) {
+ insert tasksToCreate;
+ }
+}
+
+// Good Example - Batch size optimization based on complexity
+public class ComplexProcessingBatch implements Database.Batchable, Database.AllowsCallouts {
+ public Database.QueryLocator start(Database.BatchableContext bc) {
+ return Database.getQueryLocator([
+ SELECT Id, Name FROM Account WHERE IsActive__c = true
+ ]);
+ }
+
+ public void execute(Database.BatchableContext bc, List scope) {
+ // Complex processing with callouts - use smaller batch size
+ for (Account acc : scope) {
+ // Make HTTP callout
+ HttpResponse res = ExternalAPIService.getAccountData(acc.Id);
+ // Process response
+ }
+ }
+
+ public void finish(Database.BatchableContext bc) {
+ System.debug('Batch completed');
+ }
+}
+
+// Execute with smaller batch size for callout-heavy processing
+Database.executeBatch(new ComplexProcessingBatch(), 50);
+
+// Good Example - Simple DML batch with default size
+public class SimpleDMLBatch implements Database.Batchable {
+ public Database.QueryLocator start(Database.BatchableContext bc) {
+ return Database.getQueryLocator([
+ SELECT Id, Status__c FROM Order WHERE Status__c = 'Draft'
+ ]);
+ }
+
+ public void execute(Database.BatchableContext bc, List scope) {
+ for (Order ord : scope) {
+ ord.Status__c = 'Pending';
+ }
+ update scope;
+ }
+
+ public void finish(Database.BatchableContext bc) {
+ System.debug('Batch completed');
+ }
+}
+
+// Execute with larger batch size for simple DML
+Database.executeBatch(new SimpleDMLBatch(), 200);
+
+// Good Example - Queueable Apex
+public class EmailNotificationQueueable implements Queueable, Database.AllowsCallouts {
+ private List accountIds;
+
+ public EmailNotificationQueueable(List accountIds) {
+ this.accountIds = accountIds;
+ }
+
+ public void execute(QueueableContext context) {
+ List accounts = [SELECT Id, Name, Email__c FROM Account WHERE Id IN :accountIds];
+
+ for (Account acc : accounts) {
+ sendEmail(acc);
+ }
+
+ // Chain another job if needed
+ if (hasMoreWork()) {
+ System.enqueueJob(new AnotherQueueable());
+ }
+ }
+
+ private void sendEmail(Account acc) {
+ // Email sending logic
+ }
+
+ private Boolean hasMoreWork() {
+ return false;
+ }
+}
+
+// Good Example - Stateless Batch Apex (default)
+public class AccountCleanupBatch implements Database.Batchable {
+ public Database.QueryLocator start(Database.BatchableContext bc) {
+ return Database.getQueryLocator([
+ SELECT Id, Name FROM Account WHERE LastActivityDate < LAST_N_DAYS:365
+ ]);
+ }
+
+ public void execute(Database.BatchableContext bc, List scope) {
+ delete scope;
+ }
+
+ public void finish(Database.BatchableContext bc) {
+ System.debug('Batch completed');
+ }
+}
+
+// Good Example - Stateful Batch Apex (maintains state across batches)
+public class AccountStatsBatch implements Database.Batchable, Database.Stateful {
+ private Integer recordsProcessed = 0;
+ private Integer totalRevenue = 0;
+
+ public Database.QueryLocator start(Database.BatchableContext bc) {
+ return Database.getQueryLocator([
+ SELECT Id, Name, AnnualRevenue FROM Account WHERE IsActive__c = true
+ ]);
+ }
+
+ public void execute(Database.BatchableContext bc, List scope) {
+ for (Account acc : scope) {
+ recordsProcessed++;
+ totalRevenue += (Integer) acc.AnnualRevenue;
+ }
+ }
+
+ public void finish(Database.BatchableContext bc) {
+ // State is maintained: recordsProcessed and totalRevenue retain their values
+ System.debug('Total records processed: ' + recordsProcessed);
+ System.debug('Total revenue: ' + totalRevenue);
+
+ // Send summary email or create summary record
+ }
+}
+
+// Good Example - Schedulable class to schedule a batch
+public class AccountCleanupScheduler implements Schedulable {
+ public void execute(SchedulableContext sc) {
+ // Execute the batch with batch size of 200
+ Database.executeBatch(new AccountCleanupBatch(), 200);
+ }
+}
+
+// Schedule the batch to run daily at 2 AM
+// Execute this in Anonymous Apex or in setup code:
+// String cronExp = '0 0 2 * * ?';
+// System.schedule('Daily Account Cleanup', cronExp, new AccountCleanupScheduler());
+```
+
+## Testing
+
+- **Always achieve 100% code coverage** for production code (minimum 75% required).
+- Write **meaningful tests** that verify business logic, not just code coverage.
+- Use `@TestSetup` methods to create test data shared across test methods.
+- Use `Test.startTest()` and `Test.stopTest()` to reset governor limits.
+- Test **positive scenarios**, **negative scenarios**, and **bulk scenarios** (200+ records).
+- Use `System.runAs()` to test different user contexts and permissions.
+- Mock external callouts using `Test.setMock()`.
+- Never use `@SeeAllData=true` - always create test data in tests.
+- **Use the `Assert` class methods** for assertions instead of deprecated `System.assert*()` methods.
+- Always add descriptive failure messages to assertions for clarity.
+
+```apex
+// Good Example - Comprehensive test class
+@IsTest
+private class AccountServiceTest {
+ @TestSetup
+ static void setupTestData() {
+ List accounts = new List();
+ for (Integer i = 0; i < 200; i++) {
+ accounts.add(new Account(
+ Name = 'Test Account ' + i,
+ AnnualRevenue = i * 10000
+ ));
+ }
+ insert accounts;
+ }
+
+ @IsTest
+ static void testUpdateAccountRatings_Positive() {
+ // Arrange
+ List accounts = [SELECT Id FROM Account];
+ Set accountIds = new Map(accounts).keySet();
+
+ // Act
+ Test.startTest();
+ AccountService.updateAccountRatings(accountIds);
+ Test.stopTest();
+
+ // Assert
+ List updatedAccounts = [
+ SELECT Id, Rating FROM Account WHERE AnnualRevenue > 1000000
+ ];
+ for (Account acc : updatedAccounts) {
+ Assert.areEqual('Hot', acc.Rating, 'Rating should be Hot for high revenue accounts');
+ }
+ }
+
+ @IsTest
+ static void testUpdateAccountRatings_NoAccess() {
+ // Create user with limited access
+ User testUser = createTestUser();
+
+ List accounts = [SELECT Id FROM Account LIMIT 1];
+ Set accountIds = new Map(accounts).keySet();
+
+ Test.startTest();
+ System.runAs(testUser) {
+ try {
+ AccountService.updateAccountRatings(accountIds);
+ Assert.fail('Expected SecurityException');
+ } catch (SecurityException e) {
+ Assert.isTrue(true, 'SecurityException thrown as expected');
+ }
+ }
+ Test.stopTest();
+ }
+
+ @IsTest
+ static void testBulkOperation() {
+ List accounts = [SELECT Id FROM Account];
+ Set accountIds = new Map(accounts).keySet();
+
+ Test.startTest();
+ AccountService.updateAccountRatings(accountIds);
+ Test.stopTest();
+
+ List updatedAccounts = [SELECT Id, Rating FROM Account];
+ Assert.areEqual(200, updatedAccounts.size(), 'All accounts should be processed');
+ }
+
+ private static User createTestUser() {
+ Profile p = [SELECT Id FROM Profile WHERE Name = 'Standard User' LIMIT 1];
+ return new User(
+ Alias = 'testuser',
+ Email = 'testuser@test.com',
+ EmailEncodingKey = 'UTF-8',
+ LastName = 'Testing',
+ LanguageLocaleKey = 'en_US',
+ LocaleSidKey = 'en_US',
+ ProfileId = p.Id,
+ TimeZoneSidKey = 'America/Los_Angeles',
+ UserName = 'testuser' + DateTime.now().getTime() + '@test.com'
+ );
+ }
+}
+```
+
+## Common Code Smells and Anti-Patterns
+
+- **DML/SOQL in loops** - Always bulkify your code to avoid governor limit exceptions.
+- **Hardcoded IDs** - Use custom settings, custom metadata, or dynamic queries instead.
+- **Deeply nested conditionals** - Extract logic into separate methods for clarity.
+- **Large methods** - Keep methods focused on a single responsibility (max 30-50 lines).
+- **Magic numbers** - Use named constants for clarity and maintainability.
+- **Duplicate code** - Extract common logic into reusable methods or classes.
+- **Missing null checks** - Always validate input parameters and query results.
+
+```apex
+// Bad Example - DML in loop
+for (Account acc : accounts) {
+ acc.Rating = 'Hot';
+ update acc; // AVOID: DML in loop
+}
+
+// Good Example - Bulkified DML
+for (Account acc : accounts) {
+ acc.Rating = 'Hot';
+}
+update accounts;
+
+// Bad Example - Hardcoded ID
+Account acc = [SELECT Id FROM Account WHERE Id = '001000000000001'];
+
+// Good Example - Dynamic query
+Account acc = [SELECT Id FROM Account WHERE Name = :accountName LIMIT 1];
+
+// Bad Example - Magic number
+if (accounts.size() > 200) {
+ // Process
+}
+
+// Good Example - Named constant
+private static final Integer MAX_BATCH_SIZE = 200;
+if (accounts.size() > MAX_BATCH_SIZE) {
+ // Process
+}
+```
+
+## Documentation and Comments
+
+- Use JavaDoc-style comments for classes and methods.
+- Include `@author` and `@date` tags for tracking.
+- Include `@description`, `@param`, `@return`, and `@throws` tags.
+- Include `@param`, `@return`, and `@throws` tags **only** when applicable.
+- Do not use `@return void` for methods that return nothing.
+- Document complex business logic and design decisions.
+- Keep comments up-to-date with code changes.
+
+```apex
+/**
+ * @author Your Name
+ * @date 2025-01-01
+ * @description Service class for managing Account records
+ */
+public with sharing class AccountService {
+
+ /**
+ * @author Your Name
+ * @date 2025-01-01
+ * @description Updates the rating for accounts based on annual revenue
+ * @param accountIds Set of Account IDs to update
+ * @throws AccountServiceException if user lacks update permissions
+ */
+ public static void updateAccountRatings(Set accountIds) {
+ // Implementation
+ }
+}
+```
+
+## Deployment and DevOps
+
+- Use **Salesforce CLI** for source-driven development.
+- Leverage **scratch orgs** for development and testing.
+- Implement **CI/CD pipelines** using tools like Salesforce CLI, GitHub Actions, or Jenkins.
+- Use **unlocked packages** for modular deployments.
+- Run **Apex tests** as part of deployment validation.
+- Use **Salesforce Code Analyzer** to scan code for quality and security issues.
+
+```bash
+# Salesforce CLI commands (sf)
+sf project deploy start # Deploy source to org
+sf project deploy start --dry-run # Validate deployment without deploying
+sf apex run test --test-level RunLocalTests # Run local Apex tests
+sf apex get test --test-run-id # Get test results
+sf project retrieve start # Retrieve source from org
+
+# Salesforce Code Analyzer commands
+sf code-analyzer rules # List all available rules
+sf code-analyzer rules --rule-selector eslint:Recommended # List recommended ESLint rules
+sf code-analyzer rules --workspace ./force-app # List rules for specific workspace
+sf code-analyzer run # Run analysis with recommended rules
+sf code-analyzer run --rule-selector pmd:Recommended # Run PMD recommended rules
+sf code-analyzer run --rule-selector "Security" # Run rules with Security tag
+sf code-analyzer run --workspace ./force-app --target "**/*.cls" # Analyze Apex classes
+sf code-analyzer run --severity-threshold 3 # Run analysis with severity threshold
+sf code-analyzer run --output-file results.html # Output results to HTML file
+sf code-analyzer run --output-file results.csv # Output results to CSV file
+sf code-analyzer run --view detail # Show detailed violation information
+```
+
+## Performance Optimization
+
+- Use **selective SOQL queries** with indexed fields.
+- Implement **lazy loading** for expensive operations.
+- Use **asynchronous processing** for long-running operations.
+- Monitor with **Debug Logs** and **Event Monitoring**.
+- Use **ApexGuru** and **Scale Center** for performance insights.
+
+### Platform Cache
+
+- Use **Platform Cache** to store frequently accessed data and reduce SOQL queries.
+- `Cache.OrgPartition` - Shared across all users and sessions in the org.
+- `Cache.SessionPartition` - Specific to a user's session.
+- Implement proper cache invalidation strategies.
+- Handle cache misses gracefully with fallback to database queries.
+
+```apex
+// Good Example - Using Org Cache
+public class AccountCacheService {
+ private static final String CACHE_PARTITION = 'local.AccountCache';
+ private static final Integer TTL_SECONDS = 3600; // 1 hour
+
+ public static Account getAccount(Id accountId) {
+ Cache.OrgPartition orgPart = Cache.Org.getPartition(CACHE_PARTITION);
+ String cacheKey = 'Account_' + accountId;
+
+ // Try to get from cache
+ Account acc = (Account) orgPart.get(cacheKey);
+
+ if (acc == null) {
+ // Cache miss - query database
+ acc = [
+ SELECT Id, Name, Industry, AnnualRevenue
+ FROM Account
+ WHERE Id = :accountId
+ LIMIT 1
+ ];
+
+ // Store in cache with TTL
+ orgPart.put(cacheKey, acc, TTL_SECONDS);
+ }
+
+ return acc;
+ }
+
+ public static void invalidateCache(Id accountId) {
+ Cache.OrgPartition orgPart = Cache.Org.getPartition(CACHE_PARTITION);
+ String cacheKey = 'Account_' + accountId;
+ orgPart.remove(cacheKey);
+ }
+}
+
+// Good Example - Using Session Cache
+public class UserPreferenceCache {
+ private static final String CACHE_PARTITION = 'local.UserPrefs';
+
+ public static Map getUserPreferences() {
+ Cache.SessionPartition sessionPart = Cache.Session.getPartition(CACHE_PARTITION);
+ String cacheKey = 'UserPrefs_' + UserInfo.getUserId();
+
+ Map prefs = (Map) sessionPart.get(cacheKey);
+
+ if (prefs == null) {
+ // Load preferences from database or custom settings
+ prefs = new Map{
+ 'theme' => 'dark',
+ 'language' => 'en_US'
+ };
+ sessionPart.put(cacheKey, prefs);
+ }
+
+ return prefs;
+ }
+}
+```
+
+## Build and Verification
+
+- After adding or modifying code, verify the project continues to build successfully.
+- Run all relevant Apex test classes to ensure no regressions.
+- Use Salesforce CLI: `sf apex run test --test-level RunLocalTests`
+- Ensure code coverage meets the minimum 75% requirement (aim for 100%).
+- Use Salesforce Code Analyzer to check for code quality issues: `sf code-analyzer run --severity-threshold 2`
+- Review violations and address them before deployment.