Apex Programming
Apex Basics
// Class definition
public class MyApexClass {
// Class variables
private String name;
public Integer count { get; set; }
// Constructor
public MyApexClass() {
name = 'Default';
count = 0;
}
// Method with parameters
public String greet(String personName) {
return 'Hello, ' + personName + '!';
}
// Static method
public static Integer incrementCount(Integer current) {
return current + 1;
}
}
// Interface definition
public interface MyInterface {
void doSomething();
}
// Exception class
public class MyException extends Exception {
// Custom exception properties/methods
}
public class MyApexClass {
// Class variables
private String name;
public Integer count { get; set; }
// Constructor
public MyApexClass() {
name = 'Default';
count = 0;
}
// Method with parameters
public String greet(String personName) {
return 'Hello, ' + personName + '!';
}
// Static method
public static Integer incrementCount(Integer current) {
return current + 1;
}
}
// Interface definition
public interface MyInterface {
void doSomething();
}
// Exception class
public class MyException extends Exception {
// Custom exception properties/methods
}
DML Operations
// Create records
Account acc = new Account(Name = 'Test Account');
insert acc;
// Update records
acc.Name = 'Updated Account';
update acc;
// Upsert (insert or update)
upsert acc;
// Delete records
delete acc;
// Undelete records
undelete acc;
// Bulk DML operations
List<Account> accounts = new List<Account>();
for (Integer i = 0; i < 10; i++) {
accounts.add(new Account(Name = 'Account ' + i));
}
insert accounts;
// Database methods with options
Database.SaveResult sr = Database.insert(acc, false);
List<Database.SaveResult> results = Database.insert(accounts, false);
// Check DML results
for (Database.SaveResult result : results) {
if (result.isSuccess()) {
System.debug('Successfully created account: ' + result.getId());
} else {
for (Database.Error err : result.getErrors()) {
System.debug('Error: ' + err.getMessage());
}
}
}
Account acc = new Account(Name = 'Test Account');
insert acc;
// Update records
acc.Name = 'Updated Account';
update acc;
// Upsert (insert or update)
upsert acc;
// Delete records
delete acc;
// Undelete records
undelete acc;
// Bulk DML operations
List<Account> accounts = new List<Account>();
for (Integer i = 0; i < 10; i++) {
accounts.add(new Account(Name = 'Account ' + i));
}
insert accounts;
// Database methods with options
Database.SaveResult sr = Database.insert(acc, false);
List<Database.SaveResult> results = Database.insert(accounts, false);
// Check DML results
for (Database.SaveResult result : results) {
if (result.isSuccess()) {
System.debug('Successfully created account: ' + result.getId());
} else {
for (Database.Error err : result.getErrors()) {
System.debug('Error: ' + err.getMessage());
}
}
}
SOQL & SOSL
SOQL Queries
// Basic SOQL query
List<Account> accounts = [SELECT Id, Name FROM Account];
// Query with WHERE clause
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Industry = 'Technology'];
// Query with relationship fields
List<Contact> contacts = [SELECT Id, Name, Account.Name FROM Contact];
// Query with ORDER BY and LIMIT
List<Account> accounts = [SELECT Id, Name FROM Account ORDER BY Name DESC LIMIT 10];
// Aggregate queries
AggregateResult[] groupedResults = [SELECT COUNT(Id) total, Industry FROM Account GROUP BY Industry];
for (AggregateResult ar : groupedResults) {
System.debug('Industry: ' + ar.get('Industry'));
System.debug('Count: ' + ar.get('total'));
}
// Dynamic SOQL
String accountName = 'Test%';
String query = 'SELECT Id, Name FROM Account WHERE Name LIKE :accountName';
List<Account> accounts = Database.query(query);
// SOQL For Loop (handles large result sets)
for (Account acc : [SELECT Id, Name FROM Account]) {
System.debug('Account Name: ' + acc.Name);
}
// Query with bind variables
String industry = 'Technology';
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Industry = :industry];
List<Account> accounts = [SELECT Id, Name FROM Account];
// Query with WHERE clause
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Industry = 'Technology'];
// Query with relationship fields
List<Contact> contacts = [SELECT Id, Name, Account.Name FROM Contact];
// Query with ORDER BY and LIMIT
List<Account> accounts = [SELECT Id, Name FROM Account ORDER BY Name DESC LIMIT 10];
// Aggregate queries
AggregateResult[] groupedResults = [SELECT COUNT(Id) total, Industry FROM Account GROUP BY Industry];
for (AggregateResult ar : groupedResults) {
System.debug('Industry: ' + ar.get('Industry'));
System.debug('Count: ' + ar.get('total'));
}
// Dynamic SOQL
String accountName = 'Test%';
String query = 'SELECT Id, Name FROM Account WHERE Name LIKE :accountName';
List<Account> accounts = Database.query(query);
// SOQL For Loop (handles large result sets)
for (Account acc : [SELECT Id, Name FROM Account]) {
System.debug('Account Name: ' + acc.Name);
}
// Query with bind variables
String industry = 'Technology';
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Industry = :industry];
SOSL Queries
// Basic SOSL query
List<List<SObject>> searchList = [FIND 'Test' IN ALL FIELDS RETURNING Account(Id, Name), Contact];
// Extract results from SOSL query
List<Account> accounts = (List<Account>)searchList[0];
List<Contact> contacts = (List<Contact>)searchList[1];
// SOSL with WHERE clause
List<List<SObject>> searchList = [FIND 'Test' IN ALL FIELDS RETURNING Account(Id, Name WHERE Industry = 'Technology')];
// SOSL with multiple search groups
List<List<SObject>> searchList = [FIND 'Test OR Demo' IN ALL FIELDS RETURNING Account, Contact];
// Dynamic SOSL
String searchQuery = 'FIND \'Test*\' IN ALL FIELDS RETURNING Account (Id, Name), Contact';
List<List<SObject>> searchList = Search.query(searchQuery);
// SOSL with LIMIT and ORDER BY
List<List<SObject>> searchList = [FIND 'Test' IN ALL FIELDS RETURNING Account(Id, Name ORDER BY Name LIMIT 10)];
// SOSL with different search locations
List<List<SObject>> searchList = [FIND 'Test' IN NAME FIELDS RETURNING Account];
// SOSL with email fields search
List<List<SObject>> searchList = [FIND 'example.com' IN EMAIL FIELDS RETURNING Contact(Id, Name, Email)];
List<List<SObject>> searchList = [FIND 'Test' IN ALL FIELDS RETURNING Account(Id, Name), Contact];
// Extract results from SOSL query
List<Account> accounts = (List<Account>)searchList[0];
List<Contact> contacts = (List<Contact>)searchList[1];
// SOSL with WHERE clause
List<List<SObject>> searchList = [FIND 'Test' IN ALL FIELDS RETURNING Account(Id, Name WHERE Industry = 'Technology')];
// SOSL with multiple search groups
List<List<SObject>> searchList = [FIND 'Test OR Demo' IN ALL FIELDS RETURNING Account, Contact];
// Dynamic SOSL
String searchQuery = 'FIND \'Test*\' IN ALL FIELDS RETURNING Account (Id, Name), Contact';
List<List<SObject>> searchList = Search.query(searchQuery);
// SOSL with LIMIT and ORDER BY
List<List<SObject>> searchList = [FIND 'Test' IN ALL FIELDS RETURNING Account(Id, Name ORDER BY Name LIMIT 10)];
// SOSL with different search locations
List<List<SObject>> searchList = [FIND 'Test' IN NAME FIELDS RETURNING Account];
// SOSL with email fields search
List<List<SObject>> searchList = [FIND 'example.com' IN EMAIL FIELDS RETURNING Contact(Id, Name, Email)];
Triggers & Batch Apex
Apex Triggers
// Basic trigger structure
trigger AccountTrigger on Account (before insert, before update, after insert, after update) {
// Trigger context variables
if (Trigger.isBefore) {
if (Trigger.isInsert) {
// Before insert logic
for (Account acc : Trigger.new) {
// Set default values
acc.Description = 'Created by trigger';
}
}
else if (Trigger.isUpdate) {
// Before update logic
for (Account acc : Trigger.new) {
Account oldAcc = Trigger.oldMap.get(acc.Id);
if (acc.Name != oldAcc.Name) {
acc.Description = 'Name changed from ' + oldAcc.Name + ' to ' + acc.Name;
}
}
}
}
else if (Trigger.isAfter) {
if (Trigger.isInsert) {
// After insert logic
List<Task> tasks = new List<Task>();
for (Account acc : Trigger.tasks.add(new Task(
Subject = 'Follow up on new account',
WhatId = acc.Id,
Status = 'Not Started'
));
}
insert tasks;
}
}
}
// Trigger handler pattern (recommended)
trigger AccountTrigger on Account (before insert, before update) {
AccountTriggerHandler.handleBeforeInsertUpdate(Trigger.new, Trigger.oldMap);
}
trigger AccountTrigger on Account (before insert, before update, after insert, after update) {
// Trigger context variables
if (Trigger.isBefore) {
if (Trigger.isInsert) {
// Before insert logic
for (Account acc : Trigger.new) {
// Set default values
acc.Description = 'Created by trigger';
}
}
else if (Trigger.isUpdate) {
// Before update logic
for (Account acc : Trigger.new) {
Account oldAcc = Trigger.oldMap.get(acc.Id);
if (acc.Name != oldAcc.Name) {
acc.Description = 'Name changed from ' + oldAcc.Name + ' to ' + acc.Name;
}
}
}
}
else if (Trigger.isAfter) {
if (Trigger.isInsert) {
// After insert logic
List<Task> tasks = new List<Task>();
for (Account acc : Trigger.tasks.add(new Task(
Subject = 'Follow up on new account',
WhatId = acc.Id,
Status = 'Not Started'
));
}
insert tasks;
}
}
}
// Trigger handler pattern (recommended)
trigger AccountTrigger on Account (before insert, before update) {
AccountTriggerHandler.handleBeforeInsertUpdate(Trigger.new, Trigger.oldMap);
}
Batch Apex
// Batch Apex class structure
global class AccountUpdateBatch implements Database.Batchable<SObject> {
// Start method
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator(
'SELECT Id, Name, Description FROM Account WHERE CreatedDate = TODAY'
);
}
// Execute method
global void execute(Database.BatchableContext bc, List<Account> scope) {
List<Account> accountsToUpdate = new List<Account>();
for (Account acc : scope) {
acc.Description = 'Updated by batch job on ' + System.today();
accountsToUpdate.add(acc);
}
update accountsToUpdate;
}
// Finish method
global void finish(Database.BatchableContext bc) {
// Execute any post-processing operations
AsyncApexJob job = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email FROM AsyncApexJob WHERE Id = :bc.getJobId()];
System.debug('Batch job completed with status: ' + job.Status);
}
}
// Executing a batch job
AccountUpdateBatch batchJob = new AccountUpdateBatch();
Database.executeBatch(batchJob, 100); // 100 records per batch
// Schedule a batch job
String cronExpr = '0 0 2 * * ?'; // Run at 2 AM every day
System.schedule('Daily Account Update', cronExpr, new AccountUpdateBatch());
// Batchable with state
global class StatefulBatch implements Database.Batchable<SObject>, Database.Stateful {
global Integer recordsProcessed = 0;
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator('SELECT Id FROM Account');
}
global void execute(Database.BatchableContext bc, List<Account> scope) {
recordsProcessed += scope.size();
}
global void finish(Database.BatchableContext bc) {
System.debug('Total records processed: ' + recordsProcessed);
}
}
global class AccountUpdateBatch implements Database.Batchable<SObject> {
// Start method
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator(
'SELECT Id, Name, Description FROM Account WHERE CreatedDate = TODAY'
);
}
// Execute method
global void execute(Database.BatchableContext bc, List<Account> scope) {
List<Account> accountsToUpdate = new List<Account>();
for (Account acc : scope) {
acc.Description = 'Updated by batch job on ' + System.today();
accountsToUpdate.add(acc);
}
update accountsToUpdate;
}
// Finish method
global void finish(Database.BatchableContext bc) {
// Execute any post-processing operations
AsyncApexJob job = [SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email FROM AsyncApexJob WHERE Id = :bc.getJobId()];
System.debug('Batch job completed with status: ' + job.Status);
}
}
// Executing a batch job
AccountUpdateBatch batchJob = new AccountUpdateBatch();
Database.executeBatch(batchJob, 100); // 100 records per batch
// Schedule a batch job
String cronExpr = '0 0 2 * * ?'; // Run at 2 AM every day
System.schedule('Daily Account Update', cronExpr, new AccountUpdateBatch());
// Batchable with state
global class StatefulBatch implements Database.Batchable<SObject>, Database.Stateful {
global Integer recordsProcessed = 0;
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator('SELECT Id FROM Account');
}
global void execute(Database.BatchableContext bc, List<Account> scope) {
recordsProcessed += scope.size();
}
global void finish(Database.BatchableContext bc) {
System.debug('Total records processed: ' + recordsProcessed);
}
}
Lightning Web Components
LWC Basics
// JavaScript controller (myComponent.js)
import { LightningElement, track, wire } from 'lwc';
import getContacts from '@salesforce/apex/ContactController.getContacts';
export default class MyComponent extends LightningElement {
@track contacts = [];
@track error;
searchKey = '';
// Wire method to get data from Apex
@wire(getContacts, { searchKey: '$searchKey' })
wiredContacts({ error, data }) {
if (data) {
this.contacts = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.contacts = [];
}
}
// Handle user input
handleSearchChange(event) {
this.searchKey = event.target.value;
}
// Method to create a new contact
createContact() {
this.template.querySelector('c-contact-form').show();
}
}
// Apex controller (ContactController.cls)
public with sharing class ContactController {
@AuraEnabled(cacheable=true)
public static List<Contact> getContacts(String searchKey) {
String query = 'SELECT Id, Name, Email, Phone FROM Contact';
if (searchKey != null && searchKey != '') {
query += ' WHERE Name LIKE \'%' + String.escapeSingleQuotes(searchKey) + '%\'';
}
return Database.query(query);
}
}
import { LightningElement, track, wire } from 'lwc';
import getContacts from '@salesforce/apex/ContactController.getContacts';
export default class MyComponent extends LightningElement {
@track contacts = [];
@track error;
searchKey = '';
// Wire method to get data from Apex
@wire(getContacts, { searchKey: '$searchKey' })
wiredContacts({ error, data }) {
if (data) {
this.contacts = data;
this.error = undefined;
} else if (error) {
this.error = error;
this.contacts = [];
}
}
// Handle user input
handleSearchChange(event) {
this.searchKey = event.target.value;
}
// Method to create a new contact
createContact() {
this.template.querySelector('c-contact-form').show();
}
}
// Apex controller (ContactController.cls)
public with sharing class ContactController {
@AuraEnabled(cacheable=true)
public static List<Contact> getContacts(String searchKey) {
String query = 'SELECT Id, Name, Email, Phone FROM Contact';
if (searchKey != null && searchKey != '') {
query += ' WHERE Name LIKE \'%' + String.escapeSingleQuotes(searchKey) + '%\'';
}
return Database.query(query);
}
}
LWC HTML Template
<template>
<div class="slds-m-around_medium">
<h1>Contact Manager</h1>
<!-- Search input -->
<div class="slds-grid slds-gutters">
<div class="slds-col">
<lightning-input
type="search"
value="{searchKey}"
onchange="{handleSearchChange}"
label="Search"
placeholder="Search contacts...">
</lightning-input>
</div>
<div class="slds-col slds-size_1-of-4">
<lightning-button
label="New Contact"
variant="brand"
onclick="{createContact}">
</lightning-button>
</div>
</div>
<!-- Display error if any -->
<template if:true="{error}">
<div class="slds-notify slds-notify_alert slds-theme_error">
<span class="slds-assistive-text">Error</span>
<span class="slds-icon_container slds-icon-utility-error">
<lightning-icon icon-name="utility:error"></lightning-icon>
</span>
<h2>{error.body.message}</h2>
</div>
</template>
<!-- Contacts list -->
<template if:true="{contacts}">
<div class="slds-card">
<ul class="slds-list slds-list_vertical">
<template for:each="{contacts}" for:item="contact">
<li key="{contact.Id}" class="slds-list__item">
<div class="slds-grid slds-wrap">
<div class="slds-col slds-size_1-of-2">
<b>{contact.Name}</b>
</div>
<div class="slds-col slds-size_1-of-2">
<a href="tel:{contact.Phone}">{contact.Phone}</a>
</div>
</div>
</li>
</template>
</ul>
</div>
</template>
</div>
<!-- Contact form component -->
<c-contact-form></c-contact-form>
</template>
Integration & Deployment
REST API Integration
// Apex REST Web Service
@RestResource(urlMapping='/Account/*')
global with sharing class AccountManager {
// HTTP GET method
@HttpGet
global static Account getAccount() {
RestRequest request = RestContext.request;
String accountId = request.requestURI.substring(request.requestURI.lastIndexOf('/')+1);
Account result = [SELECT Id, Phone, Website FROM Account WHERE Id = :accountId];
return result;
}
// HTTP POST method
@HttpPost
global static String createAccount(String name, String phone, String website) {
Account acc = new Account();
acc.Name = name;
acc.Phone = phone;
acc.Website = website;
insert acc;
return acc.Id;
}
// HTTP PATCH method
@HttpPatch
global static String updateAccount() {
RestRequest request = RestContext.request;
String accountId = request.requestURI.substring(request.requestURI.lastIndexOf('/')+1);
Account acc = [SELECT Id FROM Account WHERE Id = :accountId];
Map<String, Object> params = (Map<String, Object>)JSON.deserializeUntyped(request.requestbody.toString());
for (String fieldName : params.keySet()) {
acc.put(fieldName, params.get(fieldName));
}
update acc;
return 'Account updated successfully';
}
}
// Callout to external REST API
public class HttpCalloutService {
public static String makeGetCallout(String endpoint) {
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint(endpoint);
request.setMethod('GET');
HttpResponse response = http.send(request);
if (response.getStatusCode() == 200) {
return response.getBody();
} else {
throw new CalloutException('HTTP callout failed with status: ' + getStatusCode());
}
}
}
@RestResource(urlMapping='/Account/*')
global with sharing class AccountManager {
// HTTP GET method
@HttpGet
global static Account getAccount() {
RestRequest request = RestContext.request;
String accountId = request.requestURI.substring(request.requestURI.lastIndexOf('/')+1);
Account result = [SELECT Id, Phone, Website FROM Account WHERE Id = :accountId];
return result;
}
// HTTP POST method
@HttpPost
global static String createAccount(String name, String phone, String website) {
Account acc = new Account();
acc.Name = name;
acc.Phone = phone;
acc.Website = website;
insert acc;
return acc.Id;
}
// HTTP PATCH method
@HttpPatch
global static String updateAccount() {
RestRequest request = RestContext.request;
String accountId = request.requestURI.substring(request.requestURI.lastIndexOf('/')+1);
Account acc = [SELECT Id FROM Account WHERE Id = :accountId];
Map<String, Object> params = (Map<String, Object>)JSON.deserializeUntyped(request.requestbody.toString());
for (String fieldName : params.keySet()) {
acc.put(fieldName, params.get(fieldName));
}
update acc;
return 'Account updated successfully';
}
}
// Callout to external REST API
public class HttpCalloutService {
public static String makeGetCallout(String endpoint) {
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint(endpoint);
request.setMethod('GET');
HttpResponse response = http.send(request);
if (response.getStatusCode() == 200) {
return response.getBody();
} else {
throw new CalloutException('HTTP callout failed with status: ' + getStatusCode());
}
}
}
Deployment & Tools
# Salesforce CLI commands
# Login to org
sfdx force:auth:web:login -a "MyOrg"
# Create a scratch org
sfdx force:org:create -f config/project-scratch-def.json -a "MyScratchOrg" -s
# Push source to org
sfdx force:source:push -u "MyScratchOrg"
# Pull changes from org
sfdx force:source:pull -u "MyScratchOrg"
# Run Apex tests
sfdx force:apex:test:run -u "MyScratchOrg" -r "human"
# Open org
sfdx force:org:open -u "MyScratchOrg"
# Retrieve metadata
sfdx force:source:retrieve -m "ApexClass,CustomObject"
# Deploy metadata
sfdx force:source:deploy -m "ApexClass:MyClass,CustomObject:Account"
# Create a new Apex class
sfdx force:apex:class:create -n "MyNewClass" -d force-app/main/default/classes
# Create a new LWC
sfdx force:lightning:component:create -n "myComponent" -d force-app/main/default/lwc
# Generate password for user
sfdx force:user:password:generate -u "MyScratchOrg"
# Display org info
sfdx force:org:display -u "MyScratchOrg"
# List all orgs
sfdx force:org:list
# Delete a scratch org
sfdx force:org:delete -u "MyScratchOrg"
# Package version commands
sfdx force:package:version:create -p "MyPackage" -d force-app -w 10
sfdx force:package:version:list
sfdx force:package:version:promote -p "04t..."
# Login to org
sfdx force:auth:web:login -a "MyOrg"
# Create a scratch org
sfdx force:org:create -f config/project-scratch-def.json -a "MyScratchOrg" -s
# Push source to org
sfdx force:source:push -u "MyScratchOrg"
# Pull changes from org
sfdx force:source:pull -u "MyScratchOrg"
# Run Apex tests
sfdx force:apex:test:run -u "MyScratchOrg" -r "human"
# Open org
sfdx force:org:open -u "MyScratchOrg"
# Retrieve metadata
sfdx force:source:retrieve -m "ApexClass,CustomObject"
# Deploy metadata
sfdx force:source:deploy -m "ApexClass:MyClass,CustomObject:Account"
# Create a new Apex class
sfdx force:apex:class:create -n "MyNewClass" -d force-app/main/default/classes
# Create a new LWC
sfdx force:lightning:component:create -n "myComponent" -d force-app/main/default/lwc
# Generate password for user
sfdx force:user:password:generate -u "MyScratchOrg"
# Display org info
sfdx force:org:display -u "MyScratchOrg"
# List all orgs
sfdx force:org:list
# Delete a scratch org
sfdx force:org:delete -u "MyScratchOrg"
# Package version commands
sfdx force:package:version:create -p "MyPackage" -d force-app -w 10
sfdx force:package:version:list
sfdx force:package:version:promote -p "04t..."
Salesforce Resources
Useful Links
- Developer Documentation: Salesforce Developer Docs
- Trailhead: Salesforce Trailhead
- Developer Console: In-app tool for debugging and development
- Developer Forums: Salesforce Developer Community
- GitHub Repositories: Salesforce Official GitHub
- Salesforce CLI: Command Line Interface
- Developer Blog: Salesforce Developer Blog
- Podcasts: Salesforce Developers, Good Day Sir! Podcast
Best Practices
// Bulkify your code - handle multiple records
// Bad practice - SOQL inside loop
for (Account acc : accounts) {
List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id]; // Avoid!
}
// Good practice - bulk SOQL
Set<Id> accountIds = new Set<Id>();
for (Account acc : accounts) {
accountIds.add(acc.Map<Id, List<Contact>> accountContacts = new Map<Id, List<Contact>>();
for (con : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]) {
if (!accountContacts.containsKey(con.AccountId)) {
accountContacts.put(con.AccountId, new List<Contact>());
}
accountContacts.get(con.AccountId).add(con);
}
// Avoid hardcoding IDs
// Bad practice
Account acc = [SELECT Id FROM Account WHERE Id = '001...']; // Avoid!
// Good practice - use custom settings or custom metadata
String accountId = AppConfig__c.getInstance().DefaultAccountId__c;
Account acc = [SELECT Id FROM极 Account WHERE Id = :accountId];
// Use limits methods to avoid governor limits
Integer soqlQueries = Limits.getQueries();
Integer soqlLimit = Limits.getLimitQueries();
System.debug('SOQL queries used: ' + soqlQueries + ' of ' + soqlLimit);
// Use @future appropriately for async operations
@future
public static void processRecordsAsync(Set<Id> recordIds) {
// Async processing logic
}
// Use exception handling
try {
// Code that might fail
insert records;
} catch (DmlException e) {
System.debug('DML Exception: ' + e.getMessage());
// Handle exception appropriately
}
// Bad practice - SOQL inside loop
for (Account acc : accounts) {
List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id]; // Avoid!
}
// Good practice - bulk SOQL
Set<Id> accountIds = new Set<Id>();
for (Account acc : accounts) {
accountIds.add(acc.Map<Id, List<Contact>> accountContacts = new Map<Id, List<Contact>>();
for (con : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]) {
if (!accountContacts.containsKey(con.AccountId)) {
accountContacts.put(con.AccountId, new List<Contact>());
}
accountContacts.get(con.AccountId).add(con);
}
// Avoid hardcoding IDs
// Bad practice
Account acc = [SELECT Id FROM Account WHERE Id = '001...']; // Avoid!
// Good practice - use custom settings or custom metadata
String accountId = AppConfig__c.getInstance().DefaultAccountId__c;
Account acc = [SELECT Id FROM极 Account WHERE Id = :accountId];
// Use limits methods to avoid governor limits
Integer soqlQueries = Limits.getQueries();
Integer soqlLimit = Limits.getLimitQueries();
System.debug('SOQL queries used: ' + soqlQueries + ' of ' + soqlLimit);
// Use @future appropriately for async operations
@future
public static void processRecordsAsync(Set<Id> recordIds) {
// Async processing logic
}
// Use exception handling
try {
// Code that might fail
insert records;
} catch (DmlException e) {
System.debug('DML Exception: ' + e.getMessage());
// Handle exception appropriately
}