Install
openclaw skills install bookforge-code-smell-diagnosisScan a codebase or code fragment for the 22 named code smells from Fowler's refactoring catalog and produce a prioritized diagnosis report with the specific refactoring prescription for each smell. Use when: a developer wants to know what is wrong with existing code before touching it; a code review reveals structural problems but no clear fix; a class or method feels wrong but the exact smell is hard to name; a refactoring effort needs a starting point and a prioritized order of attack; a code author wants to justify a refactoring to a team by naming the specific smell and the prescribed remedy. Covers all 22 smells: Duplicated Code, Long Method, Large Class, Long Parameter List, Divergent Change, Shotgun Surgery, Feature Envy, Data Clumps, Primitive Obsession, Switch Statements, Parallel Inheritance Hierarchies, Lazy Class, Speculative Generality, Temporary Field, Message Chains, Middle Man, Inappropriate Intimacy, Alternative Classes with Different Interfaces, Incomplete Library Class, Data Class, Refused Bequest, Comments. Maps each smell to its Fowler-prescribed refactoring(s) including conditional branches (same class vs. sibling subclasses vs. unrelated classes for Duplicated Code; few cases vs. type code vs. null for Switch Statements; etc.).
openclaw skills install bookforge-code-smell-diagnosisYou have existing source code — a class, a module, a method, or an entire service — and you need to know what is structurally wrong with it before deciding how to improve it.
This skill applies when:
The core insight from Fowler and Beck: Refactoring without diagnosis is guesswork. When you can name the smell precisely, the refactoring prescription follows directly. "This is Feature Envy" immediately implies Move Method. "This is a Switch Statement on a type code" immediately implies Replace Type Code with Subclasses or Replace Type Code with State/Strategy followed by Replace Conditional with Polymorphism. The name unlocks the remedy.
This is the hub skill. Sibling skills execute specific remedies once a smell is diagnosed:
type-code-refactoring-selector — when Primitive Obsession or Switch Statements are presentconditional-simplification-strategy — when Switch Statements or complex conditionals are presentclass-responsibility-realignment — when Feature Envy, Inappropriate Intimacy, or Shotgun Surgery are presentbig-refactoring-planner — when multiple smells indicate a systemic design problemdata-organization-refactoring — when Data Clumps, Primitive Obsession, or Data Class are presentThe code to diagnose. Either a directory path to scan, specific files, a class name, or a pasted code fragment. Why: the diagnosis is grounded in actual code — general impressions are insufficient for naming specific smells and prescribing specific refactorings.
Language and framework. Why: smell signals differ by language. Long parameter lists are more prevalent in procedural-style Python than in Java with builder patterns. Switch statements in Java suggest polymorphism; pattern matching in a language with first-class ADTs is different.
pom.xml, package.json, pyproject.toml)Scan the environment to orient the diagnosis:
Size signals (quick grep targets):
- Long methods: functions/methods exceeding 20-30 lines
- Long parameter lists: functions with 4+ parameters
- Large classes: classes exceeding 200 lines or with 10+ instance variables
- Duplicated code: identical or near-identical blocks appearing in multiple places
- Switch/if-elif chains: switch statements or long if/elif chains on the same variable
Structure signals:
- Classes with only getters/setters and no behavior → Data Class
- Methods referencing another class's data more than their own class → Feature Envy
- Subclasses that override many parent methods without using the parent's data → Refused Bequest
- Long chains like a.getB().getC().getD() → Message Chains
ACTION: Read the target code — structure first, then detail.
WHY: Smell detection is pattern recognition, not line-by-line parsing. A structural read (class names, method names, field names, file organization) surfaces most smells before reading any implementation. Diving into implementation first causes you to miss forest-level smells (Large Class, Divergent Change, Shotgun Surgery) while fixating on method-level detail.
Structural questions to answer:
Then read method bodies for the classes flagged as problematic.
ACTION: Systematically evaluate each smell against the code. Do not skip smells — the ones you expect to be absent are often the most revealing when present.
WHY: Unsystematic diagnosis produces a partial list. Developers naturally notice some smells (long methods, duplication) and miss others (Divergent Change, Speculative Generality, Refused Bequest). Checking all 22 takes 10 minutes and prevents prescribing the wrong refactoring because a deeper smell was missed.
Use the full smell catalog in the reference file (references/smell-catalog.md) for detailed detection criteria. The diagnostic decision tree below gives the key signal and the prescribed remedy for each smell:
1. Duplicated Code
2. Long Method
3. Large Class
depositAmount and depositCurrency belong together)4. Long Parameter List
5. Switch Statements
6. Parallel Inheritance Hierarchies (special case of Shotgun Surgery)
7. Refused Bequest
8. Alternative Classes with Different Interfaces
9. Divergent Change
10. Shotgun Surgery
11. Feature Envy
12. Data Clumps
13. Message Chains
a.getB().getC().getD().getValue() — a client asks one object for another, then asks that object for another, navigating a chain. Any change to the intermediate structure requires changing the client14. Middle Man
15. Inappropriate Intimacy
16. Incomplete Library Class
17. Lazy Class
18. Speculative Generality
19. Temporary Field
20. Comments (used as deodorant)
The remaining two smells appear in object hierarchy design:
21. Data Class
22. Refused Bequest — covered under Group B (Object-Oriented Abusers) above.
ACTION: Rank all identified smells for the diagnosis report.
WHY: Not all smells are equal. Treating a cosmetic smell (Lazy Class) with the same urgency as a change-blocking smell (Shotgun Surgery) wastes time and de-prioritizes what matters. Prioritization makes the report actionable — the developer knows where to start.
Prioritization criteria (apply in order):
Severity tiers for the report:
ACTION: Produce the structured diagnosis report as the skill's deliverable.
WHY: The report is what the developer acts on. It must be specific enough to be actionable (naming the exact smell and the exact refactoring) and organized enough to be usable as a task list or review checklist. Vague reports ("this code needs work") produce no change. Named smells with prescribed remedies produce refactoring plans.
Output format:
# Code Smell Diagnosis — [Target: class/module/directory name]
## Summary
Files scanned: [count]
Smells found: [N total — X HIGH, Y MEDIUM, Z LOW]
Primary cluster: [the dominant smell group — e.g., "Change Preventers + Bloaters"]
---
## Priority Findings
### Finding #N — [Smell Name] — [HIGH | MEDIUM | LOW]
**Location:** [file:line or class/method name]
**Evidence:** [what in the code signals this smell — be specific]
**Prescription:** [the Fowler-prescribed refactoring(s)]
**Why this prescription:** [the specific conditional branch that applies — e.g., "duplication is in sibling subclasses, not the same class, so Pull Up Method rather than just Extract Method"]
**Next step:** [the single first action to take]
---
[repeat per finding]
---
## Refactoring Sequence
[Ordered list of the prescribed refactorings in the recommended sequence.
Change preventers first. Dependencies between refactorings noted.]
## Related Skills
[Which sibling skills to invoke for execution]
1. Name the smell before prescribing the remedy. A generic prescription ("extract this into a method") without naming the smell misses the diagnostic value. The smell name carries the full decision tree with it. "Feature Envy" tells a developer exactly what class the method belongs in. "Switch Statement on a type code" tells them exactly which three refactorings to apply in sequence.
2. Follow the conditional branches precisely. Most smells have context-dependent prescriptions. Duplicated Code in the same class has a different remedy than Duplicated Code in sibling subclasses. Switch Statements affecting a single method have a different remedy than Switch Statements scattered across the codebase. Getting the branch wrong produces the wrong refactoring.
3. One smell often hides another. Data Clumps often reveal Feature Envy once turned into objects. Long Method is often Divergent Change in disguise. Shotgun Surgery is often caused by a missing class that Data Clumps would reveal. When you name one smell, check whether it points to a deeper one.
4. Smells are not rules — they are signals. Fowler and Beck explicitly decline to give precise metrics. A 25-line method might be fine; a 10-line method might reek. The judgment is: does this code make the next change harder? Does it attract bugs? Is it difficult to understand? The smell is an indicator, not a verdict.
5. Comments are a diagnostic tool, not a finding. Heavy commenting often signals other smells. Use comments as a guide to where Extract Method and Rename Method are needed — the smell is what the comment is masking, not the comment itself.
Scenario: A developer inherits a PaymentService class (280 lines, 12 methods, 9 instance variables) and asks for a diagnosis before adding a new payment gateway.
Step 1 — Structural read:
gatewayUrl, gatewayApiKey, gatewayTimeout → Data Clumps candidate)processPayment, validateCard, logTransaction, formatCurrency, sendWebhook, retryWebhook, buildGatewayRequest, parseGatewayResponse — some methods clearly work on gateway concerns, others on local concernsStep 2 — Smell check (selected findings):
Divergent Change (HIGH): The class changes when the gateway changes (3 methods: buildGatewayRequest, parseGatewayResponse, processPayment) AND when the webhook logic changes (2 methods: sendWebhook, retryWebhook) AND when logging changes (1 method: logTransaction). Three axes of change in one class.
Data Clumps (MEDIUM): gatewayUrl, gatewayApiKey, gatewayTimeout appear together in every gateway-related method signature. Deleting gatewayUrl makes the others meaningless.
Long Method (MEDIUM): processPayment is 55 lines with embedded comments marking sections ("// validate", "// build request", "// process", "// log").
Step 3 — Prioritize: Divergent Change first (it's a change preventer). Data Clumps second (will simplify gateway extraction). Long Method third (the extracted sections will become methods in the new class).
Diagnosis report excerpt:
### Finding #1 — Divergent Change — HIGH
Location: PaymentService (entire class)
Evidence: The class changes for at least three distinct reasons:
(1) Gateway integration changes → buildGatewayRequest, parseGatewayResponse, processPayment
(2) Webhook delivery changes → sendWebhook, retryWebhook
(3) Logging changes → logTransaction
Prescription: Extract Class for each axis of change.
- Extract GatewayClient (gateway request/response)
- Extract WebhookDispatcher (send + retry logic)
- PaymentService retains orchestration only
Why this prescription: Divergent Change calls for Extract Class on each axis.
The test: "I have to change these N methods every time X happens."
Three different values of X = three different classes needed.
Next step: Extract GatewayClient first — it's the largest axis and will expose
the Data Clumps smell for remedy.
Scenario: A code reviewer asks what's wrong with this Python method:
def calculate_charge(customer_type, base_price, quantity, discount_code,
loyalty_years, is_weekend, tax_rate):
if customer_type == 'enterprise':
price = base_price * quantity * 0.85
elif customer_type == 'retail':
price = base_price * quantity
elif customer_type == 'wholesale':
price = base_price * quantity * 0.7
else:
price = base_price * quantity
if discount_code == 'SUMMER10':
price *= 0.90
elif discount_code == 'VIP20':
price *= 0.80
if loyalty_years > 5:
price *= 0.95
if is_weekend:
price *= 1.05
return price * (1 + tax_rate)
Step 2 — Smell check:
Long Parameter List (HIGH): 7 parameters. Why this branch: there is no obvious existing object that holds these — customer_type, loyalty_years suggest a Customer object; base_price, quantity, discount_code suggest an Order. Two Introduce Parameter Object applications.
Switch Statements (HIGH): The customer_type branching recurs — any new customer type requires finding every switch on customer_type and adding a case. Why this branch: type code affects behavior, and customer type is unlikely to change at runtime → Replace Type Code with Subclasses on Customer, then Replace Conditional with Polymorphism for the pricing logic.
Diagnosis report excerpt:
### Finding #1 — Switch Statements — HIGH
Location: calculate_charge(), lines 2-9 (customer_type branching)
Evidence: A switch on customer_type determines pricing multiplier.
This switch will exist wherever customer pricing is computed.
Every new customer type requires finding every such switch.
Prescription: Replace Type Code with Subclasses (EnterpriseCustomer,
RetailCustomer, WholesaleCustomer); then Replace Conditional with
Polymorphism (each subclass implements its own price multiplier method).
Why this branch: type code affects behavior; customer type doesn't change
at runtime for a given customer → subclassing is appropriate.
(If type changed at runtime, use Replace Type Code with State/Strategy instead.)
Next step: Introduce Parameter Object first (Finding #2) to create a
Customer object; then apply type code replacement on that object.
### Finding #2 — Long Parameter List — HIGH
Location: calculate_charge() signature (7 parameters)
Evidence: customer_type, loyalty_years → Customer data cluster.
base_price, quantity, discount_code → Order data cluster.
Deleting customer_type makes loyalty_years ambiguous without context.
Prescription: Introduce Parameter Object twice — Customer(customer_type,
loyalty_years) and Order(base_price, quantity, discount_code).
Then: Preserve Whole Object — pass Customer and Order, not their fields.
Next step: Define Customer and Order dataclasses; update the signature.
This unblocks the Switch Statement refactoring (Finding #1).
Refactoring sequence:
1. Introduce Parameter Object: Customer, Order
2. Replace Type Code with Subclasses on Customer
3. Replace Conditional with Polymorphism for pricing
4. Preserve Whole Object in calculate_charge signature
Scenario: A codebase has Animal, Dog, Cat, Bird hierarchy. A developer notices that every time a new animal species is added, a parallel set of AnimalSound, DogSound, CatSound, BirdSound classes must also be added.
Step 2 — Smell check:
Parallel Inheritance Hierarchies (HIGH): Subclassing Animal always requires subclassing AnimalSound. The prefix pattern is exact (Dog/DogSound, Cat/CatSound).
Prescription: Make instances of one hierarchy refer to instances of the other (Strategy pattern). Move Method and Move Field from AnimalSound hierarchy into Animal hierarchy using a sound strategy object. Once all behavior is moved, the AnimalSound hierarchy disappears.
| File | Contents | When to read |
|---|---|---|
references/smell-catalog.md | Full detection criteria for all 22 smells with code examples per language; borderline cases; false positive filters | Step 2 — systematic smell check |
references/refactoring-prescriptions.md | Full prescription tree per smell with all conditional branches; cross-references to Fowler catalog chapters | Step 2 — selecting the correct refactoring |
Hub skill relationships:
type-code-refactoring-selector — when Switch Statements or Primitive Obsession (type code variant) are diagnosedconditional-simplification-strategy — when Switch Statements or deeply nested conditionals are presentclass-responsibility-realignment — when Feature Envy, Inappropriate Intimacy, Divergent Change, or Shotgun Surgery are the primary findingsbig-refactoring-planner — when the diagnosis reveals systemic design problems requiring coordinated multi-step refactoringdata-organization-refactoring — when Data Clumps, Primitive Obsession, or Data Class are primary findingsThis skill is licensed under CC-BY-SA-4.0. Source: BookForge — Refactoring: Improving the Design of Existing Code by Martin Fowler and Kent Beck.
Install related skills from ClawhHub:
clawhub install bookforge-type-code-refactoring-selectorclawhub install bookforge-conditional-simplification-strategyclawhub install bookforge-class-responsibility-realignmentclawhub install bookforge-big-refactoring-plannerclawhub install bookforge-data-organization-refactoringOr install the full book set from GitHub: bookforge-skills