skills/code-quality/code-smell-detector/SKILL.md
Identifies code smells — structural patterns that correlate with maintainability problems — and explains why each matters in context. Use when reviewing a PR for structural quality, when the user asks what's wrong with a piece of code that isn't buggy, or when prioritizing refactoring targets.
npx skillsauth add santosomar/general-secure-coding-agent-skills code-smell-detectorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A smell is not a bug. It's a structural property that makes bugs more likely later. The code works today; the smell is about tomorrow.
| Smell | Detection heuristic | Why it matters | Fix |
| ------------------------ | --------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------ |
| Long method | > 40 lines, > 3 levels of nesting | Can't hold it in your head; every change risks the whole thing | Extract method |
| God class | > 15 methods, > 10 fields, or depends on > 8 other classes | Every change touches this file; merge conflicts; nobody understands it all | Split by responsibility |
| Feature envy | Method reads 3+ fields of another object, 0 of its own | The method is in the wrong class | Move method |
| Shotgun surgery | One conceptual change → edits in 5+ files | High coupling; change is expensive and error-prone | Consolidate the scattered concern |
| Primitive obsession | Three strings passed together (street, city, zip) everywhere | The missing type is the abstraction you need | Introduce parameter object / value type |
| Boolean blindness | Method takes 3+ booleans; call sites look like f(true, false, true) | Nobody knows what true means at the call site | Named parameters, enum, or config object |
| Speculative generality | Abstract class with one concrete subclass; interface with one impl; hook method nobody overrides | Complexity paid for, never used | Inline it → dead-code-eliminator |
| Data clump | Same 3+ variables always appear together in signatures | Missing type (same as primitive obsession, across methods) | Introduce a class |
| Message chain | a.getB().getC().getD().doThing() | Caller knows the entire object graph; any link change breaks it | Tell-don't-ask; hide the chain |
| Duplicated code | Two regions > 6 lines, > 80% similar | Bug fixed in one, not the other | Extract; but see FP note below |
Don't dump all findings. Rank by change frequency × smell severity:
Use git log --format= --name-only | sort | uniq -c | sort -rn to get change frequency. Smells in cold code are academic.
| Smell flagged | But actually fine when… |
| ------------------------ | -------------------------------------------------------------------------- |
| Long method | It's a flat sequence of independent steps (setup, setup, setup — a script) |
| God class | It's the app's composition root / DI container — by design, it knows everything |
| Duplicated code | The two copies have different reasons to change. "Coincidental duplication" should NOT be extracted — you'll couple two unrelated concerns |
| Speculative generality | It's a plugin interface and the second impl is on the roadmap this quarter |
| Message chain | The chain is navigating a well-known, stable schema (config.database.pool.size) |
Code:
class OrderProcessor:
def process(self, order, user, warehouse, shipper, payment, notifier,
audit, retry, dry_run): # 9 params
if not dry_run:
if payment.charge(user.card, order.total):
if warehouse.has_stock(order.items):
warehouse.reserve(order.items)
if shipper.can_deliver(user.address.zip): # message chain
label = shipper.create_label(user.address.street,
user.address.city,
user.address.zip) # data clump
# ... 40 more lines ...
Findings:
process() does validation, charging, reservation, shipping, notification, auditing. Six responsibilities.retry, dry_run as positional booleans. Call sites: processor.process(o, u, w, s, p, n, a, True, False). Which one is dry_run?street, city, zip always travel together. There's an Address object right there (user.address) — pass it whole.user.address.zip. Minor here (Address is stable), but if shipper took an Address, this goes away with fix #3.Prioritization: git log shows OrderProcessor edited 14 times last quarter. Long method + high churn → extract first.
code-refactoring-assistant for the mechanics. This skill finds; that one fixes.<file>:<line> <SMELL> churn=<commits-last-90d>
<what the structure is>
<why it will cost you — specific to this instance>
<suggested refactoring — one sentence>
<false-positive check: does the suppression rule apply?>
---
Ranked by (churn × severity). Fix the top 3; ignore the rest until they climb.
development
Extracts human-readable pseudocode from a verified formal artifact (Dafny, Lean, TLA+) while preserving the verified properties as annotations, so the proof-carrying logic can be reimplemented in a production language. Use when porting verified code to an unverified target, when documenting what a formal spec actually does, or when handing a verified algorithm to an implementer.
development
Translates natural-language or pseudocode descriptions of concurrent and distributed systems into TLA+ specifications ready for the TLC model checker. Identifies state variables, actions, type invariants, safety properties, and liveness properties from the description. Use when formalizing a protocol, when the user describes a distributed algorithm to verify, when designing a consensus or locking scheme, or when starting formal verification of a concurrent system.
testing
Reduces a TLA+ model so TLC can actually check it — shrinks constants, adds state constraints, abstracts data, or applies symmetry — when the state space is too large to enumerate. Use when TLC runs out of memory, when checking takes hours, or when a spec works at N=2 and you need confidence at larger scale.
development
TLA+-specific instance of model-guided repair — reads a TLC error trace, identifies the enabling condition that should have been false, strengthens the corresponding action, and maps the fix to source code. Use when TLC reports an invariant violation or deadlock and you have the code-to-TLA+ mapping from extraction.