Verified 2026-09 against https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm.
Work the areas in order. Report only what you find.
1. Apex and governor limits
- No SOQL, DML, callout,
Database.method orMessaging.sendEmailinside a loop. - Queries are selective — a filter on an indexed field, and no full-table scan on a large object.
- Collections are used for lookups: build a
Map<Id, SObject>once, rather than querying per row. - No
Trigger.new[0], and no method signature that forces the caller into a loop. - Heap: no unbounded collection accumulating across a batch; query only the fields needed.
- CPU: no nested loop over two large collections where a map would do.
- Callouts are not mixed with DML in the same transaction without the correct async pattern.
- Async choice is right: Queueable for chaining, Batch for volume,
@futureonly for legacy. - No hard-coded ids — queues, profiles and users are looked up; record type Ids come from
Utils.getRecordTypeId(...)in astatic final, not an inline describe call. - No hard-coded org-specific URLs or usernames.
2. Access and injection
- Every class declares
with sharing(orwithout sharingwith a reason) — no class omits the keyword. - Queries returning data to users use
WITH USER_MODE. - DML on user-supplied data uses
AccessLevel.USER_MODE. -
WITH SECURITY_ENFORCEDis replaced, not supplemented, where found. - Dynamic SOQL uses
Database.queryWithBindswith bind variables — no concatenation. -
without sharingappears only with a comment justifying the elevation. - System-mode results returned to a user pass through
Security.stripInaccessible. -
@AuraEnabledand Apex REST methods validate every input and do not trust an id parameter. - Nothing guest-reachable grants more than intended.
3. Triggers and automation
- One trigger per object, delegating to a handler — no logic in the trigger body.
- Recursion guard where the trigger can cause its own re-entry.
- The object's full automation inventory was reviewed: triggers, record-triggered Flows, remaining Process Builders, workflow rules, validation rules.
- Before-save Flow and Apex trigger do not both write the same field.
- No reliance on ordering between record-triggered Flows — it is not guaranteed.
- Field updates that re-trigger automation are intentional and bounded.
4. Flows
- No Get, Create, Update or Delete element inside a loop — collect into a collection variable and act once after the loop.
- A fault connector on every element that can fail, going somewhere a human will see.
- Entry criteria are tight enough that the Flow does not run on every save.
- Before-save used for same-record field updates (no extra DML) rather than after-save.
- Hard-coded ids absent; use custom metadata or a lookup.
- Subflows do not hide a loop-with-DML one level down.
5. Lightning Web Components
- Apex called imperatively has error handling and a user-visible failure state.
-
@wireerrors are handled, not only the data branch. - No DOM access that Lightning Web Security restricts; no reaching outside the component.
- Third-party libraries load from a static resource, never a CDN, with a Trusted URL configured.
-
loadScript/loadStyleare guarded so they run once. - SLDS 2 styling hooks are used rather than overriding internal SLDS classes, which break on upgrade.
- Accessibility on primary flows: labels, roles, focus management, keyboard operability.
- No sensitive data placed in component state that is visible in the DOM.
6. Tests
- Assertions exist and are meaningful — not
Assert.isTrue(true)or a bareSystem.assert. - A bulk case at 200 records, exercising the real entry point.
- A negative case: invalid input, missing permission, validation failure.
- A restricted-user case with
System.runAs— an admin-only test proves nothing about access. -
@TestSetupfor shared data; noSeeAllData=true. - Test data is created by a factory, not copy-pasted per test.
- Async work is asserted after
Test.stopTest(). - Callouts are mocked with
HttpCalloutMock; the mock asserts the request, not only the response.
7. Permissions, metadata and packaging
- Permission set diffs read line by line — especially "View All", "Modify All", "Author Apex", "Manage Users" and field-level security changes.
- Profile changes justified; prefer permission sets for new grants.
- No secret in custom metadata, custom settings or a static resource.
Credential__cis acceptable when the field is encrypted and FLS is restricted to the integration user. -
package.xmlcontains nothing unintended; destructive changes are deliberate and reviewed. - Named Credentials used for callout authentication, or
Credential__cwhere the callout cannot use one. - New objects and fields have descriptions and sensible API names.
8. What the tooling should have caught
Run Code Analyzer v5 before reading, and treat its output as input to the review rather than the review itself:
sf code-analyzer run --workspace force-app --view detail
The Graph Engine finds CRUD/FLS violations across call paths; PMD finds the per-file patterns; RetireJS flags vulnerable JavaScript libraries in static resources; the Flow engine scans Flows. If a finding is a false positive, say so in the review with the reason rather than silently ignoring it.