afv-library/skills/generating-apex/SKILL.md

18 KiB
Raw Blame History

name description
generating-apex Primary Apex authoring skill for class generation, refactoring, and review. ALWAYS ACTIVATE when the user mentions Apex, .cls, triggers, or asks to create/refactor a class (service, selector, domain, batch, queueable, schedulable, invocable, DTO, utility, interface, abstract, exception). Use this skill for requests involving SObject CRUD, mapping collections, fetching related records, scheduled jobs, batch jobs, trigger design, @AuraEnabled controllers, or code review of existing Apex.

Generating Apex

Use this skill for production-grade Apex: new classes, selectors, services, async jobs, invocable methods, and triggers; and for evidence-based review of existing .cls.

When This Skill Owns the Task

  • Any Apex class generation or refactor (service, selector, domain, DTO/wrapper, utility, interface, abstract, exception)
  • Trigger design and trigger-framework decisions
  • Async or orchestration: Batch, Queueable (incl. Finalizer), Schedulable, CursorStep, @InvocableMethod
  • @AuraEnabled controllers for LWC/Aura
  • Review of bulkification, sharing, security, or maintainability

Required Inputs

Gather or infer before authoring:

  • Class type (service, selector, domain, batch, queueable, schedulable, invocable, trigger, trigger action, DTO, utility, interface, abstract, exception)
  • Target object(s) and business goal
  • Class name (derive using the naming table below)
  • Net-new vs refactor/fix; any org/API constraints
  • Deployment targets

Defaults unless specified:

  • Sharing: with sharing (see sharing rules per type below)
  • Access: public (use global only when required by managed packages or @InvocableMethod)
  • API version: 62.0
  • ApexDoc comments: yes

Workflow

All steps in this workflow are MANDATORY and must be executed in order. Execute every step without skipping, merging, or reordering. If a step is blocked or seemingly not applicable, STOP and request the missing context, or explicitly mark it as "N/A" with a one-sentence justification in the final report before proceeding. Continue only when the gate for the current step is satisfied.

  1. [MANDATORY] Discover project conventions

    • ServiceSelectorDomain layering, logging utilities
    • Existing classes/triggers and current trigger framework or handler pattern
    • Whether Trigger Actions Framework (TAF) is already in use
  2. [MANDATORY] Choose the smallest correct pattern (see Type-Specific Guidance below)

  3. [MANDATORY] Review templates and assets

    • Check this skills templates/ and assets/
    • For any test class work, always read and use generating-apex-test skill
  4. [MANDATORY] Author with guardrails — apply every rule in the Rules section below

    • Generate {ClassName}.cls with ApexDoc
    • Generate {ClassName}.cls-meta.xml
  5. [MANDATORY] Generate test classes — Use the skill generating-apex-test and follow its complete workflow to generate {ClassName}Test.cls and {ClassName}Test.cls-meta.xml. You MUST use that skill now and execute its instructions before proceeding to Step 6.

  6. [MANDATORY] Static analysis

    • Execute the MCP tool run_code_analyzer on the newly generated/updated files
    • Remediate all violations with severity levels sev0, sev1, and sev2
    • Re-run run_code_analyzer until no sev0sev2 issues remain
  7. [MANDATORY] Execute Apex tests

    • Run the orgs Apex test suite including {ClassName}Test
    • All test authoring, failure remediation, and coverage improvements MUST be performed by reading and following skills/generating-apex-test/SKILL.md; iterate until green
  8. [MANDATORY] Report using the output format at the bottom of this file

  9. [MANDATORY] Enforce cross-skill boundaries

    • Test class creation, updates, refactors, data setup, and advanced fixtures: ALWAYS delegate by reading and following skills/generating-apex-test/SKILL.md
    • NEVER write test code directly in this skill; all test work flows through the test skill

Rules

Hard-Stop Constraints (Must Enforce)

If any constraint would be violated in generated code, stop and explain the problem before proceeding:

