Generate Apex classes and tests with bulkification, CRUD/FLS, and the project's trigger framework wired in. Covers services, selectors, domain classes, batch / queueable / schedulable, invocable methods, REST resources, and the matching test classes with TestDataFactory + 251+ record bulk tests. Trigger phrases: 'create an Apex class', 'generate a service class', 'write a queueable for', 'scaffold a batch class', 'build an @InvocableMethod', 'add a REST resource', 'generate Apex tests for', 'cover this class with tests'. Pairs with `apex-patterns` (reference) and `test-factory` (TDF reference). Do NOT trigger for triggers themselves — use `apex-trigger-refactor` for trigger work.
How this skill is triggered — by the user, by Claude, or both
Slash command
/sf-compound-engineering:apex-generate [class type, target object, business intent, or 'test for <ClassName>'][class type, target object, business intent, or 'test for <ClassName>']The summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **<span data-proof="authored" data-by="ai:claude">Principles enforced:</span>** <span data-proof="authored" data-by="ai:claude">1 (preserve the quality ceiling), 2 (verifiability), 5 (taste over typing). See</span> <span data-proof="authored" data-by="ai:claude">`PRINCIPLES.md`.</span>
Principles enforced: 1 (preserve the quality ceiling), 2 (verifiability), 5 (taste over typing). See
PRINCIPLES.md.
Generate production-grade Apex (class + .cls-meta.xml) AND its test class (Test.cls +
.cls-meta.xml) as one unit. Test generation is mandatory, not optional. Bulkify all SOQL
and DML out of loops. Enforce CRUD/FLS via Schema.* checks or USER_MODE. Declare an explicit
sharing keyword on every class. Test with 251+ records to cross the 200-trigger boundary.
Use TestDataFactory — never inline record creation in @TestSetup. Use Assert.* class only —
never legacy System.assertEquals. After generation, run sf code-analyzer and sf apex run
test; remediate sev0/sev1/sev2 violations and capture pass/fail + coverage in the report.
Class type — service, selector, domain, batch, queueable, schedulable, invocable, DTO, utility, interface, abstract, exception, REST resource
Target object(s) and the business intent
Class name — derive from type + target (e.g., OpportunityService, LeadSelector, OrderBatch)
Net-new vs refactor — and any API version constraints
Verification target — coverage %, specific assertions
Defaults if unspecified: with sharing, public, API 66.0, ApexDoc on every public method, test class mandatory.
If the request is clear, generate immediately. Don't ask back-and-forth on routine choices.
Task sf-learnings-researcher(class_intent) — has anyone solved this shape? Must-read.
Task sf-repo-research-analyst(class_intent) — what selectors / services / handlers / TDF already exist? Reuse before generating.
Read skills/apex-patterns/SKILL.md (Selector / Service / Domain / Trigger Handler patterns)
Read skills/governor-limits/SKILL.md (limit-bearing operations)
Read skills/test-factory/SKILL.md (TDF patterns — required for the test class step)
@InvocableMethod for Flow callability.| Rule | Why |
|---|---|
| All SOQL outside loops | Governor limit (100 per transaction) |
| All DML outside loops | Governor limit (150 per transaction) |
| Sharing keyword declared on every class | with sharing / without sharing / inherited sharing — never default |
| CRUD/FLS enforced on every database access | Use Schema.sObjectType.X.isAccessible() or WITH USER_MODE / AccessLevel.USER_MODE |
| Bulkify all entry points | Methods accept List<SObject> / Map<Id, SObject>, not single records |
| ApexDoc on every public method | Includes @param, @return, @throws, business rationale |
@AuraEnabled(cacheable=true) for read-only LWC reads | Cache hit + governor relief |
Generate two files together:
{ClassName}.cls
{ClassName}.cls-meta.xml (apiVersion 66.0, status Active)
A class without tests is not a deliverable. Generate {ClassName}Test.cls and its .cls-meta.xml immediately.
Test discipline (every rule is enforced):
| Rule | Application |
|---|---|
| One behavior per method | shouldUpdateStatus_WhenValidInput, shouldThrow_WhenNullInput, shouldHandleBulk_When251Records — separate methods per scenario |
| Bulkify with 251+ records | Crosses the 200-trigger batch boundary. For batch Apex tests, set batchSize >= testRecordCount |
Isolate test data via TestDataFactory | Never inline record building in @TestSetup. If TDF doesn't exist, generate it first |
Use Assertclass only | Assert.areEqual, Assert.isTrue, Assert.fail — never legacy System.assertEquals |
Wrap with Test.startTest() / Test.stopTest() | Resets governor limits, fires async synchronously |
| Mock external boundaries | HttpCalloutMock for callouts, Test.setFixedSearchResults for SOSL, DML mocks via dependency injection |
| Test negative paths | Validate exceptions and error handling, not just happy paths |
No SeeAllData=true | Org data dependency = flaky tests |
| No magic numbers in assertions | Derive expected values from setup constants |
Given/When/Then structure inside every test method:
@isTest
static void shouldUpdateStatus_WhenValidInput() {
// Given
List<Account> accounts = [SELECT Id FROM Account];
// When
Test.startTest();
MyService.processAccounts(accounts);
Test.stopTest();
// Then
Assert.areEqual(251, [SELECT COUNT() FROM Account WHERE Status__c = 'Processed'],
'All accounts should be marked Processed');
}
Coverage targets: 75% minimum to deploy, 90%+ recommended per class.
Writing files is the midpoint. The deliverable isn't done until both checks pass.
# Static analysis
sf code-analyzer run --target force-app/main/default/classes/{ClassName}.cls --json
# Or via MCP: run_code_analyzer on the generated files
# Test execution
sf apex run test --tests {ClassName}Test --code-coverage --result-format human --synchronous --json
Remediate every sev0/sev1/sev2 violation and re-run. If tests fail, fix and re-run — don't paper over with try/catch.
Generated:
- {ClassName}.cls + .cls-meta.xml
- {ClassName}Test.cls + .cls-meta.xml
- TestDataFactory.cls + .cls-meta.xml (if not previously present)
Analyzer: {sev0=0, sev1=0, sev2=0, sev3=N} or "unavailable: <reason>"
Testing: {pass=N, fail=0, coverage=NN%} or "unavailable: <reason>"
Patterns: {layer=Service|Selector|Domain|Async|Invocable|REST}
Sharing: {with|without|inherited} sharing
Bulkification: PASS — no SOQL/DML in loops
CRUD/FLS: {Schema.* checks | USER_MODE | both}
A report missing analyzer or testing lines is incomplete. Always invoke the tools before claiming unavailable.
apex-generate test for <ClassName>)If invoked with test for <ClassName>, skip Phase 1 and treat the existing .cls as input. Read it, identify untested branches, generate or extend the test class, run Phase 3 validation. Same discipline applies.
After successful generation, if the work surfaced a non-obvious pattern (e.g., a TDF helper that handles a duplicate-rule edge case, a mock pattern for a third-party callout), run /sf-compound to capture it under docs/solutions/ (Principle 7).
Adapted from forcedotcom/afv-library/skills/generating-apex and generating-apex-test (Apache-2.0). Upstream ships separate skills with extensive assets/ template files and references/ for async-testing, mocking-patterns, assertion-patterns, and TDF. This plugin merges them into one because production-class + test-class is one transaction, not two. For the full template files and edge-case tables (duplicate rules, multi-org test data isolation, governor-limit reset semantics under Test.stopTest()), consult the upstream references.
npx claudepluginhub sangameshgupta/sf-compound-engineering-plugin --plugin sf-compound-engineeringGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.