What it isAlways-on coding rules. GitHub Copilot applies them to files matching **/*.cls,**/*.trigger; the Claude Code version is a path-scoped rule generated from the same file. Why use instructions →
Install
This repository (recommended)
Adds .github/instructions/apex.instructions.md — commit it so everyone's Copilot follows the same rules.
Nothing to invoke — edit a matching file and Copilot picks the rules up. Adjust applyTo if your repository layout differs.
This repository (recommended)
Adds .claude/rules/apex.md — commit it. Claude loads it when it reads a matching file.
npx -y github:AGCO-Global/org-skills add instructions apex --tool claude
Just for me, every repository
Save it in your user profile instead.
npx -y github:AGCO-Global/org-skills add instructions apex --tool claude --user
Use it
Nothing to invoke. Adjust the paths globs if your repository layout differs.
Commands run in your repository folder and need Node.js 20+. They use your normal git sign-in to GitHub, so they work while the repository is private. Run npx -y github:AGCO-Global/org-skills list to see everything available.
Settings
name: "Apex"
description: "Always-on rules for writing Salesforce Apex classes, triggers, async jobs and test classes."
applyTo: "**/*.cls,**/*.trigger"
Instructions Copilot follows
Apex rules
Write every method as if it receives 200 records, runs as a user with restricted sharing, and shares its transaction with other automation.
Bulkification and limits
Never put SOQL or DML inside a loop. Collect Ids, run one query into a Map<Id, SObject>, work in memory, then do one DML. Loops of queries hit the 100 SOQL limit.
Build child groupings (Map<Id, List<Child__c>>) in one pass; avoid nested loops over two large lists. O(n²) burns CPU time.
Aggregate in SOQL (GROUP BY, SUM, COUNT) instead of summing in Apex loops.
In triggers, compare Trigger.new with Trigger.oldMap and process only records whose relevant fields changed.
Set same-record fields in before context; no extra DML and no recursion.
Design against the per-transaction limits: 100 SOQL, 150 DML, 10,000 DML rows, 50,000 query rows, 10 s CPU, 6 MB heap (sync).
Use Database.insert(records, false) only when partial success is acceptable, and inspect every SaveResult.
Triggers and layering
One trigger per object, containing only a call into its handler (new OrderTriggerHandler().run();). Multiple triggers have no guaranteed order.
Handlers route by context and delegate; no business logic in the trigger or handler body.
Guard recursion with a static Set<Id> of processed records, not a static boolean. Booleans skip records in later chunks.
Put all SOQL in selector classes (OrderSelector.selectByIds(Set<Id>)) and business rules in service classes. Services are reusable from triggers, controllers, Flow and batch.
Keep @AuraEnabled and @InvocableMethod methods thin: validate input, call a service, return a DTO.
Invocable methods take and return a List<Request>/List<Result>. Flow calls them in bulk.
Security
Declare with sharing on every class; inherited sharing for utilities; without sharing only in a small, dedicated class with a comment explaining why.
Use WITH USER_MODE in SOQL and AccessLevel.USER_MODE for DML in user-facing paths so the platform enforces CRUD/FLS. SYSTEM_MODE needs a written reason.
Use bind variables (:accountIds) or Database.queryWithBinds; never concatenate input into SOQL. Prevents SOQL injection.
Validate everything from LWC or API callers: types, ranges, and that the user may access the given Ids.
No hard-coded Ids, profile names, record type Ids, URLs or emails. Use Custom Metadata, Custom Labels and getRecordTypeInfosByDeveloperName(); Ids differ per org.
Keep secrets in Named Credentials or protected settings, never in code.
Async Apex
Queueable for post-commit work and callouts from triggers; Batch Apex for large volumes; Schedulable only to enqueue a Queueable or Batch.
Do not use @future in new code. It cannot chain or take complex types.
Make async jobs idempotent; they can run more than once. Chain rather than fan out to stay under queued-job limits.
Publish Platform Events for cross-system decoupling; subscribers must tolerate duplicate delivery.
Split setup-object DML (User, PermissionSet) into a Queueable to avoid MIXED_DML_OPERATION.
Errors and logging
Catch specific exceptions; never leave a catch empty.
Throw AuraHandledException with a user-safe message at the controller boundary; log the real error with its record Ids.
Log through the project's logger (persisted via a custom object or Platform Event), not System.debug. Logs must survive rollback.
Use Database.setSavepoint() and Database.rollback() around multi-step DML that must be atomic.
Tests
One @IsTest class per class, data from a TestDataFactory in @TestSetup; never SeeAllData=true.
Cover positive, bulk (200+ records), negative and System.runAs sharing cases.
Wrap the call in Test.startTest()/Test.stopTest() to reset limits and run async work.
Assert outcomes with messages (Assert.areEqual(expected, actual, 'status after approval')); a test without asserts proves nothing.
Mock callouts with HttpCalloutMock and services with interfaces or Test.createStub.
Treat 75% coverage as a floor; aim for 85% or more on services and selectors.
Go deeper: for larger tasks use the apex-development, salesforce-automation, salesforce-data-model and salesforce-integration skills, and backend-code-review before opening a pull request.
---
name: "Apex"
description: "Always-on rules for writing Salesforce Apex classes, triggers, async jobs and test classes."
applyTo: "**/*.cls,**/*.trigger"
---
# Apex rules
Write every method as if it receives 200 records, runs as a user with restricted sharing, and shares its transaction with other automation.
## Bulkification and limits
- Never put SOQL or DML inside a loop. Collect Ids, run one query into a `Map<Id, SObject>`, work in memory, then do one DML. Loops of queries hit the 100 SOQL limit.
- Build child groupings (`Map<Id, List<Child__c>>`) in one pass; avoid nested loops over two large lists. O(n²) burns CPU time.
- Aggregate in SOQL (`GROUP BY`, `SUM`, `COUNT`) instead of summing in Apex loops.
- In triggers, compare `Trigger.new` with `Trigger.oldMap` and process only records whose relevant fields changed.
- Set same-record fields in `before` context; no extra DML and no recursion.
- Design against the per-transaction limits: 100 SOQL, 150 DML, 10,000 DML rows, 50,000 query rows, 10 s CPU, 6 MB heap (sync).
- Use `Database.insert(records, false)` only when partial success is acceptable, and inspect every `SaveResult`.
## Triggers and layering
- One trigger per object, containing only a call into its handler (`new OrderTriggerHandler().run();`). Multiple triggers have no guaranteed order.
- Handlers route by context and delegate; no business logic in the trigger or handler body.
- Guard recursion with a static `Set<Id>` of processed records, not a static boolean. Booleans skip records in later chunks.
- Put all SOQL in selector classes (`OrderSelector.selectByIds(Set<Id>)`) and business rules in service classes. Services are reusable from triggers, controllers, Flow and batch.
- Keep `@AuraEnabled` and `@InvocableMethod` methods thin: validate input, call a service, return a DTO.
- Invocable methods take and return a `List<Request>`/`List<Result>`. Flow calls them in bulk.
## Security
- Declare `with sharing` on every class; `inherited sharing` for utilities; `without sharing` only in a small, dedicated class with a comment explaining why.
- Use `WITH USER_MODE` in SOQL and `AccessLevel.USER_MODE` for DML in user-facing paths so the platform enforces CRUD/FLS. `SYSTEM_MODE` needs a written reason.
- Use bind variables (`:accountIds`) or `Database.queryWithBinds`; never concatenate input into SOQL. Prevents SOQL injection.
- Validate everything from LWC or API callers: types, ranges, and that the user may access the given Ids.
- No hard-coded Ids, profile names, record type Ids, URLs or emails. Use Custom Metadata, Custom Labels and `getRecordTypeInfosByDeveloperName()`; Ids differ per org.
- Keep secrets in Named Credentials or protected settings, never in code.
## Async Apex
- Queueable for post-commit work and callouts from triggers; Batch Apex for large volumes; Schedulable only to enqueue a Queueable or Batch.
- Do not use `@future` in new code. It cannot chain or take complex types.
- Make async jobs idempotent; they can run more than once. Chain rather than fan out to stay under queued-job limits.
- Publish Platform Events for cross-system decoupling; subscribers must tolerate duplicate delivery.
- Split setup-object DML (User, PermissionSet) into a Queueable to avoid `MIXED_DML_OPERATION`.
## Errors and logging
- Catch specific exceptions; never leave a `catch` empty.
- Throw `AuraHandledException` with a user-safe message at the controller boundary; log the real error with its record Ids.
- Log through the project's logger (persisted via a custom object or Platform Event), not `System.debug`. Logs must survive rollback.
- Use `Database.setSavepoint()` and `Database.rollback()` around multi-step DML that must be atomic.
## Tests
- One `@IsTest` class per class, data from a `TestDataFactory` in `@TestSetup`; never `SeeAllData=true`.
- Cover positive, bulk (200+ records), negative and `System.runAs` sharing cases.
- Wrap the call in `Test.startTest()`/`Test.stopTest()` to reset limits and run async work.
- Assert outcomes with messages (`Assert.areEqual(expected, actual, 'status after approval')`); a test without asserts proves nothing.
- Mock callouts with `HttpCalloutMock` and services with interfaces or `Test.createStub`.
- Treat 75% coverage as a floor; aim for 85% or more on services and selectors.
Go deeper: for larger tasks use the apex-development, salesforce-automation, salesforce-data-model and salesforce-integration skills, and backend-code-review before opening a pull request.
---
paths:
- "**/*.cls"
- "**/*.trigger"
---
<!-- Apex — generated from instructions/salesforce/apex/apex.instructions.md for Claude Code. Edit that file, not this one. -->
# Apex rules
Write every method as if it receives 200 records, runs as a user with restricted sharing, and shares its transaction with other automation.
## Bulkification and limits
- Never put SOQL or DML inside a loop. Collect Ids, run one query into a `Map<Id, SObject>`, work in memory, then do one DML. Loops of queries hit the 100 SOQL limit.
- Build child groupings (`Map<Id, List<Child__c>>`) in one pass; avoid nested loops over two large lists. O(n²) burns CPU time.
- Aggregate in SOQL (`GROUP BY`, `SUM`, `COUNT`) instead of summing in Apex loops.
- In triggers, compare `Trigger.new` with `Trigger.oldMap` and process only records whose relevant fields changed.
- Set same-record fields in `before` context; no extra DML and no recursion.
- Design against the per-transaction limits: 100 SOQL, 150 DML, 10,000 DML rows, 50,000 query rows, 10 s CPU, 6 MB heap (sync).
- Use `Database.insert(records, false)` only when partial success is acceptable, and inspect every `SaveResult`.
## Triggers and layering
- One trigger per object, containing only a call into its handler (`new OrderTriggerHandler().run();`). Multiple triggers have no guaranteed order.
- Handlers route by context and delegate; no business logic in the trigger or handler body.
- Guard recursion with a static `Set<Id>` of processed records, not a static boolean. Booleans skip records in later chunks.
- Put all SOQL in selector classes (`OrderSelector.selectByIds(Set<Id>)`) and business rules in service classes. Services are reusable from triggers, controllers, Flow and batch.
- Keep `@AuraEnabled` and `@InvocableMethod` methods thin: validate input, call a service, return a DTO.
- Invocable methods take and return a `List<Request>`/`List<Result>`. Flow calls them in bulk.
## Security
- Declare `with sharing` on every class; `inherited sharing` for utilities; `without sharing` only in a small, dedicated class with a comment explaining why.
- Use `WITH USER_MODE` in SOQL and `AccessLevel.USER_MODE` for DML in user-facing paths so the platform enforces CRUD/FLS. `SYSTEM_MODE` needs a written reason.
- Use bind variables (`:accountIds`) or `Database.queryWithBinds`; never concatenate input into SOQL. Prevents SOQL injection.
- Validate everything from LWC or API callers: types, ranges, and that the user may access the given Ids.
- No hard-coded Ids, profile names, record type Ids, URLs or emails. Use Custom Metadata, Custom Labels and `getRecordTypeInfosByDeveloperName()`; Ids differ per org.
- Keep secrets in Named Credentials or protected settings, never in code.
## Async Apex
- Queueable for post-commit work and callouts from triggers; Batch Apex for large volumes; Schedulable only to enqueue a Queueable or Batch.
- Do not use `@future` in new code. It cannot chain or take complex types.
- Make async jobs idempotent; they can run more than once. Chain rather than fan out to stay under queued-job limits.
- Publish Platform Events for cross-system decoupling; subscribers must tolerate duplicate delivery.
- Split setup-object DML (User, PermissionSet) into a Queueable to avoid `MIXED_DML_OPERATION`.
## Errors and logging
- Catch specific exceptions; never leave a `catch` empty.
- Throw `AuraHandledException` with a user-safe message at the controller boundary; log the real error with its record Ids.
- Log through the project's logger (persisted via a custom object or Platform Event), not `System.debug`. Logs must survive rollback.
- Use `Database.setSavepoint()` and `Database.rollback()` around multi-step DML that must be atomic.
## Tests
- One `@IsTest` class per class, data from a `TestDataFactory` in `@TestSetup`; never `SeeAllData=true`.
- Cover positive, bulk (200+ records), negative and `System.runAs` sharing cases.
- Wrap the call in `Test.startTest()`/`Test.stopTest()` to reset limits and run async work.
- Assert outcomes with messages (`Assert.areEqual(expected, actual, 'status after approval')`); a test without asserts proves nothing.
- Mock callouts with `HttpCalloutMock` and services with interfaces or `Test.createStub`.
- Treat 75% coverage as a floor; aim for 85% or more on services and selectors.
Go deeper: for larger tasks use the apex-development, salesforce-automation, salesforce-data-model and salesforce-integration skills, and backend-code-review before opening a pull request.