Constraint Rationale
Place all SOQL outside loops Avoid query governor limits (100 queries)
Place all DML outside loops Avoid DML governor limits (150 statements)
Declare a sharing keyword on every class Prevent unintended without sharing defaults and data exposure
Use Custom Metadata/Labels/describe calls instead of hardcoded IDs Ensure portability across orgs
Always handle exceptions (log, rethrow, or recover) Prevent silent failures
Use bind variables for all dynamic SOQL with user input Prevent SOQL injection
Use Apex-native collections (List, Map, Set) rather than Java types Prevent compile errors
Verify methods exist in Apex before use Prevent reliance on non-existent APIs
Use Assert class instead of System.assert* in test classes Legacy System.assert, System.assertEquals, System.assertNotEquals are deprecated; use Assert.areEqual, Assert.isTrue, Assert.fail, etc.

Bulkification & Governor Limits

  • Collect all SOQL and DML outside of loops; operate on List, Set, Map
  • All public APIs accept and process collections; single-record overloads delegate to the bulk method
  • In batch/bulk flows, prefer partial-success DML (Database.update(records, false)) and process SaveResult for errors
  • Use Map<Id, SObject> constructor for efficient ID-based lookups from query results
  • Use Set<Id> for deduplication and membership checks; prefer Set.contains() over List.contains()

Security

  • Default to with sharing; document the justification when without sharing or inherited sharing is chosen
  • Use WITH USER_MODE in SOQL queries for automatic CRUD/FLS enforcement
  • Use bind variables (:variableName) for all dynamic SOQL; sanitize queries by binding variables rather than concatenating user input
  • For dynamic field/operator names in SOQL, validate against an allowlist or Schema.describe before use
  • Use Security.stripInaccessible() when WITH USER_MODE is not available (pre-API 62.0)
  • Store credentials and API keys in Named Credentials and reference them in code
  • Use AuraHandledException for user-facing errors in @AuraEnabled methods; ensure messages are user-safe and exclude internal details
  • When using without sharing, add a Custom Permission check to restrict access

Error Handling

  • Catch specific exceptions before generic Exception; include context in messages
  • Preserve exception cause chains: use new CustomException('message', causedException) — preserve the original stack trace by passing the cause rather than concatenating e.getMessage() into a new exception
  • Provide a custom exception class per service domain when meaningful
  • In @AuraEnabled methods, catch exceptions and rethrow as AuraHandledException

Null Safety

  • Add guard clauses for null/empty inputs at the top of every public method
  • Return empty collections instead of null
  • Use safe navigation (?.) for chained property access
  • Use null coalescing (??) for default values
  • Prefer String.isBlank(value) over manual checks like value == null || value.trim().isEmpty()

