Stack conventions
Salesforce (Apex, LWC, metadata)
Apex classes and triggers, Lightning Web Components and source-format metadata in a Salesforce DX project.
Apex classes and triggers, Lightning Web Components and source-format metadata in a Salesforce DX project.
Used for: salesforce, salesforce-prod.
Principles
- Bulkify everything; every trigger, method and query must handle 200 records as well as one.
- Never put SOQL, SOSL or DML inside a loop; collect, query once and write once.
- Enforce sharing and field-level security by default with "with sharing", WITH USER_MODE and AccessLevel.USER_MODE.
- Keep one trigger per object with no logic in it; delegate to a handler class.
- Prefer declarative configuration and Custom Metadata over hard-coded values and IDs.
- Write meaningful tests that create their own data and assert outcomes, including bulk and negative cases.
- Keep business logic in Apex service classes, not in controllers, triggers or components.
- In Lightning Web Components, use base components and Lightning Data Service before custom Apex.
- Design within the per-transaction governor limits (synchronous - 100 SOQL queries, 150 DML statements, 10,000 DML rows, 10 seconds CPU, 6 MB heap) and check Limits methods where volume is unbounded.
- In LWC, pass data down with @api properties and up with CustomEvent; never mutate an @api property inside the child.
Conventions
Accessibility
- Use Lightning base components and SLDS so components get keyboard support and accessible markup.
- Give every input a label and every icon-only button an alternative-text or title.
- Do not rely on colour alone to convey state; follow SLDS accessibility guidance.
Concurrency
- Guard triggers against recursion with a static set of processed IDs rather than a boolean flag.
- Use FOR UPDATE when concurrent updates to the same record must be serialised.
- Design Queueable and Batch Apex to be safe when retried or run in parallel.
Dependencies
- Declare package dependencies in sfdx-project.json and keep API versions consistent across new metadata.
- Use npm only for LWC tooling (Jest, ESLint, Prettier) and keep the lock file committed.
Documentation
- Document public Apex classes and methods with ApexDoc comments describing behaviour, parameters and exceptions.
- Explain any without sharing or system mode use in a comment next to it.
Errors
- Catch only exceptions the code can handle; never swallow exceptions in empty catch blocks.
- Use Database methods with allOrNone=false only when partial success is intended, and inspect every SaveResult.
- Throw custom exceptions that extend Exception with a clear message for callers.
- Use addError on records in triggers to report validation failures to users.
Logging
- Remove debugging System.debug statements from production code, or route logging through the repository's logging framework.
- Never log record data containing personal information, credentials or session IDs.
Naming
- Name classes in PascalCase with a role suffix (AccountService, AccountSelector, AccountTriggerHandler, AccountServiceTest).
- Name triggers
- Use camelCase for methods and variables, and UPPER_SNAKE_CASE for constants.
- Name LWC folders in camelCase; the component is used in markup in kebab-case.
Performance
- Query only the fields and rows needed, with selective WHERE clauses on indexed fields.
- Use maps keyed by ID to relate records instead of nested loops.
- Keep an eye on governor limits (queries, DML rows, CPU time, heap) in loops and recursion.
- Cache wire adapters and use cacheable=true on read-only Apex methods called from LWC.
Security
- Declare "with sharing" on classes unless a documented reason requires otherwise.
- Run SOQL and DML in user mode (WITH USER_MODE, AccessLevel.USER_MODE) so object and field permissions apply.
- Prefer WITH USER_MODE over the older WITH SECURITY_ENFORCED; use Security.stripInaccessible when inaccessible fields must be removed rather than fail the operation.
- Use bind variables in SOQL; never concatenate user input into dynamic queries, and escape it if dynamic SOQL is unavoidable.
- Never hard-code record IDs, credentials or endpoints; use Named Credentials for callouts.
- In LWC, never use innerHTML with untrusted data or bypass Lightning Locker or Lightning Web Security.
Structure
- Keep source in the Salesforce DX project layout under the package directories in sfdx-project.json.
- Give every Apex class and LWC a meta.xml with the repository's API version.
- Keep triggers logic-free and put shared logic in service and selector classes.
- Grant access with permission sets (and permission set groups), not profiles; do not author destructiveChanges, profiles or flows unless the project enables them.
Testing
- Create test data in the test (or a test data factory); never use SeeAllData=true.
- Test with bulk data (at least 200 records) and with negative cases.
- Wrap the code under test in Test.startTest and Test.stopTest and assert outcomes with Assert class methods.
- Run tests as a restricted user with System.runAs when sharing or permissions matter.
- Mock callouts with HttpCalloutMock and keep code coverage at or above the repository's bar.
- Name test classes
<Class>Testand methods<method>_<condition>_<expectedResult>; one behaviour per method. - Unit-test Apex services with selectors and callouts mocked (Stub API, HttpCalloutMock); integration-style tests exercise the trigger, flow or LWC wire path end to end with real DML in the test.
- Every class and trigger the change touches stays at or above 75% coverage (the org deployment rule) and new code aims for 90%; coverage without assertions does not count.
Review checklist
- Is every trigger, method and query bulk-safe for 200 records?
- Is there no SOQL, SOSL or DML inside a loop?
- Do classes declare with sharing, and do queries and DML run in user mode?
- Are dynamic SOQL inputs bound or escaped?
- Is the trigger free of logic and delegating to a handler or service?
- Are record IDs, credentials and endpoints kept out of code?
- Do tests create their own data, avoid SeeAllData, and include bulk and negative cases?
- Do tests assert outcomes with Assert methods inside Test.startTest and Test.stopTest?
- Are exceptions handled specifically, with no empty catch blocks?
- Does each new class and component have a meta.xml with the repository's API version?
- Do Lightning Web Components use base components, labels and SLDS for accessibility?
- Are configuration values in Custom Metadata rather than hard-coded?
Quality checks (run when your repository has the tool configured)
| Check | Kind | Tool | Blocks the pull request |
|---|---|---|---|
code-analyzer |
static | sf code-analyzer | no |
prettier-apex |
format | prettier | no |
eslint-lwc |
lint | eslint | no |
lwc-jest |
test | sfdx-lwc-jest | no |
pmd-apex |
static | pmd | no |
npm-audit |
security | npm audit | no |
gitleaks |
security | gitleaks | no |