Skip to content

Fast Start - Code Scanning

Framework: KernDX | Total time: ~20 minutes

> PMD rulesets and ESLint rules that catch framework violations in your IDE before they reach code review.

Before you start:

  • [ ] KernDX package installed in your org
  • [ ] Org configured post-install — verify with the Kern app's Health Check (see Installation guide)
  • [ ] VS Code with Apex PMD extension installed -- or IntelliJ with Illuminated Cloud
  • [ ] SF Code Analyzer v5 installed (sf plugins install @salesforce/plugin-code-analyzer)
  • [ ] Working in a sandbox or scratch org (not production)

What you'll build: A fully configured scanning pipeline that catches inline SOQL, direct DML, System.debug(), and 21 other framework violations -- inline in your IDE and in CI/CD.

Success looks like: You write a class with System.debug() and [SELECT Id FROM Account], and your IDE underlines both lines before you even save.

In one line: Drop scanner/kerndx-pmd-ruleset.xml into your Apex PMD settings and every framework anti-pattern lights up in your editor.


Table of Contents

Expand
  1. Tier 1: See It Work (~2 minutes)
  2. Tier 2: Set Up Your Project (~15 minutes)
  3. Tier 3: Production Patterns (~5-10 minutes)
  4. Common Issues
  5. What You Now Know
  6. Next Steps

Tier 1: See It Work (~2 minutes)

The sample violation class

The FastStart_Scanner_DEMO.cls class ships with the KernDX subscriber test artifacts. It is a deliberately violating Apex class -- not a test class, and not meant to be deployed to production. It exists solely so you can point the scanner at something and see real violations without writing any code.

The class has two violations baked in:

LineViolationRule
Method bodyInline SOQL [SELECT Id FROM Account]KernNoInlineSOQL
Method bodySystem.debug(accounts)KernNoSystemDebug

Run the scanner

Run SF Code Analyzer against the demo class using the KernDX ruleset:

bash
sf code-analyzer run --rule-selector pmd:KernDXFrameworkCompliance --target release-testing/subscriber/classes/FastStart_Scanner_DEMO.cls --config-file code-analyzer.yml --view detail

Expected output (violations including at minimum):

text
KernNoInlineSOQL     High       Inline SOQL is not allowed. Use a selector (SEL_*) or QRY_Builder.
KernNoSystemDebug    Moderate   System.debug() is not allowed. Use LOG_Builder for structured, async logging.

The scanner found the violations. The inline [SELECT ...] should use a selector or QRY_Builder, and System.debug() should use LOG_Builder. Severity levels map from PMD priorities: Priority 1 → High, Priority 3 → Moderate, Priority 5 → Low.

> When to move to Tier 2: When you want violations to appear inline in your IDE as you type, without running the CLI manually.


Tier 2: Set Up Your Project (~15 minutes)

Step 1: IDE Setup (VS Code)

Add the KernDX ruleset to your .vscode/settings.json:

json
{
   "apexPMD.rulesets": [
      "scanner/kerndx-pmd-ruleset.xml"
   ]
}

Save the file. Open any Apex class that contains System.debug() or [SELECT ...] -- you'll see yellow/red squiggly underlines on the offending lines. Hover for the rule name and suggested replacement.

