Rules reference
Every one of the 14 rules in depth: exact detection logic, options, false-positive notes
CleanLens has 14 rules. Each one is configured in .clean-code-tracker.json
under rules.<ruleId>:
"maxFunctionLines": { "enabled": true, "severity": "medium", "limit": 40 }| Field | Type | Meaning |
|---|---|---|
enabled | boolean, required | The rule runs only if this is true. |
severity | low | medium | high | critical, required | Sets the violation's weight and its editor diagnostic level. |
limit | positive number, optional | Threshold for numeric rules. |
options | object of numbers/booleans, optional | Rule-specific extra settings. |
Summary
| Rule id | Title | Category | Default severity | Default limit / options | JS/TS | Python |
|---|---|---|---|---|---|---|
maxFunctionLines | Long function | structure | medium | limit 40 | ✅ | ✅ |
maxFileLines | Large file | structure | medium | limit 400 (Django 500) | ✅ | ✅ |
maxParameters | Too many parameters | structure | medium | limit 5 | ✅ | ✅ |
maxComplexity | High complexity | structure | high | limit 10 | ✅ | ≈ |
maxNestingDepth | Deep nesting | structure | high | limit 4 | ✅ | ≈ |
detectDuplicateCode | Duplicate code | duplication | medium | options.minLines 6 | ✅ | ✅ |
detectUnusedCode | Unused code | hygiene | low | — | ✅ | imports only |
requireClearVariableNames | Unclear name | hygiene | low | — | ✅ | ✅ |
requireErrorHandling | Missing error handling | hygiene | medium | — | await | risky calls |
forbidEmptyCatchBlocks | Empty catch block | hygiene | high | — | ✅ | ✅ |
forbidHardcodedSecrets | Hardcoded secret | hygiene | critical | — | ✅ | ✅ |
forbidDebugStatements | Debug statement | hygiene | low | — | ✅ | ✅ |
requireSingleResponsibility | Multiple responsibilities | structure | medium | options.maxMethods 10 | ✅ | ✅ |
requireDocumentationForComplexCode | Complex code without docs | structure | low | options.complexityThreshold 15 | ✅ | ✅ |
≈ = approximate (heuristic, see below).
The defaults come from BASE_RULES in src/configuration/presets.ts.
If a rule has no limit/option in the config, the analyzer uses the same
default value.
Structure rules
maxFunctionLines — Long function
- Reports a function whose span (first line to last line, inclusive) is
greater than
limit. - JS/TS: every function-like node, including arrow functions and methods. Nested functions are measured separately; the outer function's span includes them.
- Python:
def/async deffrom the header to the last indented line. - Location: the whole function (start → end line). Carries the function name.
maxFileLines — Large file
- Reports a file with more lines than
limit. One violation per file, on line 1. - Counts all lines, including blanks and comments.
maxParameters — Too many parameters
- Reports a function that declares more than
limitparameters. - Python ignores
self,cls,*argsand**kwargs. - JS/TS counts every declared parameter; a destructured object counts as one.
maxComplexity — High complexity
- Reports a function whose cyclomatic complexity is greater than
limit. - JS/TS: 1 + each
if,?:, loop,catch, non-emptycase,&&,||,??. Nested functions are not included. - Python (approximate): 1 + each body line starting with
if/elif/for/while/except/case+ eachand/or. Nested functions are included.
maxNestingDepth — Deep nesting
- Reports a control statement nested deeper than
limit. - JS/TS: depth increases for
if,for,for…in,for…of,while,do,switch. Anelse ifdoes not add a level. Every statement past the limit is reported. - Python (approximate): depth = indentation ÷ 4 on
if/elif/else/for/while/ with/try/exceptlines. The count includes the enclosingdef/classlevels, so Python code reaches the limit sooner than equivalent JS. Consider a higher limit for Python projects.
requireSingleResponsibility — Multiple responsibilities
Two triggers:
- A class with more than
options.maxMethodsmethods (JS/TS: methods, getters, setters; Python:defs one level inside the class). - A function that is both longer than 1.5 ×
maxFunctionLines.limitand more complex than 1.5 ×maxComplexity.limit. This trigger reads the other two rules' limits, so changing them changes this rule too.
This is a heuristic. See the false-positive notes below.
requireDocumentationForComplexCode — Complex code without docs
- Reports a function with complexity ≥
options.complexityThresholdand no documentation. - JS/TS: documentation = a JSDoc block (
/** … */) attached to the function. A plain//comment does not count. - Python: documentation = a docstring on the first body line.
Duplication rule
detectDuplicateCode — Duplicate code
- Reports a block of at least
options.minLinessignificant lines (default 6, minimum 3) that is identical, after whitespace normalization, to a block seen earlier. - Lines shorter than 12 characters, brace-only lines, comments and import/export lines are ignored, so boilerplate does not trigger it.
- One violation per duplicated block; the message says where the original is.
- The HEAD scan detects clones across files; the commit walk detects clones within a file. See 05 — Analyzers.
Hygiene rules
detectUnusedCode — Unused code
- JS/TS: reports an import, a module-level non-exported variable, or a
non-exported function declaration whose name appears only once in the
file. There is no type information and no cross-file analysis, so only
obvious cases are found. Names starting with
_are ignored. - Python: reports unused imports only (the name appears once in the file).
from x import *is ignored.
requireClearVariableNames — Unclear name
- Variables (JS/TS): 1–2 character names not on the allow list, vague words
with a number (
data1,tmp2,obj3), and short letters with digits (abc1). - Parameters and function/class names (both languages): only vague words
with a number and letters+digits. Conventional short names (
e,x,cb,i) are allowed. - Allowed:
_-prefixed names and technical names (utf8,sha256,ipv6,oauth2,i18n,h1, …).
requireErrorHandling — Missing error handling
- JS/TS: an
awaitthat is not inside atryblock (in the same function) and not part of a.catch(…)/.then(…)chain. - Python: a line calling
open(,requests.*(,urllib,json.load(s)(,socket.,subprocess.,os.remove(,shutil.,int(,float(that is not inside atry:block of the same function. - Code often handles errors at a higher level (a framework, a caller), so this rule is the most likely to be noisy.
forbidEmptyCatchBlocks — Empty catch block
- JS/TS: a
catchclause with no statements. A catch that contains only a comment counts as empty. - Python: an
except …:whose body is onlypassor....
forbidHardcodedSecrets — Hardcoded secret
- Checks string values assigned to variables, object properties and assignments (JS/TS), and assignments or any string on a line (Python).
- Reports known token formats (AWS, GitHub, Slack, JWT,
sk-…, private keys), or a credential-looking value assigned to a secret-looking name. Placeholders such aschangeme,your_api_key,<token>,${VAR},exampleare ignored. - Severity is
criticalby default: one secret weighs as much as eightlowfindings.
forbidDebugStatements — Debug statement
- JS/TS:
console.log/debug/info/trace/dir/table(…)anddebugger.console.errorandconsole.warnare allowed. - Python: a line starting with
print(, and anybreakpoint().
False positives and how to handle them
| Rule | Typical false positive | Suggested handling |
|---|---|---|
requireErrorHandling | Errors handled by a framework or a caller | Lower to low, or disable |
requireSingleResponsibility | Large but cohesive classes (e.g. a Django ModelAdmin, a React class component) | Raise maxMethods |
requireDocumentationForComplexCode | Teams that document with // comments | Raise complexityThreshold, or disable |
maxNestingDepth (Python) | Methods start at depth 2 | Raise limit to 5–6 for Python |
forbidDebugStatements | CLIs and scripts where print/console.log is the output | Exclude the script folders, or disable |
detectUnusedCode | Names used only via re-export or string lookup | Prefix with _, or lower to low |
forbidHardcodedSecrets | Test fixtures with fake tokens | Exclude the fixtures folder |
There are no inline suppression comments. Adjust the rule, or exclude the path. Recipes are in 11 — Customization guide.
Rule changes and caching
Any change to rules changes the analysis config hash, which invalidates the
per-commit cache. The next run re-analyzes everything with the new rules. See
09 — Caching & performance.