Also called Bad smell, Design smell, Anti-pattern
How a code smell is measured
A smell is a judgment, but most have a measurable proxy:
| Smell | Typical proxy | Common starting threshold |
|---|---|---|
| Long method | Lines of code (NLOC) per function | 50 to 60 lines |
| Complex method | Cyclomatic complexity | 10 |
| Deep nesting | Maximum nesting depth | 4 |
| Large class | Lines or method count per class | 200+ code lines |
| Duplicated code | Matching token sequences across files | 6+ lines in a clone |
| Long parameter list | Parameter count | 5 |
| Shotgun surgery | Files that co-change with many others | from git history |
| Feature envy | Calls to another class vs own class | needs a call graph |
Take a method of 140 lines, CCN 18, nesting 5, with 7 parameters. That one function trips four proxies at once: long method, complex method, deep nesting and long parameter list. The refactoring that fixes one (Extract Method) usually fixes the rest, which is why smells cluster and why counting them as separate problems overstates the work.
Thresholds are conventions, not laws. Fowler and Beck deliberately gave no numbers; the book says that no set of metrics rivals informed human intuition.
Why code smells matter
Smells matter when they sit in code that keeps changing. A smell makes each edit slower (the reader must untangle the code first) and riskier (more behavior is coupled in one place). A god class or a duplicated block that nobody touches is not worth a refactoring sprint. The same smell in a file changed every week is a recurring cost, and that is where technical debt accrues interest. Smells also differ in how strongly they predict defects. In repowise's own validation, low cohesion, duplicated code and broad error handling fire often but predict bugs weakly, so they are down-weighted in the defect score and counted in full only in maintainability.
How to measure code smells yourself
- Multi-language rule engines: SonarQube (or SonarLint in the editor) and PMD for Java and others.
- JavaScript and TypeScript: ESLint rules such as
complexity,max-lines-per-function,max-depthandmax-params. - Python: Pylint (
too-many-arguments,too-many-locals), Ruff's complexity and argument-count rules,radon ccfor complexity. - Duplication:
jscpdworks across many languages. - Change coupling and churn: plain
git log, or a code-hotspot tool, because shotgun surgery is visible in history, not in a single snapshot.
For rule-based smells on one snapshot, these linters are the right pick and are free. They do not rank smells by how often the file changes.
How repowise measures code smells
repowise treats smells as scored findings from deterministic detectors (no LLM) and folds them into the per-file code health score. The smell-like ones, with thresholds read from the source:
| Classic smell | repowise detector | Fires when |
|---|---|---|
| Long method | large_method | 60+ code lines and CCN 3 or more; critical at 200 |
| Complex method | complex_method | CCN 9 or more |
| God class / large class | god_class | 200+ code lines, 15+ methods and a brain method (NLOC 70+, CCN 9+) |
| Brain method | brain_method | NLOC 70+, CCN 9+, in a central file (advisory, not scored) |
| Divergent change / low cohesion | low_cohesion | LCOM4 of 2 or more with 5+ methods |
| Duplicated code | dry_violation | 8% of a file duplicated with a 6-line clone, across files; hidden from default surfaces |
| Long parameter list | primitive_obsession | 5+ parameters (7 for constructors), mostly primitive types |
| Shotgun surgery | co_change_scatter | top fifth of the repo by co-change breadth, 3+ commits in 90 days |
| Deep nesting | nested_complexity | nesting depth 4 or more |
| Complicated condition | complex_conditional | 3 or more boolean operators |
Feature envy is covered by the Move Method refactoring plan, which proposes moving a method to the class it works with. A low score means look here first, and the file's change history decides the order.
Where to see them: repowise health, repowise health --file <path> and get_health over MCP, or the code health feature page. Refactoring plans (Extract Method, Extract Class, Move Method, Split File, Break Cycle) name the exact symbols; generate_refactoring_code is an opt-in MCP tool that uses your LLM, and repowise never applies an edit itself. Related: temporal coupling is the history signal behind the shotgun-surgery row.