# Advanced PMD Patterns
Multi-rule rulesets, overriding built-in rules, exclusion patterns, Java-based rules, and sharing rules across projects.
## Multiple Rules in One Ruleset File
A single ruleset XML can contain any number of rules. This is the recommended approach for teams maintaining a shared rule set:
```xml
Team coding standards
3
2
3
```
Rules with different `language` values (apex, xml, visualforce, etc.) can coexist in one ruleset. PMD applies each rule only to files matching its language.
## Overriding Built-in PMD Rules
Override severity, priority, or configurable properties on PMD's built-in rules without writing new ones:
```xml
Customized built-in rule thresholds
2
2
```
**Configuration:**
```yaml
# code-analyzer.yml
engines:
pmd:
custom_rulesets:
- "custom-rules/customized-builtins.xml"
```
### How rule ref works
| Pattern | Effect |
|---------|--------|
| `` | Override one specific rule |
| `` | Include entire category |
| `` | Include category minus specific rules |
| Nested `` | Override severity (1=Critical, 2=High, 3=Moderate, 4=Low, 5=Info) |
| Nested `` | Override configurable thresholds/options |
### Common built-in rules to customize
| Rule | Useful Properties |
|------|-------------------|
| `CyclomaticComplexity` | `classReportLevel` (default: 40), `methodReportLevel` (default: 25) |
| `ExcessiveParameterList` | `minimum` (default: 4) |
| `ExcessiveClassLength` | `minimum` (default: 1000) |
| `TooManyFields` | `maxfields` (default: 15) |
| `NcssMethodCount` | `minimum` (default: 60) |
| `DebugsShouldUseLoggingLevel` | `strictMode` (default: false) |
| `ApexUnitTestClassShouldHaveAsserts` | (no properties — exclude if unwanted) |
## Exclusion Patterns
Three mechanisms for excluding files and rules:
### 1. File exclusions in `code-analyzer.yml`
Exclude entire directories or file patterns from all engines:
```yaml
# code-analyzer.yml
ignores:
files:
- "**/node_modules/**"
- "**/fflib_*"
- "**/*Test*.cls"
- "**/generated/**"
```
Supports glob patterns: `*`, `**`, `?`, `[...]`, `{...}`
### 2. Rule exclusions in PMD rulesets
Exclude specific rules when including an entire category:
```xml
```
### 3. Exclude patterns in PMD rulesets
Exclude files at the PMD level (applies only to rules in that ruleset):
```xml
.*/fflib_.*
.*/test/.*
...
```
### 4. Disabling individual rules via config
Disable any rule without editing rulesets:
```yaml
# code-analyzer.yml
rules:
pmd:
ApexUnitTestClassShouldHaveAsserts:
disabled: true
CyclomaticComplexity:
severity: "Low" # Downgrade instead of disable
```
## Java-Based Custom Rules (Advanced)
For rules that need logic beyond XPath (complex data flow, cross-file analysis), write Java PMD rules:
1. **Create a Java class** extending `net.sourceforge.pmd.lang.apex.rule.AbstractApexRule`
2. **Compile into a JAR** file
3. **Reference in config:**
```yaml
# code-analyzer.yml
engines:
pmd:
java_classpath_entries:
- "custom-rules/my-rules.jar" # JAR with compiled rule classes
- "custom-rules/lib/" # Folder of JARs (all JARs inside loaded)
custom_rulesets:
- "category/myteam/rules.xml" # Ruleset inside the JAR (classpath resource)
```
The JAR's ruleset XML references the Java class:
```xml
3
```
**Requirements:**
- Java 11+ for compilation
- PMD 7.x API (use `net.sourceforge.pmd.lang.rule.xpath.XPathRule` for XPath, or extend `AbstractApexRule` for visitor-pattern Java rules)
## Sharing Rules Across Projects
### Approach 1: Relative paths with shared config
If projects share a parent directory or monorepo:
```yaml
# code-analyzer.yml
engines:
pmd:
custom_rulesets:
- "../shared-rules/team-standards.xml" # Relative to config_root
```
`config_root` is the directory containing `code-analyzer.yml`. All relative paths resolve from there.
### Approach 2: JAR-based distribution
Package rules into a JAR and distribute via artifact repository:
1. Build a JAR containing your ruleset XML at a classpath resource path (e.g., `category/myteam/standards.xml`)
2. Place the JAR in each project's `custom-rules/` directory (or download in CI)
3. Reference in config:
```yaml
engines:
pmd:
java_classpath_entries:
- "custom-rules/myteam-rules-1.0.jar"
custom_rulesets:
- "category/myteam/standards.xml" # Resource inside the JAR
```
### Approach 3: Git submodule or symlink
For simpler setups, share the ruleset XML via git submodule:
```bash
git submodule add https://github.com/myteam/pmd-rules.git custom-rules/shared
```
```yaml
engines:
pmd:
custom_rulesets:
- "custom-rules/shared/team-standards.xml"
```
### CI/CD Integration
All approaches work in CI because paths are relative to `config_root`. Example GitHub Actions step:
```yaml
- name: Run Code Analyzer
run: |
sf code-analyzer run --rule-selector "pmd:2,regex:2" --target force-app/
```
No absolute paths needed — the config file handles resolution.