> Multiple rulesets: The Apex PMD extension accepts an array. Add org-specific naming rules alongside the framework rules: > ```json

"apexPMD.rulesets": [ "scanner/kerndx-pmd-ruleset.xml", "scanner/subscriber-naming-pmd-ruleset.xml" ]

Step 2: SF Code Analyzer v5

For CLI-based scanning (CI/CD, pre-commit hooks, batch analysis), configure code-analyzer.yml in your project root:

yaml
engines:
  pmd:
    custom_rulesets:
      - scanner/kerndx-pmd-ruleset.xml

Run the scanner:

bash
sf code-analyzer run --target force-app/ --view detail

This scans all Apex classes and triggers in force-app/ and displays violations grouped by file. Add --view table for a concise tabular format, or add --output-file results.csv for CI reporting.

Step 3: IntelliJ / Illuminated Cloud

IntelliJ with Illuminated Cloud only accepts a single PMD ruleset path. Use the combined ruleset that includes all KernDX rules by reference:

  1. Open Settings > Illuminated Cloud > PMD
  2. Set Custom Ruleset Path to scanner/combined-pmd-ruleset.xml
  3. Apply

Violations now appear inline in the IntelliJ editor. The combined file uses PMD <rule ref="..."/> -- no rules are duplicated.

Step 4: ESLint for LWC

The ESLint plugin is already configured in force-app/main/default/lwc/eslint.config.mjs. Run it with:

bash
npm run lint

This checks all LWC JavaScript files for six rules: ComponentBuilder usage, console.log blocking, component folder naming, assertion-less Jest tests, shared-fixture mutation, and unjustified coverage-exempt comments. See the ESLint rule reference below for the full list.

Verify it works -- create a test LWC with extends LightningElement instead of extends ComponentBuilder(...):

javascript
import {LightningElement} from 'lwc';

export default class TestViolation extends LightningElement {}

Run npm run lint -- you'll see:

text
error  LWC components must extend ComponentBuilder instead of LightningElement  kerndx/use-component-builder

Tier 3: Production Patterns (~5-10 minutes)

Priority tiers

The 24 KernDX PMD rules are organized into three priority tiers for phased adoption:

TierPriorityCountApproach
Blockers12Fix immediately -- these bypass core framework patterns
Should Fix312Fix in current sprint -- makes code harder to maintain if left
Informational510Fix opportunistically -- best practice, no immediate action required

Recommended adoption path: Start with Priority 1 (triggers and SOQL). Once clean, enable Priority 3. Add Priority 5 when the team is comfortable with the framework.

PMD rule reference

All 24 KernDX PMD rules:

RuleWhat It BlocksUse InsteadPriority
KernTriggerMustDelegateLogic in trigger bodynew TRG_Dispatcher().run()1
KernNoInlineSOQL[SELECT ...], Database.query()Selector or QRY_Builder1
KernNoDirectDMLinsert/update/delete/upsert, Database.* DMLDML_Builder3
KernNoSystemDebugSystem.debug()LOG_Builder3
KernNoRawHttpnew HttpRequest(), new Http()UTIL_HttpClient3
KernUseSchedulerBaseimplements Schedulable directlyextends SCHED_Base3
KernNoRawScheduleSystem.schedule()SCHED_Base + ScheduledJob__c3
KernNoRawEventPublishEventBus.publish()LOG_Builder / framework events3
KernNoRawHttpMockimplements HttpCalloutMock/WebServiceMockAPI_MockFactory3
KernNoRawRestContextRestContext.request/responseAPI_Inbound framework3
KernNoRawEmailMessaging.sendEmail(), new SingleEmailMessage()UTIL_Email3
KernRestResourceNaming@RestResource on non-REST_* classREST_* + API_Dispatcher3
KernNoInlineDmlInTestsinsert/update/delete inside @IsTest methodsTST_Builder / DML_Builder3
KernNoBooleanExceptionThrownBoolean exceptionThrown = false; try { ... } catch (...) { exceptionThrown = true; }Assert.fail(...) + Assert.isInstanceOfType(...)3
KernNoLegacyAssertSystem.assert*()Assert.*5
KernUseTestBuildernew Account(Name = ...) in testsTST_Builder5
KernNoRawCacheCache.Org.*, Cache.Session.*UTIL_Cache5
KernNoRawDescribeSchema.getGlobalDescribe()UTIL_SObjectDescribe5
KernNoRawTypeForNameType.forName()UTIL_System.getTypeForClassName()5
KernNoRawEnqueueJobSystem.enqueueJob()UTIL_AsynchronousJobLauncher5
KernNoRawCryptoCrypto.*UTIL_Crypto5
KernNoRawFeatureManagementFeatureManagement.checkPermission()UTIL_FeatureFlag.isEnabled()5
KernNoCoverageTheatreTest methods with no assertionsReal Assert.* calls that exercise the behaviour5
KernCoverageExemptRequiresReason@CoverageExempt without a justification commentAdd an inline // Coverage-exempt because … explanation5

ESLint rule reference

RuleWhat It BlocksUse Instead
kerndx/use-component-builderextends LightningElementextends ComponentBuilder(...)
kerndx/no-console-logconsole.log(), window.console.*this.consoleLog(), this.consoleError()
kerndx/enforce-component-namingLWC folders without domain prefixdomain[Brand]FeatureVariant
kerndx/no-jest-theatreAssertion-less Jest tests, hollow createElement assertionsReal expect(...) calls that exercise behaviour
kerndx/no-mutating-shared-fixtureMutation of a beforeAll(...) fixture across testsRebuild per-test state in beforeEach(...)
kerndx/no-coverage-exempt-without-reasonkern-coverage-exempt comments without justificationAdd a substantive platform-limitation reason

Suppressing rules

When a rule produces a false positive or you have a justified exception, suppress it with an annotation and a comment explaining why.

Apex (PMD):

apex
@SuppressWarnings('PMD.KernNoDirectDML')
public inherited sharing class DML_SharingProxy
{
   // Framework infrastructure — direct DML is intentional here
}

Multiple rules on one class:

apex
@SuppressWarnings('PMD.KernNoDirectDML, PMD.KernNoRawHttp')

JavaScript (ESLint):

javascript
// eslint-disable-next-line kerndx/use-component-builder -- template-only component with no controller interaction
export default class Notice extends LightningElement {}

CI/CD integration

The PMD rulesets are standard XML files that work with any tool that supports custom PMD rulesets.

ToolSetup
GearsetStatic Code Analysis settings > upload kerndx-pmd-ruleset.xml as a custom ruleset
CopadoPMD SCA Settings > add as custom PMD ruleset > set enforcement level
AutoRABITStatic Code Analysis > Apex PMD > upload ruleset > assign to analysis profile
CodeScanQuality Profile > add as custom ruleset > activate KernDX rules > set severity thresholds

GitHub Actions example

yaml
name: Code Scan
on: [pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - name: Install SF CLI
        run: npm install -g @salesforce/cli
      - name: Install Code Analyzer
        run: sf plugins install @salesforce/plugin-code-analyzer
      - name: Run KernDX Scanner
        run: sf code-analyzer run --target force-app/ --view detail

Org-specific naming rules

The scanner/subscriber-naming-pmd-ruleset.xml file is an example of org-specific naming rules. It enforces the Domain_[Brand_]Layer_Name[_TEST] convention for one specific subscriber org.

You can create your own naming ruleset following the same pattern:

  1. Copy subscriber-naming-pmd-ruleset.xml as a starting point
  2. Modify the regex in the XPath expression to match your naming convention
  3. Update the rule name, message, and description
  4. Add the file to your PMD configuration alongside kerndx-pmd-ruleset.xml

See the Code Scanning - Guide for detailed instructions on creating custom rules.


Common Issues

ProblemCauseFix
"Rule not found" or "Class not found" errorPMD version mismatch (v7 vs v6)Change the class attribute on each rule. PMD 7: net.sourceforge.pmd.lang.rule.xpath.XPathRule. PMD 6: net.sourceforge.pmd.lang.apex.rule.ApexXPathRule
Too many violations on first scanExisting codebase predates framework adoptionUse priority tiers for phased adoption -- start with P1 blockers only, expand to P3 and P5 over time
False positive in framework infrastructure classRule correctly flags the pattern, but the class intentionally uses the raw APISuppress with @SuppressWarnings('PMD.RuleName') and add a comment explaining why
Apex PMD extension not showing violationsRuleset path not set or extension not installedVerify .vscode/settings.json has apexPMD.rulesets pointing to scanner/kerndx-pmd-ruleset.xml
ESLint kerndx/* rules not foundPlugin not loaded in ESLint configVerify eslint.config.mjs imports and registers eslint-plugin-kerndx
combined-pmd-ruleset.xml fails with relative path errorsRunning from wrong directoryEnsure the working directory contains the scanner/ folder, or use absolute paths in the <rule ref="..."/> elements

What You Now Know

ConceptWhat It Does
kerndx-pmd-ruleset.xml24 PMD rules enforcing KernDX framework conventions (triggers, queries, DML, logging, HTTP, coverage hygiene, etc.)
eslint-plugin-kerndx6 ESLint rules enforcing LWC + test conventions (ComponentBuilder, console.log, naming, coverage-exempt justification, jest-theatre prevention, shared-fixture mutation prevention)
combined-pmd-ruleset.xmlSingle-file reference for tools that only accept one ruleset (IntelliJ)
Priority tiers (1/3/5)Phased adoption -- start with blockers, expand to should-fix, then informational
@SuppressWarningsPer-class or per-method opt-out with justification
code-analyzer.ymlSF Code Analyzer v5 configuration for CLI and CI/CD scanning

Key patterns:

  • Start with Priority 1 rules (inline SOQL and trigger delegation) -- these are the highest-impact framework violations
  • Configure your IDE first so violations appear as you type, before you commit
  • Use @SuppressWarnings sparingly and always with a justification comment
  • Create org-specific naming rulesets following the subscriber-naming-pmd-ruleset.xml example
  • Integrate into CI/CD to prevent regressions after the initial cleanup

Next Steps

TopicLink
Code Scanning Guide (CI/CD recipes, custom rules)Code Scanning - Guide
Fast Start - SelectorsFast Start - Selectors
Fast Start - DMLFast Start - DML
Fast Start - LoggingFast Start - Logging
Fast Start - Trigger ActionsFast Start - Trigger Actions
Fast Start - Test DataFast Start - Test Data
Fast Start - Outbound APIsFast Start - Outbound APIs