All skills
github avatar

/salesforce-apex-quality

@90921cc official
by githubgithub/awesome-copilot40k stars
5,040

Apex code quality guardrails for Salesforce development. Enforces bulk-safety rules (no SOQL/DML in loops), sharing model requirements, CRUD/FLS security, SOQL injection prevention, PNB test coverage (Positive / Negative / Bulk), and modern Apex idioms. Use this skill when reviewing or generating Apex classes, trigger handlers, batch jobs, or test classes to catch governor limit risks, security gaps, and quality issues before deployment.

Use this Skill: https://skilld.dev/gh/github/awesome-copilot/salesforce-apex-quality

This session only. Nothing lands on disk.

SKILL.md

β‰ˆ117 tokens always: the name and description. β‰ˆ1.7k when used: this file.

Salesforce Apex Quality Guardrails

Apply these checks to every Apex class, trigger, and test file you write or review.

Step 1 β€” Governor Limit Safety Check

Scan for these patterns before declaring any Apex file acceptable:

SOQL and DML in Loops β€” Automatic Fail

// ❌ NEVER β€” causes LimitException at scale
for (Account a : accounts) {
    List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :a.Id]; // SOQL in loop
    update a; // DML in loop
}

// βœ… ALWAYS β€” collect, then query/update once
Set<Id> accountIds = new Map<Id, Account>(accounts).keySet();
Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();
for (Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]) {
    if (!contactsByAccount.containsKey(c.AccountId)) {
        contactsByAccount.put(c.AccountId, new List<Contact>());
    }
    contactsByAccount.get(c.AccountId).add(c);
}
update accounts; // DML once, outside the loop

