Skip to main content

Anti-patterns report

The output of vulkro-sf antipatterns. Reports findings from the Salesforce Well-Architected anti-pattern catalog (AP-001 to AP-014). This is a pattern-quality report, not a security report: the detectors flag governor-limit risk, recursive triggers, and maintainability issues, not credentials or injection sinks.

Available formats: text (default, colourized terminal output), html (self-contained file for hand-off), json (machine-readable for dashboards), sarif (for code-scanning platforms), and junit (for CI test-report ingestion).

Per-rule reporting

Each AP-NNN rule contributes its own section to the report. Within a section, findings are grouped by file and sorted by line. Every finding carries the rule ID, the file path, the start and end line range, a one-line description of what was detected, and a remediation hint.

RuleWhat gets reported when the rule fires
AP-001 SOQL inside a loopThe SELECT statement's location, the enclosing for / while shape, and the suggestion to hoist the query outside the loop.
AP-002 DML inside a loopThe insert / update / delete / upsert location, the enclosing loop, and the suggestion to collect into a list and DML once after the loop.
AP-003 Hard-coded record IDThe 15- or 18-character ID literal, the file and line, and the suggestion to move the ID to Custom Metadata or a Custom Setting.
AP-004 Trigger without bulk-safetyThe handler method, the offending Trigger.new[0] access, and the suggestion to iterate the full collection.
AP-005 Recursive triggerThe trigger that performs DML on the same sObject without a static recursion guard, with the suggested guard pattern.
AP-006 Empty catch blockThe try { ... } catch (...) { } location and a reminder that swallowing every exception masks real failures.
AP-007 System.debug in production codeThe System.debug call site that is not guarded by Test.isRunningTest().
AP-008 Schema introspection in a loopThe Schema.getGlobalDescribe() or getDescribe() call inside a loop body and the suggestion to cache the result.
AP-009 Async chain without governor budgetingThe executeBatch or enqueueJob call inside a batch class without a chain-depth guard.
AP-010 Missing sharing keywordThe Apex class declaration that does DML and declares neither with sharing nor inherited sharing nor without sharing.
AP-011 Hard-coded URL in ApexThe literal https:// URL in an Apex string and the suggestion to use a Named Credential or Custom Metadata Type.
AP-012 Stateless Visualforce controller pattern misuseThe controller that holds large in-memory collections across requests, with a suggestion to use stateless view-state minimisation.
AP-013 Aura method exposes system contextThe aura:method controller method that does sharing-bypass work without enforcement comments.
AP-014 Flow with no fault pathThe Flow node that calls a subflow or Apex action without a fault connector.

Sample output (text)

vulkro-sf antipatterns 0.5.2
project: force-app
scanned: 124 files in 0.8s

AP-001 SOQL inside a loop 2 findings
force-app/main/default/classes/LeadBatch.cls:42:9
Query [SELECT Id, Name FROM Lead WHERE Status = :s] is inside a for-loop body.
Hoist the query outside the loop and bind the per-iteration value with a list.

force-app/main/default/classes/AccountUpdater.cls:117:13
Query [SELECT Id FROM Account WHERE ParentId = :pid] is inside a while-loop body.
Hoist the query outside the loop and bind the per-iteration value with a list.

AP-003 Hard-coded record ID 1 finding
force-app/main/default/classes/RouteRules.cls:23:36
Hard-coded ID "0051Q00000ABC123" found in source.
Move the ID to a Custom Metadata Type or a Custom Setting so the value differs
per org without a code change.

AP-010 Missing sharing keyword 1 finding
force-app/main/default/classes/QuoteService.cls:1:1
Class QuoteService performs DML but does not declare a sharing keyword.
Add 'with sharing' (or 'inherited sharing' for re-entrant utility classes).

Summary: 4 findings across 3 rules, 4 files.
Exit 1.

Sample output (json)

{
"tool": {"name": "vulkro-sf antipatterns", "version": "0.5.2"},
"project": "force-app",
"scanned_at": "2026-05-30T14:33:10Z",
"findings": [
{
"rule_id": "AP-001",
"rule_title": "SOQL inside a loop",
"severity": "medium",
"confidence": "high",
"file": "force-app/main/default/classes/LeadBatch.cls",
"start_line": 42,
"end_line": 42,
"start_column": 9,
"end_column": 64,
"message": "Query [SELECT Id, Name FROM Lead WHERE Status = :s] is inside a for-loop body.",
"remediation": "Hoist the query outside the loop and bind the per-iteration value with a list."
}
],
"summary": {
"total": 4,
"rules_with_findings": 3,
"files_with_findings": 4
}
}

HTML output

The HTML variant is a self-contained file (inlined CSS, no CDN dependencies) with the same per-rule grouping, plus a top-of-page summary strip and a click-to-expand snippet showing the offending source line in context. Use it for hand-off to a non-developer reviewer or to attach to an engagement-close deliverable.

vulkro-sf antipatterns . --format html -o antipatterns.html