Constants & Literals

  • Extract all literal strings and numbers into private static final constants or a dedicated constants class
  • Use Label. custom labels for user-facing strings
  • Use Custom Metadata for configurable values (thresholds, mappings, feature flags)
  • Never output HTML-escaped entities in code (e.g., &#39;); use literal single quotes ' in Apex string literals

Naming Conventions

Type Pattern Example
Service {SObject}Service AccountService
Selector {SObject}Selector AccountSelector
Domain {SObject}Domain OpportunityDomain
Batch {Descriptive}Batch AccountDeduplicationBatch
Queueable {Descriptive}Queueable ExternalSyncQueueable
Schedulable {Descriptive}Schedulable DailyCleanupSchedulable
DTO {Descriptive}DTO AccountMergeRequestDTO
Wrapper {Descriptive}Wrapper OpportunityLineWrapper
Utility {Descriptive}Util StringUtil
Interface I{Descriptive} INotificationService
Abstract Abstract{Descriptive} AbstractIntegrationService
Exception {Descriptive}Exception AccountServiceException
Trigger {SObject}Trigger AccountTrigger
Trigger Action TA_{SObject}_{Action} TA_Account_SetDefaults

Additional naming rules:

  • Classes: PascalCase
  • Methods: camelCase, start with a verb (get, create, process, validate, is, has)
  • Variables: camelCase, descriptive nouns; Maps as {value}By{key} (e.g., accountsById); Sets as {noun}Ids
  • Constants: UPPER_SNAKE_CASE
  • Use full descriptive names instead of abbreviations (acc, tks, rec)

ApexDoc

  • Required on the class header and every public/global method
  • Include: brief description, @param, @return, @throws, @example where helpful

Modern Apex Idioms

Prefer current language features:

  • Safe navigation: obj?.Field__c
  • Null coalescing: value ?? fallback
  • WITH USER_MODE over manual CRUD/FLS checks
  • Database.Cursor (CursorStep) over Batch Apex for new high-throughput jobs
  • Assert class (Assert.areEqual, Assert.isTrue, Assert.fail, etc.) over legacy System.assert, System.assertEquals, System.assertNotEquals in all test classes

Code Structure

  • Keep each class focused on a single responsibility
  • Extract private helpers for methods longer than ~40 lines
  • Prefer interfaces for cross-class contracts to keep coupling loose
  • Maintain consistent abstraction levels within a method — keep orchestration separate from low-level implementation

Async Decision Matrix

Scenario Default Key Traits
Standard async work Queueable Job ID, chaining, non-primitive types, configurable delay (up to 10 min via AsyncOptions), dedup signatures
Very large datasets Batch Apex Chunked processing, max 5 concurrent; use QueryLocator for large scopes
Modern batch alternative CursorStep (Database.Cursor) 2000-record chunks, higher throughput, no 5-job limit
Recurring schedule Scheduled Flow (preferred) or Schedulable Schedulable has 100-job limit; use only when chaining to Batch or needing complex Apex logic
Post-job cleanup Finalizer (System.Finalizer) Runs regardless of Queueable success/failure
Long-running callouts Continuation Up to 3 per transaction, 3 parallel
Legacy fire-and-forget @future Prefer Queueable for new development
Delays > 10 minutes System.scheduleBatch() Schedule a Batch job at a specific future time

Type-Specific Guidance

Service

  • with sharing; stateless; keep public APIs focused and static where reasonable
  • Delegate all SOQL to Selectors and SObject behavior to Domains
  • Wrap business errors in a custom exception (e.g., AccountServiceException)

Selector

  • inherited sharing (inherits from caller — allows reuse from both with and without sharing contexts)
  • One Selector per SObject or query domain
  • Return List<SObject> or Map<Id, SObject>; maintain a DRY base field list
  • Accept filter criteria as parameters; always include WITH USER_MODE

Domain

  • with sharing; encapsulate SObject field defaults, derivations, and validations
  • Operate only on in-memory lists; perform SOQL and DML in Services/Selectors
  • Designed to be invoked from services, triggers, and orchestration layers

Batch

  • with sharing; implement Database.Batchable<SObject> (add Database.Stateful when tracking results across chunks)
  • Keep start() focused on query definition; place business logic in execute()
  • Use QueryLocator for large datasets; handle partial failures via Database.SaveResult
  • Provide a meaningful finish() — at minimum logging; consider notifications
  • Accept filter parameters via constructor to make the batch reusable

Queueable

  • with sharing; accept data via constructor
  • Add chain-depth guards to prevent infinite chains
  • Optionally implement Finalizer for recovery/cleanup
  • Use AsyncOptions for configurable delay (up to 10 min) and dedup signatures

Schedulable

  • with sharing; keep execute() lightweight — delegate to a Queueable or Batch
  • Provide CRON expression constants; document schedule intent
  • Provide a convenience scheduleDaily() or similar static helper

DTO / Wrapper

  • No sharing keyword needed (pure data containers; SOQL and DML are handled elsewhere)
  • Prefer simple public properties; provide no-arg and parameterized constructors
  • Serialization-friendly (JSON.serialize/deserialize); implement Comparable when ordering matters

Utility

  • No sharing keyword needed (utility classes are pure functions; SOQL/DML belong in Services/Selectors)
  • All methods public static; provide a private constructor to prevent instantiation
  • Keep all methods pure and side-effect-free; perform SOQL and DML in dedicated layers

Interface

  • Define clear contracts with ApexDoc on each method signature

Abstract

  • with sharing; offer default behavior via virtual methods
  • Mark extension points protected virtual or protected abstract

Custom Exception

  • No sharing keyword needed
  • Extend Exception; keep simple with descriptive names
  • Apex exceptions support: new MyException(), new MyException('msg'), new MyException(cause), new MyException('msg', cause)

Trigger

  • One trigger per object; delegate all logic to handler or TAF action classes
  • Include all relevant DML contexts (before insert, after insert, before update, after update, before delete, after delete, after undelete)
  • If TAF is installed, the trigger body is a single line: new MetadataTriggerHandler().run();

Trigger Action (TAF)

  • One class per concern per context; implement TriggerAction.{Context} (e.g., TriggerAction.BeforeInsert)
  • Register via Trigger_Action__mdt custom metadata records (actions do nothing without registration)
  • Name: TA_{SObject}_{ActionName}
  • For recursion prevention, prefer field-value comparison over static boolean flags

Invocable Method (@InvocableMethod)

  • with sharing; use inner Request/Response classes with @InvocableVariable
  • Accept List<Request>, return List<Response>; bulkify — query and DML outside loops
  • Always include isSuccess (Boolean) and errorMessage (String) in Response
  • Return errors in Response rather than throwing exceptions (exceptions trigger Flow fault path)
  • Limit supported @InvocableVariable types to primitives, Id, SObject, and List<T> (types such as Map, Set, and Blob are not supported)

@AuraEnabled Controller

  • with sharing; use WITH USER_MODE in all SOQL
  • Use @AuraEnabled(cacheable=true) only for read-only queries; leave cacheable unset for DML operations
  • Catch exceptions and rethrow as AuraHandledException with user-friendly messages

Output Expectations

Deliverables per class:

  • {ClassName}.cls
  • {ClassName}.cls-meta.xml (default API version 62.0 unless specified)
  • {ClassName}Test.cls (generated via skills/generating-apex-test/SKILL.md)
  • {ClassName}Test.cls-meta.xml (generated via skills/generating-apex-test/SKILL.md)

Meta XML template:

<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>{API_VERSION}</apiVersion>
    <status>Active</status>
</ApexClass>

Report in this order:

Apex work: <summary>
Files: <paths>
Design: <pattern / framework choices>
Workflow: all mandatory steps completed (19); any N/A justified
Risks: <security, bulkification, async, dependency notes>
Analyzer: run_code_analyzer clean (sev0sev2 remediated)
Testing: Apex tests executed; failures fixed or delegated via skills/generating-apex-test
Deploy: <dry-run or next step>

Cross-Skill Integration

Need Delegate to Reason
Describe objects / fields first metadata skill Ensure alignment with the correct schema
Seed bulk or edge-case data data skill Create realistic datasets
Run Apex tests / fix failing tests Read and follow skills/generating-apex-test/SKILL.md Execute and iterate on failures
Deploy to org deploy skill Validation and deployment orchestration
Build Flow that calls Apex Flow skill Declarative orchestration via @InvocableMethod
Build LWC that calls Apex LWC skill UI/controller integration via @AuraEnabled

LSP Validation

This skill supports an LSP-assisted authoring loop for .cls and .trigger files:

  • Syntax issues detected immediately after write/edit
  • Auto-fix common syntax errors in a short loop (max 3 attempts)
  • Semantic quality validated via the code review checklist

Full guide: troubleshooting


Optional Enhancers

  • Run static analysis (Salesforce Code Analyzer / PMD) and fix issues before delivery

Reference Map

Core guides

Checklists & catalogs

Specialized patterns

Additional references

  • best practices — platform cache, static caching, guard clauses, comment guidelines
  • troubleshooting — LSP validation, deployment errors, debug logs, governor limit debugging