Rule: if you see [SELECT or Database.query, insert, update, delete, upsert, merge inside a for loop body β€” stop and refactor before proceeding.

Step 2 β€” Sharing Model Verification

Every class must declare its sharing intent explicitly. Undeclared sharing inherits from the caller β€” unpredictable behaviour.

Declaration When to use
public with sharing class Foo Default for all service, handler, selector, and controller classes
public without sharing class Foo Only when the class must run elevated (e.g. system-level logging, trigger bypass). Requires a code comment explaining why.
public inherited sharing class Foo Framework entry points that should respect the caller's sharing context

If a class does not have one of these three declarations, add it before writing anything else.

Step 3 β€” CRUD / FLS Enforcement

Apex code that reads or writes records on behalf of a user must verify object and field access. The platform does not enforce FLS or CRUD automatically in Apex.

// Check before querying a field
if (!Schema.sObjectType.Contact.fields.Email.isAccessible()) {
    throw new System.NoAccessException();
}

// Or use WITH USER_MODE in SOQL (API 56.0+)
List<Contact> contacts = [SELECT Id, Email FROM Contact WHERE AccountId = :accId WITH USER_MODE];

// Or use Database.query with AccessLevel
List<Contact> contacts = Database.query('SELECT Id, Email FROM Contact', AccessLevel.USER_MODE);

Rule: any Apex method callable from a UI component, REST endpoint, or @InvocableMethod must enforce CRUD/FLS. Internal service methods called only from trusted contexts may use with sharing instead.

Step 4 β€” SOQL Injection Prevention

// ❌ NEVER β€” concatenates user input into SOQL string
String soql = 'SELECT Id FROM Account WHERE Name = \'' + userInput + '\'';

// βœ… ALWAYS β€” bind variable
String soql = [SELECT Id FROM Account WHERE Name = :userInput];

// βœ… For dynamic SOQL with user-controlled field names β€” validate against a whitelist
Set<String> allowedFields = new Set<String>{'Name', 'Industry', 'AnnualRevenue'};
if (!allowedFields.contains(userInput)) {
    throw new IllegalArgumentException('Field not permitted: ' + userInput);
}

Step 5 β€” Modern Apex Idioms

Prefer current language features (API 62.0 / Winter '25+):

Old pattern Modern replacement
if (obj != null) { x = obj.Field__c; } x = obj?.Field__c;
x = (y != null) ? y : defaultVal; x = y ?? defaultVal;
System.assertEquals(expected, actual) Assert.areEqual(expected, actual)
System.assert(condition) Assert.isTrue(condition)
[SELECT ... WHERE ...] with no sharing context [SELECT ... WHERE ... WITH USER_MODE]

Step 6 β€” PNB Test Coverage Checklist

Every feature must be tested across all three paths. Missing any one of these is a quality failure:

Positive Path

  • Expected input β†’ expected output.
  • Assert the exact field values, record counts, or return values β€” not just that no exception was thrown.

Negative Path

  • Invalid input, null values, empty collections, and error conditions.
  • Assert that exceptions are thrown with the correct type and message.
  • Assert that no records were mutated when the operation should have failed cleanly.

Bulk Path

  • Insert/update/delete 200–251 records in a single test transaction.
  • Assert that all records processed correctly β€” no partial failures from governor limits.
  • Use Test.startTest() / Test.stopTest() to isolate governor limit counters for async work.

Test Class Rules

@isTest(SeeAllData=false)   // Required β€” no exceptions without a documented reason
private class AccountServiceTest {

    @TestSetup
    static void makeData() {
        // Create all test data here β€” use a factory if one exists in the project
    }

    @isTest
    static void givenValidInput_whenProcessAccounts_thenFieldsUpdated() {
        // Positive path
        List<Account> accounts = [SELECT Id FROM Account LIMIT 10];
        Test.startTest();
        AccountService.processAccounts(accounts);
        Test.stopTest();
        // Assert meaningful outcomes β€” not just no exception
        List<Account> updated = [SELECT Status__c FROM Account WHERE Id IN :accounts];
        Assert.areEqual('Processed', updated[0].Status__c, 'Status should be Processed');
    }
}

Step 7 β€” Trigger Architecture Checklist

  • One trigger per object. If a second trigger exists, consolidate into the handler.
  • Trigger body contains only: context checks, handler invocation, and routing logic.
  • No business logic, SOQL, or DML directly in the trigger body.
  • If a trigger framework (Trigger Actions Framework, ff-apex-common, custom base class) is already in use β€” extend it. Do not create a parallel pattern.
  • Handler class is with sharing unless the trigger requires elevated access.

Quick Reference β€” Hardcoded Anti-Patterns Summary

Pattern Action
SOQL inside for loop Refactor: query before the loop, operate on collections
DML inside for loop Refactor: collect mutations, DML once after the loop
Class missing sharing declaration Add with sharing (or document why without sharing)
escape="false" on user data (VF) Remove β€” auto-escaping enforces XSS prevention
Empty catch block Add logging and appropriate re-throw or error handling
String-concatenated SOQL with user input Replace with bind variable or whitelist validation
Test with no assertion Add a meaningful Assert.* call
System.assert / System.assertEquals style Upgrade to Assert.isTrue / Assert.areEqual
Hardcoded record ID ('001...') Replace with queried or inserted test record ID

Source: SKILL.md on GitHub

No alerts14d4 checks Β· Risk SAFE
  • Gen Agent Trust Hub14d

    This skill provides a set of security and quality guardrails for Salesforce Apex development. It focuses on enforcing platform security best practices, such as preventing SOQL injection, ensuring proper CRUD/FLS enforcement, and managing data isolation through sharing models.

  • Socket14d

    No alerts

  • Snyk14d

    Risk: LOW Β· No issues

  • ZeroLeaks5mo

    Score: 93/100 Β· 2 sections analyzed

Signed by skilld at 90921cc. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 18 hours ago.

Activeupdated 5 months ago

README badge

README badge for github/awesome-copilot/salesforce-apex-quality

Enforces Salesforce Apex best practices including bulk-safety (no SOQL/DML in loops), sharing model declarations, CRUD/FLS security checks, SOQL injection prevention, and PNB test coverage requirements. Apply this skill when writing or reviewing Apex classes, triggers, batch jobs, and test classes to catch governor limit violations and security gaps before deployment.

Generated from the current SKILL.md.

Does this skill enforce CRUD/FLS checks automatically?
No. The skill provides guardrails and patterns you must apply manually. Apex does not enforce FLS or CRUD automatically, so the skill teaches you to check object and field access with Schema methods or WITH USER_MODE syntax before querying or updating records.
What Apex API version does this assume?
The skill targets API 62.0 (Winter '25) and later for modern idioms like safe navigation (?.) and null coalescing (??), and API 56.0+ for WITH USER_MODE in SOQL.
Does this cover trigger frameworks?
Yes. The skill enforces one trigger per object and requires all business logic to live in handler classes, not the trigger body. It notes that if your project already uses a trigger framework (Trigger Actions Framework, ff-apex-common, etc.), you must extend it rather than create a parallel pattern.
What test coverage does this require?
The skill mandates Positive (happy path), Negative (errors and edge cases), and Bulk (200–251 records in one transaction) test paths for every feature. All three must include meaningful assertions, not just absence of exceptions.
Does this prevent SOQL injection?
Yes. The skill forbids string concatenation with user input in SOQL and requires bind variables. For dynamic field names, it requires validation against a whitelist before inclusion in the query.

Generated from the current SKILL.md. These answers refresh after source changes.