Salesforce Developer
Core Workflow
- Analyze requirements - Understand business needs, data model, governor limits, scalability
- Design solution - Choose declarative vs programmatic, plan bulkification, design integrations
- Implement - Write Apex classes, LWC components, SOQL queries with best practices
- Validate governor limits - Verify SOQL/DML counts, heap size, and CPU time stay within platform limits before proceeding
- Test thoroughly - Write test classes with 90%+ coverage, test bulk scenarios (200-record batches)
- Deploy - Use Salesforce DX, scratch orgs, CI/CD for metadata deployment
Reference Guide
Load detailed guidance based on context:
| Topic |
Reference |
Load When |
| Apex Development |
references/apex-development.md |
Classes, triggers, async patterns, batch processing |
| Lightning Web Components |
references/lightning-web-components.md |
LWC framework, component design, events, wire service |
| SOQL/SOSL |
references/soql-sosl.md |
Query optimization, relationships, governor limits |
| Integration Patterns |
references/integration-patterns.md |
REST/SOAP APIs, platform events, external services |
| Deployment & DevOps |
references/deployment-devops.md |
Salesforce DX, CI/CD, scratch orgs, metadata API |
Constraints
MUST DO
- Bulkify Apex code — collect IDs/records before loops, query/DML outside loops
- Write test classes with minimum 90% code coverage, including bulk scenarios
- Use selective SOQL queries with indexed fields; leverage relationship queries
- Use appropriate async processing (batch, queueable, future) for long-running work
- Implement proper error handling and logging; use
Database.update(scope, false) for partial success
- Use Salesforce DX for source-driven development and metadata deployment
MUST NOT DO
- Execute SOQL/DML inside loops (governor limit violation — see bulkified trigger pattern below)
- Hard-code IDs or credentials in code
- Create recursive triggers without safeguards
- Skip field-level security and sharing rules checks
- Use deprecated Salesforce APIs or components
Code Patterns
Bulkified Trigger (Correct Pattern)
// CORRECT: collect IDs, query once outside the loop
trigger AccountTrigger on Account (before insert, before update) {
AccountTriggerHandler.handleBeforeInsert(Trigger.new);
}
public class AccountTriggerHandler {
public static void handleBeforeInsert(List<Account> newAccounts) {
Set<Id> parentIds = new Set<Id>();
for (Account acc : newAccounts) {
if (acc.ParentId != null) parentIds.add(acc.ParentId);
}
Map<Id, Account> parentMap = new Map<Id, Account>(
[SELECT Id, Name FROM Account WHERE Id IN :parentIds]
);
for (Account acc : newAccounts) {
if (acc.ParentId != null && parentMap.containsKey(acc.ParentId)) {
acc.Description = 'Child of: ' + parentMap.get(acc.ParentId).Name;
}
}
}
}
// INCORRECT: SOQL inside loop — governor limit violation
trigger AccountTrigger on Account (before insert) {
for (Account acc : Trigger.new) {
Account parent = [SELECT Id, Name FROM Account WHERE Id = :acc.ParentId]; // BAD
acc.Description = 'Child of: ' + parent.Name;
}
}
Batch Apex
public class ContactBatchUpdate implements Database.Batchable<SObject> {
public Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator([SELECT Id, Email FROM Contact WHERE Email = null]);
}
public void execute(Database.BatchableContext bc, List<Contact> scope) {
for (Contact c : scope) {
c.Email = 'unknown@example.com';
}
Database.update(scope, false); // partial success allowed
}
public void finish(Database.BatchableContext bc) {
// Send notification or chain next batch
}
}
// Execute: Database.executeBatch(new ContactBatchUpdate(), 200);
Test Class
@IsTest
private class AccountTriggerHandlerTest {
@TestSetup
static void makeData() {
Account parent = new Account(Name = 'Parent Co');
insert parent;
Account child = new Account(Name = 'Child Co', ParentId = parent.Id);
insert child;
}
@IsTest
static void testBulkInsert() {
Account parent = [SELECT Id FROM Account WHERE Name = 'Parent Co' LIMIT 1];
List<Account> children = new List<Account>();
for (Integer i = 0; i < 200; i++) {
children.add(new Account(Name = 'Child ' + i, ParentId = parent.Id));
}
Test.startTest();
insert children;
Test.stopTest();
List<Account> updated = [SELECT Description FROM Account WHERE ParentId = :parent.Id];
System.assert(!updated.isEmpty(), 'Children should have descriptions set');
System.assert(updated[0].Description.startsWith('Child of:'), 'Description format mismatch');
}
}
SOQL Best Practices
// Selective query — use indexed fields in WHERE clause
List<Opportunity> opps = [
SELECT Id, Name, Amount, StageName
FROM Opportunity
WHERE AccountId IN :accountIds // indexed field
AND CloseDate >= :Date.today() // indexed field
ORDER BY CloseDate ASC
LIMIT 200
];
// Relationship query to avoid extra round-trips
List<Account> accounts = [
SELECT Id, Name,
(SELECT Id, LastName, Email FROM Contacts WHERE Email != null)
FROM Account
WHERE Id IN :accountIds
];
Lightning Web Component (Counter Example)
<!-- counterComponent.html -->
<template>
<lightning-card title="Counter">
<div class="slds-p-around_medium">
<p>Count: {count}</p>
<lightning-button label="Increment"
</div>
</lightning-card>
</template>
// counterComponent.js
import { LightningElement, track } from 'lwc';
export default class CounterComponent extends LightningElement {
@track count = 0;
handleIncrement() {
this.count += 1;
}
}
<!-- counterComponent.js-meta.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>59.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__RecordPage</target>
</targets>
</LightningComponentBundle>
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: salesforce-developer3description: Writes and debugs Apex code, builds Lightning Web Components, optimizes SOQL queries, implements triggers, batch jobs, platform events, and integrations on the Salesforce platform. Use when developing Salesforce applications, customizing CRM workflows, managing governor limits, bulk processing, or setting up Salesforce DX and CI/CD pipelines. Use when this capability is needed.4---56# Salesforce Developer78## Core Workflow9101. **Analyze requirements** - Understand business needs, data model, governor limits, scalability112. **Design solution** - Choose declarative vs programmatic, plan bulkification, design integrations123. **Implement** - Write Apex classes, LWC components, SOQL queries with best practices134. **Validate governor limits** - Verify SOQL/DML counts, heap size, and CPU time stay within platform limits before proceeding145. **Test thoroughly** - Write test classes with 90%+ coverage, test bulk scenarios (200-record batches)156. **Deploy** - Use Salesforce DX, scratch orgs, CI/CD for metadata deployment1617## Reference Guide1819Load detailed guidance based on context:2021| Topic | Reference | Load When |22|-------|-----------|-----------|23| Apex Development | `references/apex-development.md` | Classes, triggers, async patterns, batch processing |24| Lightning Web Components | `references/lightning-web-components.md` | LWC framework, component design, events, wire service |25| SOQL/SOSL | `references/soql-sosl.md` | Query optimization, relationships, governor limits |26| Integration Patterns | `references/integration-patterns.md` | REST/SOAP APIs, platform events, external services |27| Deployment & DevOps | `references/deployment-devops.md` | Salesforce DX, CI/CD, scratch orgs, metadata API |2829## Constraints3031### MUST DO32- Bulkify Apex code — collect IDs/records before loops, query/DML outside loops33- Write test classes with minimum 90% code coverage, including bulk scenarios34- Use selective SOQL queries with indexed fields; leverage relationship queries35- Use appropriate async processing (batch, queueable, future) for long-running work36- Implement proper error handling and logging; use `Database.update(scope, false)` for partial success37- Use Salesforce DX for source-driven development and metadata deployment3839### MUST NOT DO40- Execute SOQL/DML inside loops (governor limit violation — see bulkified trigger pattern below)41- Hard-code IDs or credentials in code42- Create recursive triggers without safeguards43- Skip field-level security and sharing rules checks44- Use deprecated Salesforce APIs or components4546## Code Patterns4748### Bulkified Trigger (Correct Pattern)4950```apex51// CORRECT: collect IDs, query once outside the loop52trigger AccountTrigger on Account (before insert, before update) {53 AccountTriggerHandler.handleBeforeInsert(Trigger.new);54}5556public class AccountTriggerHandler {57 public static void handleBeforeInsert(List<Account> newAccounts) {58 Set<Id> parentIds = new Set<Id>();59 for (Account acc : newAccounts) {60 if (acc.ParentId != null) parentIds.add(acc.ParentId);61 }62 Map<Id, Account> parentMap = new Map<Id, Account>(63 [SELECT Id, Name FROM Account WHERE Id IN :parentIds]64 );65 for (Account acc : newAccounts) {66 if (acc.ParentId != null && parentMap.containsKey(acc.ParentId)) {67 acc.Description = 'Child of: ' + parentMap.get(acc.ParentId).Name;68 }69 }70 }71}72```7374```apex75// INCORRECT: SOQL inside loop — governor limit violation76trigger AccountTrigger on Account (before insert) {77 for (Account acc : Trigger.new) {78 Account parent = [SELECT Id, Name FROM Account WHERE Id = :acc.ParentId]; // BAD79 acc.Description = 'Child of: ' + parent.Name;80 }81}82```8384### Batch Apex8586```apex87public class ContactBatchUpdate implements Database.Batchable<SObject> {88 public Database.QueryLocator start(Database.BatchableContext bc) {89 return Database.getQueryLocator([SELECT Id, Email FROM Contact WHERE Email = null]);90 }91 public void execute(Database.BatchableContext bc, List<Contact> scope) {92 for (Contact c : scope) {93 c.Email = 'unknown@example.com';94 }95 Database.update(scope, false); // partial success allowed96 }97 public void finish(Database.BatchableContext bc) {98 // Send notification or chain next batch99 }100}101// Execute: Database.executeBatch(new ContactBatchUpdate(), 200);102```103104### Test Class105106```apex107@IsTest108private class AccountTriggerHandlerTest {109 @TestSetup110 static void makeData() {111 Account parent = new Account(Name = 'Parent Co');112 insert parent;113 Account child = new Account(Name = 'Child Co', ParentId = parent.Id);114 insert child;115 }116117 @IsTest118 static void testBulkInsert() {119 Account parent = [SELECT Id FROM Account WHERE Name = 'Parent Co' LIMIT 1];120 List<Account> children = new List<Account>();121 for (Integer i = 0; i < 200; i++) {122 children.add(new Account(Name = 'Child ' + i, ParentId = parent.Id));123 }124 Test.startTest();125 insert children;126 Test.stopTest();127128 List<Account> updated = [SELECT Description FROM Account WHERE ParentId = :parent.Id];129 System.assert(!updated.isEmpty(), 'Children should have descriptions set');130 System.assert(updated[0].Description.startsWith('Child of:'), 'Description format mismatch');131 }132}133```134135### SOQL Best Practices136137```apex138// Selective query — use indexed fields in WHERE clause139List<Opportunity> opps = [140 SELECT Id, Name, Amount, StageName141 FROM Opportunity142 WHERE AccountId IN :accountIds // indexed field143 AND CloseDate >= :Date.today() // indexed field144 ORDER BY CloseDate ASC145 LIMIT 200146];147148// Relationship query to avoid extra round-trips149List<Account> accounts = [150 SELECT Id, Name,151 (SELECT Id, LastName, Email FROM Contacts WHERE Email != null)152 FROM Account153 WHERE Id IN :accountIds154];155```156157### Lightning Web Component (Counter Example)158159```html160<!-- counterComponent.html -->161<template>162 <lightning-card title="Counter">163 <div class="slds-p-around_medium">164 <p>Count: {count}</p>165 <lightning-button label="Increment" onclick={handleIncrement}></lightning-button>166 </div>167 </lightning-card>168</template>169```170171```javascript172// counterComponent.js173import { LightningElement, track } from 'lwc';174export default class CounterComponent extends LightningElement {175 @track count = 0;176 handleIncrement() {177 this.count += 1;178 }179}180```181182```xml183<!-- counterComponent.js-meta.xml -->184<?xml version="1.0" encoding="UTF-8"?>185<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">186 <apiVersion>59.0</apiVersion>187 <isExposed>true</isExposed>188 <targets>189 <target>lightning__AppPage</target>190 <target>lightning__RecordPage</target>191 </targets>192</LightningComponentBundle>193```194195---196> Converted and distributed by [TomeVault](https://tomevault.io/claim/jeffallan) — claim your Tome and manage your conversions.197<!-- tomevault:4.0:skill_md:2026-04-11 -->