salesforce-developer
Salesforce Developer Skill
How to Use This Skill
Read the appropriate reference file(s) AND the corresponding coding ruleset before generating code:
Step 1: Read the Coding Rulesets (MANDATORY for all code generation)
| Code Type | Ruleset File |
|---|---|
| Apex (classes, triggers, tests, async, integrations) | ../../Blogs/salesforce-apex-coding-rules.md |
| LWC (components, templates, JS, events, Jest tests) | ../../Blogs/salesforce-lwc-coding-rules.md |
These rulesets contain comprehensive coding standards, anti-patterns, PMD/ESLint rules, and code examples. Always apply these rules to all generated code.
Step 2: Read the Appropriate Reference File(s)
| User's Task | Reference File |
|---|---|
| Apex classes, triggers, testing, DML, async, dynamic Apex, JSON, wrappers, interfaces, debugging | references/apex-patterns.md |
| SOQL/SOSL, query optimization, dynamic SOQL, Big Objects, LDV, cursors | references/soql-optimization.md |
| LWC, Aura, dynamic components, lazy loading | references/lwc-guide.md |
| REST/Bulk/SOAP API, integrations, OAuth, Named Credentials | references/api-integration.md |
| Formulas, validation rules | references/formulas-validation.md |
| Flows, screen flows, record-triggered flows, process automation | references/flows-automation.md |
| Security, sharing, CRUD/FLS, permissions, encryption | references/security-sharing.md |
| Deployment, sf CLI, scratch orgs, CI/CD, packaging | references/deployment-devops.md |
| Agentforce, AI agents, Prompt Builder, platform events, CDC | references/agentforce-ai.md |
Step 3: Cross-Reference the Architect Skill (for design questions)
For tasks involving architecture, data modeling, integration patterns, or solution design, also read:
- Architect Skill: ../salesforce-architect-skill/SKILL.md — Well-Architected framework, decision guides, integration patterns
- Then read the relevant reference files inside
../salesforce-architect-skill/references/(e.g.,data-model-patterns.md,integration-patterns.md,well-architected-checklist.md)
For complex tasks (e.g., "build an LWC that calls Apex with integration"), read multiple reference files from both skills AND both rulesets.
Mandatory Rules
- Bulkify all Apex — no SOQL/DML inside loops. Handle 200+ records in triggers.
- Governor limits — 100 SOQL (sync), 150 DML, 50k rows, 10s CPU, 6 MB heap.
- One trigger per object — handler class with
switch on Trigger.operationType. - Security —
with sharingon classes,WITH SECURITY_ENFORCEDin SOQL,Security.stripInaccessible()for DML. - Testing — 200-record bulk tests, positive/negative/edge,
@TestSetup,Test.startTest()/Test.stopTest(). - Error handling — try-catch on DML,
AuraHandledExceptionfor LWC,Database.insert(records, false)for partial success. - Latest GA API version (currently 66.0) on all new metadata. Update when Salesforce releases a new version.
- Always include
-meta.xmlfor classes, triggers, LWC. - Use
sfCLI (not deprecatedsfdx). - Layered architecture — Trigger → Handler → Service → Selector.
LLM Anti-Patterns — NEVER Generate These
These are the most common AI mistakes in Salesforce code. For full code examples of each rule, see references/apex-patterns.md.
Apex Compilation Errors
- A1: SOQL Field Coverage — Every field referenced in Apex MUST be in the SOQL SELECT clause. This is the #1 AI mistake.
- A2: Relationship Fields — Parent = dot notation (
Account.Name), child = subquery ((SELECT Id FROM Contacts)). Never mix them. - A3: Non-Existent Methods —
Datetime.addMilliseconds(),String.contains()with regex,List.sort(comparator),Map.values().sort()do NOT exist. - A4: Non-Existent Types —
StringBuffer,StringBuilder,HashMap,ArrayList,HashSet,char,byte[]do NOT exist in Apex. UseIntegernotint(Apex has no true Java-style primitives; while the language is case-insensitive for its own types,intis not a valid Apex type at all). - A5: Static vs Instance —
String.toLowerCase()is wrong (instance method).myString.valueOf()is wrong (static method). - A6: String Utilities — Use
String.isBlank()for user input (handles null, empty, whitespace).isEmpty()misses whitespace.
LWC Template Errors
- L1: No Inline Expressions — LWC templates do NOT support
{a + b}, ternary, negation, or object literals. Use JavaScript getters. - L2: Import Decorators — Always import
api,track,wirefrom'lwc'before using them. - L3: Event Naming — Custom events must be all lowercase, no hyphens. Parent uses
onprefix:onmyevent.
Runtime Errors
- R1: Null Checks — Always query into a
List, then check!isEmpty()before accessing[0]. - R2: Map.containsKey() — Check before
Map.get()to avoid NPE. - R3: Recursive Trigger Prevention — Use
static Set<Id>, NOTstatic Boolean. Boolean blocks all subsequent records. - R4: Guard DML — Check
!list.isEmpty()before DML. Saves CPU even though empty DML doesn't consume limits. - R5: MIXED_DML — Cannot DML setup objects (User, Profile) and non-setup objects in same transaction. Use
@futureor Queueable.
Deployment Errors
- D1: Permission Set Fields — Only include FLS for fields on the correct object. Skip standard non-FLS fields (
Name,Id,CreatedDate). - D2: Permission Set Apex Access — Include
classAccessesfor Apex classes used by LWC via@AuraEnabled. - D3: package.xml Order — CustomObject → CustomField → ApexClass → ApexTrigger → Layout → PermissionSet → Profile.
Integration Errors
- I1: Same-Org Endpoints — Use org's instance URL, not
api.salesforce.com. - I2: Callout After DML — Cannot make HTTP callout after DML in same transaction. Callout first, or use
@future(callout=true).
Agentforce & Flow Errors
- AF1: @AuraEnabled — Must be
public static. With@wire, must havecacheable=true. Class must bewith sharing. - AF2: @InvocableVariable Types — Primitives,
List<String>,List<Id>,SObject,List<SObject>, Apex-defined types. NO Maps. - AF3: @JsonAccess — Custom return types for Agentforce actions need
@JsonAccess(serializable='always').
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Classes | PascalCase + suffix | AccountTriggerHandler, OrderService, ContactSelector |
| Test Classes | PascalCase + Test |
AccountServiceTest |
| Methods | camelCase, verb-first | getAccountsByIds(), calculateTotal() |
| Variables | camelCase | accountList, totalRevenue |
| Constants | UPPER_SNAKE_CASE | MAX_RETRY_COUNT, DEFAULT_PAGE_SIZE |
| Triggers | {Object}Trigger |
AccountTrigger |
| LWC | camelCase folder, kebab-case markup | accountList → <c-account-list> |
| Custom Objects | PascalCase + __c |
Invoice_Line_Item__c |
| Custom Fields | PascalCase + __c |
Total_Amount__c |
| Platform Events | PascalCase + __e |
Order_Placed__e |
| Custom Metadata | PascalCase + __mdt |
Integration_Config__mdt |
Governor Limits Quick Reference
| Resource | Sync Limit | Async Limit |
|---|---|---|
| SOQL queries | 100 | 200 |
| SOQL rows | 50,000 | 50,000 |
| DML statements | 150 | 150 |
| DML rows | 10,000 | 10,000 |
| CPU time | 10,000 ms | 60,000 ms |
| Heap size | 6 MB | 12 MB |
| Callouts | 100 | 100 |
| Future calls | 50 | 0 (in future) |
| Queueable jobs | 50 | 1 |
Async Apex Decision Guide
| Pattern | Use When | Key Notes |
|---|---|---|
@future |
Fire-and-forget, callouts | Primitives only, max 50/txn, can't chain |
Queueable |
Chaining, complex types, job ID | 50/txn sync, 1 child async, Transaction Finalizers |
Batch Apex |
50k+ records | 200/execute, QueryLocator up to 50M rows |
Schedulable |
Recurring CRON jobs | 100 scheduled jobs/org |
| Platform Events | Event-driven, decoupled | EventBus.publish(), RetryableException for retry |
For full async patterns, see references/apex-patterns.md.
Order of Execution
- Load original record / initialize for insert
- Overwrite with new field values
- System validation (required fields, formats)
- Before-save record-triggered flows
- Before triggers
- System validation + custom validation rules
- Duplicate rules
- Record saved to DB (not yet committed)
- After triggers
- Assignment/auto-response rules
- Workflow rules (field updates re-fire triggers ONCE)
- Process Builder / flow trigger workflow actions
- After-save record-triggered flows
- Roll-up summary calculations
- Criteria-based sharing evaluation
- DML committed
- Post-commit: email, async Apex, async flow paths
Key: Before triggers can modify Trigger.new without DML. After triggers CANNOT.
Code Generation Checklist
- Every field referenced in code MUST be in the SOQL SELECT clause (Rule A1)
- Include
-meta.xmlfor all new classes, triggers, LWC - Latest GA API version (currently 66.0) on all metadata
- Include JSDoc in LWC JavaScript and ApexDoc in Apex
- Wrap DML in try-catch, use
AuraHandledExceptionfor LWC - Use
WITH SECURITY_ENFORCEDin SOQL - Use
Database.insert(records, false)for partial success - Null-check query results,
Map.get()returns, parent relationship fields - Guard DML with
!list.isEmpty() - Use
with sharingon user-facing classes,inherited sharingon utilities - Use
switch on Trigger.operationTypein handlers - Never hardcode IDs — use
Schema.describe, Custom Metadata, or Custom Labels - Prefer
Assert.areEqual()(modern Assert class) overSystem.assertEquals()for new code - Test methods use Arrange-Act-Assert with descriptive names
Common Error Solutions
| Error | Fix |
|---|---|
Too many SOQL queries: 101 |
Move query outside loop, use Maps |
MIXED_DML_OPERATION |
Use @future or Queueable for setup object DML |
Non-selective query |
Add indexed filter, request custom index |
You have uncommitted work |
Move callout before DML or use @future(callout=true) |
NullPointerException |
Add null checks; query into List + isEmpty() |
SObject row was retrieved via SOQL without querying the requested field |
Add field to SOQL SELECT (Rule A1) |
Too many DML statements: 151 |
Collect records in lists, single DML outside loop |
UNABLE_TO_LOCK_ROW |
Use FOR UPDATE or implement retry |
Apex CPU time limit exceeded |
Optimize loops, use async Apex |
For full error patterns and JSON/debugging details, see references/apex-patterns.md.
Metadata XML Templates
Apex Class
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>66.0</apiVersion>
<status>Active</status>
</ApexClass>
Apex Trigger
<?xml version="1.0" encoding="UTF-8"?>
<ApexTrigger xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>66.0</apiVersion>
<status>Active</status>
</ApexTrigger>
LWC Component
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>66.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
Project Structure
force-app/main/default/
├── classes/ # Handler, Service, Selector, Controller, Test classes + -meta.xml
├── triggers/ # One trigger per object + -meta.xml
├── lwc/ # camelCase folders with html/js/css/js-meta.xml + __tests__/
├── objects/ # fields/, validationRules/, listViews/
├── permissionsets/
├── customMetadata/
└── labels/
Architecture: Trigger (thin) → Handler (routing, recursion guard) → Service (business logic, DML) → Selector (SOQL, with sharing)
Quick Reference Pointers
- Apex coding rules (full ruleset) → ../../Blogs/salesforce-apex-coding-rules.md
- LWC coding rules (full ruleset) → ../../Blogs/salesforce-lwc-coding-rules.md
- Architecture & solution design → ../salesforce-architect-skill/SKILL.md
- Security & sharing rules → references/security-sharing.md
- Agentforce, platform events, CDC → references/agentforce-ai.md
- OAuth, Named Credentials, Connected Apps → references/api-integration.md
- LDV, custom indexes, data skew, Big Objects → references/soql-optimization.md
- Dynamic LWC, lazy loading → references/lwc-guide.md
- sf CLI commands, CI/CD pipelines → references/deployment-devops.md
- Flow decision guide, invocable Apex → references/flows-automation.md