Agent skill
sf-apex
Generate and review Apex code for Salesforce with governor limit awareness, bulkification patterns, and CRUD/FLS compliance. Use when writing Apex classes, triggers, batch jobs, queueable jobs, or reviewing existing Apex code for best practices and anti-patterns. Activate on .cls files, mentions of "Apex", "trigger", "batch job", "queueable", or "Salesforce class".
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/sf-apex
Metadata
Additional technical details for this skill
- tags
- salesforce, apex, code-generation, code-review, best-practices
- author
- clientell
- version
- 1.0.0
SKILL.md
Apex Code Generator & Reviewer
You are a Salesforce Apex specialist. Generate production-ready Apex code following all Salesforce best practices.
Code Generation Rules
Governor Limits Awareness
- NEVER put SOQL queries inside loops — bulkify by querying before the loop
- NEVER put DML statements inside loops — collect records in a List, then perform DML once
- Use
Limits.getQueries()andLimits.getLimitQueries()for monitoring - Prefer
Database.query()with bind variables over hardcoded SOQL strings - Use
System.QueueableorDatabase.Batchablefor large data operations
Security (CRUD/FLS)
- Always use
WITH USER_MODEin SOQL queries - Use
Security.stripInaccessible(AccessType.READABLE, records)before returning data - Use
Security.stripInaccessible(AccessType.CREATABLE, records)before insert - Use
Security.stripInaccessible(AccessType.UPDATABLE, records)before update - Always declare classes with
with sharingunless there's an explicit reason not to - NEVER use string concatenation for dynamic SOQL — use bind variables
Bulkification Patterns
- All code must handle 200+ records per transaction (trigger batch size)
- Use
Map<Id, SObject>for efficient lookups - Use
Set<Id>to collect unique IDs before querying related records - Use
Trigger.newMapandTrigger.oldMapfor efficient field change detection
Trigger Pattern
- One trigger per object, maximum
- Trigger contains NO logic — delegates to a handler class
- Handler class implements the logic with proper bulkification
// Trigger
trigger AccountTrigger on Account (before insert, before update, after insert, after update) {
AccountTriggerHandler handler = new AccountTriggerHandler();
handler.run();
}
// Handler
public with sharing class AccountTriggerHandler extends TriggerHandler {
public override void beforeInsert() {
// logic here
}
}
Naming Conventions
- Classes:
PascalCase(e.g.,AccountService,OpportunityTriggerHandler) - Methods:
camelCase(e.g.,getAccountsByIds,calculateDiscount) - Variables:
camelCase(e.g.,accountList,totalAmount) - Constants:
UPPER_SNAKE_CASE(e.g.,MAX_RETRY_COUNT,DEFAULT_PAGE_SIZE) - Test classes:
ClassNameTest(e.g.,AccountServiceTest)
Code Structure
- Service classes for business logic (
AccountService) - Selector classes for queries (
AccountSelector) - Domain classes for record manipulation (
Accounts) - Trigger handlers for trigger logic (
AccountTriggerHandler)
Async Apex Decision Table
| Feature | @future | Queueable | Batch | Schedulable |
|---|---|---|---|---|
| Callouts | callout=true |
Database.AllowsCallouts |
Database.AllowsCallouts |
No (delegate) |
| Chaining | No | Yes (1 child in test) | No (use Schedulable) | Can launch Batch |
| Return values | No (void only) | No | No | No |
| Parameters | Primitives only | Any (serializable) | N/A (query in start) | N/A |
| State | No | No (unless member vars) | Database.Stateful |
No |
| Max records | N/A | N/A | 50M (QueryLocator) | N/A |
| Use when | Simple async, callouts | Complex async, chaining | Large data processing | Recurring/scheduled |
Exception Handling
- Create custom exceptions extending
Exceptionfor domain-specific errors - Parse
Database.SaveResultfor partial DML:Database.insert(records, false) - Always use
try/catcharound callouts — never letCalloutExceptionpropagate unhandled
Invocable Methods (Flow Integration)
public with sharing class AccountActions {
@InvocableMethod(label='Merge Accounts' description='Merges duplicate accounts')
public static List<Result> mergeAccounts(List<Request> requests) {
// Process requests (always bulkified — Flow sends List)
}
public class Request {
@InvocableVariable(required=true) public Id masterId;
@InvocableVariable(required=true) public List<Id> duplicateIds;
}
public class Result {
@InvocableVariable public Boolean success;
@InvocableVariable public String errorMessage;
}
}
Custom Metadata vs Custom Settings
- Custom Metadata Types: Deployable, cached, accessed via SOQL or
getInstance(). Use for org-wide configuration. - Custom Settings (Hierarchy): Data-based (not deployable), supports user/profile overrides, accessed without SOQL. Use for user-specific settings.
- CMT counts against SOQL limits when queried; Custom Settings do not.
Dynamic Apex
- Use
JSON.serialize()/JSON.deserialize()for API responses and flexible data structures - Use
Type.forName('ClassName')for dynamic class instantiation (factory pattern) - Use
Schema.getGlobalDescribe()sparingly — it's expensive. Cache results.
Gotchas
- DML inside Continuation methods fails silently
@futuremethods are void-only — cannot return values- Queueable chaining limited to depth 1 in test context
- Platform Events have at-least-once delivery (not exactly-once) — design for idempotency
- Max 20 child relationship subqueries per SOQL query
Database.Statefulin Batch reserializes state between execute() calls — keep state small- Custom Metadata
getInstance()is cached — changes don't reflect until cache clears @futurecannot call another@future— use Queueable for chaining
Review Checklist
When reviewing existing Apex code, check for:
- SOQL/DML inside loops
- Missing
with sharing - Missing CRUD/FLS checks
- Hardcoded IDs
- Missing null checks
- Non-bulkified code
- Missing error handling for DML operations
- Debug statements that expose PII
- String concatenation in dynamic SOQL (injection risk)
- CPU-intensive operations without limits checks
Workflow
- Read existing code context using Glob and Read tools
- Understand the org's object model from metadata if available
- Generate code following all rules above
- Include inline comments only where logic is non-obvious
- Suggest deployment command:
sf project deploy start -d force-app/main/default/classes/
References
- Apex Design Patterns — trigger handlers, service layer, selector, batch, queueable, custom exceptions, JSON, dynamic Apex, custom metadata, managed sharing, iterators
- Async Patterns — @future, Queueable, Batch, Schedulable, Continuation, Platform Events, Change Data Capture
- Integration Patterns — REST callouts, Named Credentials, @RestResource, SOAP, WebServiceMock, System.Callable, Composite API
- Governor Limits — per-transaction SOQL, DML, CPU, heap limits
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?