Instrucciones de origen · Vista previa de solo lectura
name
smell
description
Detect software architecture bad smells, algorithmic complexity hotspots, and anti-patterns in a codebase. Produces a detailed markdown report identifying violations of architectural principles, design patterns, code quality, and performance complexity. Triggers on: smell, code smell, architecture smell, find anti-patterns, detect bad smells, complexity analysis, 代码坏味道, 架构坏味道, 反模式, 找出坏味道, 复杂度分析.
user-invocable
true
Smell — Architecture Bad Smell Detector
Analyze a codebase to find violations of software architecture principles, anti-patterns, code "bad smells," and algorithmic complexity hotspots. Produce a comprehensive, actionable markdown report.
Knowledge base: This skill encodes architectural patterns, anti-patterns, code smells, and algorithmic complexity heuristics drawn from industry research and practice, including the classic code smells catalog by Martin Fowler / Kent Beck (as organized on refactoring.guru: Bloaters, Object-Orientation Abusers, Change Preventers, Dispensables, Couplers).
The Job
Understand the scope — ask what part of the project to analyze (full project, specific module, or recent changes)
Scan the codebase using find, grep, and Agent (Explore subagent) to gather evidence
Identify architectural smells and anti-patterns
Generate a detailed markdown report saved to tasks/smell-report-[timestamp].md
Present a summary of findings to the user
Step 1: Scope Clarification
Ask the user:
What scope should I analyze?
A. Entire project (thorough, may take time)
B. Specific module/directory: [please specify]
C. Only recently changed files (git diff)
D. Only architectural-level issues (skip low-level code smells)
If the user doesn't specify, default to option A for small projects (< 100 files) or C for large projects.
Step 2: Evidence Gathering
Use the Explore subagent (Agent with subagent_type: "Explore") to scan the codebase for architectural patterns and anti-patterns. Run multiple parallel explorations:
Exploration Commands
Run these in parallel to gather evidence efficiently:
Project Structure Scan: Map the directory tree, identify the architectural style (layered, modular monolith, microservices, etc.)
Shared service classes undermining slice independence
Remedy: Use events/messages for cross-slice communication, duplicate simple logic if needed
Top Ten Software Architecture Mistakes
A set of architecture-level anti-patterns describing over- and under-engineering. The common thread: architecture disconnected from real needs and reality. The opposite extreme (too little architecture) is equally a smell.
Over-Layered / Multitier Architecture
"Layers on layers on layers." Adding tiers beyond what the problem needs:
Each layer just forwards calls to the next with no transformation or value
Simple read requires touching 6+ classes across 4 layers
Remedy: Collapse pass-through layers; keep only layers that carry real responsibility
Over-Abstraction
Abstraction piled on until the code is impossible to follow:
Excessive interfaces, generics, factories, and indirection for single implementations
You can't tell what actually runs without stepping through many hops
Remedy: Inline single-implementation abstractions; abstract only at real variation points (rule of three)
Futuristic Architecture
Solution built for imagined future requirements that no one can actually predict:
Most speculative flexibility is wasted effort — closely related to Speculative Generality and YAGNI
Remedy: Build for today's known requirements; add flexibility when a real second case arrives
Technology-Enthusiast Architecture
New/shiny technology put into production because the architect liked it:
Unproven tech adopted without validating it fits the problem or scales
Chasing trends over stability
Remedy: Evaluate tech against actual requirements; prefer proven tools; prototype before committing
Overkill Architecture
A simple problem solved with a disproportionate amount of architecture and technology:
Microservices, event sourcing, k8s for a CRUD app with a handful of users
Remedy: Match architecture weight to problem size (KISS); start simple, evolve when justified
Cloud / Visio Architecture
"Architecture" that exists only in nice diagrams, disconnected from the code and runtime reality:
Diagrams don't match what's actually deployed; boxes and arrows with no code correspondence
Remedy: Keep architecture docs grounded in and verified against the real system
Note on the opposite extreme: total lack of architecture (no boundaries, no structure) is equally a smell — see Big Ball of Mud and Missing Architecture. Both under- and over-engineering are failures.
Remedy: Parameterize, use dependency injection, make state explicit
Stamp Coupling
Passing entire data structures when only a few fields needed:
Functions receiving large DTOs but using one field
Remedy: Create focused parameters or smaller interfaces (ISP)
Shotgun Surgery
A single change requires modifications across many files:
Adding a field touches 5+ files in different modules
Remedy: Consolidate related behavior, apply Single Responsibility
Feature Envy
A method that uses another class's methods more than its own:
Method calls other.foo(), other.bar(), other.baz() with few self-calls
Remedy: Move the method to the class it envies
Data Clumps
Same group of fields appearing together in multiple places:
(street, city, zip) appearing in 5 method signatures
Remedy: Extract into a value object
Divergent Change
One module/class is repeatedly changed for many unrelated reasons (the opposite of Shotgun Surgery):
"I always change these three methods for DB changes, and those two for UI changes" in the same class
Remedy: Split the class along its axes of change (Single Responsibility)
Inappropriate Intimacy
Two classes are too entangled with each other's internals:
Reaching into another class's private fields, tight bidirectional references
Remedy: Move methods/fields to the class they belong to, extract a shared class, or replace with delegation
Message Chains
Long navigation chains like a.getB().getC().getD().doThing():
Client coupled to the whole object graph; violates the Law of Demeter
Remedy: Hide delegation — add a method on the first object that returns what the client needs
Middle Man
A class that delegates almost all of its work to another class:
Most methods just forward calls; adds indirection without value
Remedy: Remove the middle man and let clients talk to the real object (inline the delegation)
Parallel Inheritance Hierarchies
Every time you add a subclass to one hierarchy, you must add one to another:
Shape/ShapeRenderer, Employee/EmployeePermission growing in lockstep
Remedy: Merge hierarchies or make one hierarchy reference the other instead of mirroring it
Code-Level Smells
Long Method
Methods > 50 lines (or whatever suits the language)
Deep nesting > 3 levels
Multiple levels of abstraction mixed
Remedy: Extract methods at same abstraction level, compose
Long Parameter List
Methods with > 4 parameters
Boolean flags controlling behavior
Remedy: Introduce parameter object, split method, remove flag arguments
Duplicated Code
Identical or near-identical logic in 3+ places
Copy-paste with slight variations
Remedy: Extract shared method, apply Template Method or Strategy pattern
Primitive Obsession
Using primitives instead of domain types:
string for Email, PhoneNumber, URL
int for Money, Age, Quantity
decimal without Currency context
Remedy: Create value objects with validation and behavior
Magic Numbers/Strings
Hardcoded literals without explanation
if (status == 3) instead of if (status == Status.COMPLETED)
Remedy: Extract named constants or enums
Comments as Deodorant
Comments that explain what code does (code should be self-documenting)
Commented-out code blocks
"TODO" comments accumulating without resolution
Remedy: Refactor to make code clear, delete dead code, track TODOs as issues
Deep Nesting (Arrow Anti-Pattern)
Loops and conditionals nested so deeply the code drifts rightward into an "arrow" shape:
if { if { for { if { ... } } } } — hard to trace which conditions hold at any point
Usually > 3 levels of indentation in one function
Remedy: Guard clauses / early returns, extract nested blocks into methods, invert conditions, replace conditional with polymorphism
Dead Code
Unused imports, variables, functions
Unreachable branches
Commented-out code in version control
Remedy: Delete it (git history preserves it if needed)
Data Class
A class that is only fields plus getters/setters, with no meaningful behavior:
A "data bag" other classes reach into and manipulate from outside
Closely related to Anemic Domain Model at the class level
Remedy: Move the behavior that operates on the data into the class ("Tell, Don't Ask")
Lazy Class
A class/module that no longer does enough to justify its existence:
Left over after refactoring, or an abstraction that never grew
Remedy: Inline it into its caller or collapse the hierarchy
Speculative Generality
Abstractions, hooks, parameters, or generics added for hypothetical future needs:
Unused abstract base classes, unused parameters, "just in case" configuration
Violates YAGNI
Remedy: Remove unused abstraction; add it when a real second use case appears
Temporary Field
An instance field that is only set/used in certain circumstances and empty otherwise:
Fields populated only during one algorithm, confusing readers the rest of the time
Remedy: Extract the field + the methods that use it into their own class (Extract Class / introduce a Method Object)
Testing Smells
No Tests
Modules with zero test coverage
Business logic without unit tests
Remedy: Write characterization tests first, then add behavior tests
Test-Implementation Coupling
Tests asserting internal method calls, private state, or implementation details
Tests breaking on refactoring without behavior changes
Remedy: Test through public APIs, assert behavior not implementation
Test Environment Dependency
Tests depending on file system, network, database, system clock without mocking
Non-deterministic tests (flaky tests)
Remedy: Use test doubles, control environment, use DI
Complexity Smells (Algorithmic Anti-Patterns)
Complexity smells indicate code whose runtime grows inefficiently with input size. These are not mere "micro-optimizations" — they are algorithmic choices that cause real performance degradation at scale.
Nested Loops (O(n^2) and Worse)
Two or more loops nested inside each other, producing polynomial complexity.
Detection:for/while inside another for/while; forEach/map inside forEach/map; loop containing another loop (any depth)
Impact: O(n^2) for double-nested, O(n^3) for triple; explodes with moderate data sizes
Remedy:
Build a Map/Set index for the inner collection → O(n+m)
Sort + two-pointer approach → O(n log n)
Group/bucket data before iterating
Sweep-line for interval/range problems
Correctness checks: Does order matter? Are there duplicate keys? Is the original picking first/last/all matches?
N+1 Query Pattern
A database query, API call, or I/O operation inside a loop body.
Detection:fetch()/axios()/query()/execute()/findMany()/findOne()/findUnique()/select()/where() inside any loop construct
Impact: 1 + N round-trips instead of 1; network latency multiplied by item count
Remedy:
Batch fetch by IDs: SELECT * FROM x WHERE id IN (...) then join in memory
Use ORM eager-loading / include / preload / DataLoader
Correctness checks: Don't fetch records the original per-item logic wouldn't authorize; preserve missing-record behavior
Repeated Linear Scan (Missing Index)
Linear search (includes, indexOf, .find, in_array) inside a loop, where a Set/Map would give O(1) lookup.
Detection:.includes() / .indexOf() / .find() / .findIndex() / in_array() / contains() inside a loop body
Impact: O(n*m) instead of O(n+m) — each iteration scans the entire collection
Remedy: Build a Set (for membership) or Map (for key→value lookup) once before the loop
Correctness checks: Does equality semantics change after Set conversion? JavaScript object identity vs. value equality; Python hashability
Sort-in-Loop
Sorting inside a loop body, repeating O(n log n) work unnecessarily.
Detection:.sort() / sorted() / sort() inside any iterative block
Impact: O(k * n log n) instead of O(n log n) — sort repeated k times
Remedy:
Sort once outside the loop
Maintain a heap (PriorityQueue) if incremental top-K is needed
Use binary search/insertion into sorted collection
Correctness checks: Is each intermediate sorted state externally observable? Does comparator depend on loop-local state?
Render-Path Recompute (UI Complexity)
Expensive data transformation (filter→map→sort chains) inside UI component render bodies, recomputed on every render.
Detection:.filter().map().sort().reduce() chains inside React/Vue/Svelte component function bodies; inside function Component() or const Component = () => in JSX/TSX
Impact: Re-derivation on every state change even if inputs unchanged; jank with large collections
Remedy:
useMemo / computed / derived with correct dependency arrays
Move derivation to selectors, loaders, or server-side
Virtualize long lists (windowing)
Stabilize callbacks and object props only when child renders are affected
Correctness checks: Dependency arrays must include every semantic input; memoization must not hide mutations of mutable inputs
Pairwise Comparison
Comparing every element with every other element using double-nested iteration.
Detection: Two nested loops iterating the same or similar collections, comparing pairs
Impact: O(n^2) for pair matching, overlap detection, conflict checking, nearest-neighbor
Remedy:
Sort + two-pointer for pair/range matching
Sweep-line for interval overlaps
Spatial hashing or grid bucketing for proximity
Union-find for connectivity
Correctness checks: Order stability; tie-breaking in equality cases
Unnecessary Recompute (Missing Memoization)
Same pure computation repeated with same inputs without caching.
Detection: Identical function calls with same arguments in hot paths; repeated expensive transforms; recursive calls without memoization
Impact: Linear/polynomial wasted work; especially bad with recursive Fibonacci-style patterns (O(2^n) → O(n) with memo)
Remedy: Add memoization/caching with proper invalidation; use lru_cache/memoize/useMemo as appropriate
Wrong Data Structure
Using a suboptimal data structure for the access pattern.
Detection:
Array/List used for frequent membership tests → should be Set
Array/List used for key-value lookups → should be Map/Object
Array used as queue with shift()/pop(0) (O(n) per dequeue) → should use proper Queue
Sorted insertion into array (O(n) per insert) → should use Heap
Remedy: Replace with the data structure whose complexity matches the access pattern:
Set → O(1) has/add/delete
Map → O(1) get/set
Heap → O(log n) push/pop for priority
Queue/Deque → O(1) enqueue/dequeue
What NOT to Flag
Cold paths: Complexity that only runs on startup, config loading, or tiny N (< 100) is rarely worth fixing
Intentional tradeoffs: Clear, readable O(n) code where O(n log n) would add complexity with no measurable gain
Repeated switch/if-else chains that branch on a type code or enum:
The same conditional structure duplicated in several places
Adding a new type forces editing every switch (OCP violation)
Remedy: Replace conditional with polymorphism (Strategy/State), or Replace Type Code with Subclasses
Refused Bequest
A subclass inherits methods/fields it doesn't need:
Overrides inherited methods to throw, no-op, or do something unrelated
Signals the inheritance relationship is wrong
Remedy: Push down unused members, or replace inheritance with delegation
Alternative Classes with Different Interfaces
Two classes perform the same role but expose differently-named methods:
sort() vs arrange(), getUser() vs fetchUser() for interchangeable classes
Remedy: Unify the interface (rename methods, extract a common superclass/interface)
Incomplete Library Class
A third-party/library class lacks methods you need and can't be modified:
Scattered helper functions or copy-paste wrappers around the library
Remedy: Introduce a Foreign Method or wrap it in an adapter/local extension class
Other refactoring.guru smells are documented in their thematic sections above:
Divergent Change, Data Class, Lazy Class, Speculative Generality,
Temporary Field, Parallel Inheritance Hierarchies, Inappropriate Intimacy,
Message Chains, and Middle Man.
Edge Cases & Fallback
Scenario
Handling
User doesn't specify scope
Default to recent changes (git diff) for repos > 200 files, full analysis otherwise
Project has no clear architecture
Report "Big Ball of Mud" with evidence, recommend incremental refactoring
Empty/monorepo project
Report that architecture analysis requires code; ask user to specify module
Language not supported
Report general structural observations; note language-specific checks are limited
Report file path conflicts
Append -2, -3, etc. to filename
User wants a quick check
Run only Critical-level scans, skip Code and Naming categories
User wants only one category
Focus analysis on that category, skip others
Report Output Example
🔍 Architecture Smell Analysis Complete
Project: goal-workflow
Style: Modular Monolith (with some layering violations)
Files Analyzed: 47
Health: 🟡 Fair
Critical: 3 | Warnings: 6 | Suggestions: 9
🔴 Critical Issues:
1. Anemic Domain Model — `models/` classes have only getters/setters,
all logic in `services/`. Violates DDD Rich Domain Model principle.
2. N+1 Query Pattern — `services/order.ts:142` fetches user per order in loop;
should batch-load users by IDs (O(n*m) → O(n+m)).
3. Static Cling — `util/ApiClient.ts` uses all static methods,
making consumer code untestable.
🟡 Warnings:
1. God Object — `services/workflow.ts` at 847 lines handles too many concerns
2. Nested Loop O(n^2) — `analytics.ts:89` pairwise comparison of events;
sort+two-pointer would be O(n log n)
3. Leaky Abstraction — `repositories/user.ts` exposes MongoDB query syntax
4. Duplicated Code — validation logic duplicated across 4 controllers
5. Circular Dependency — `auth` ↔ `user` modules depend on each other
6. Magic Numbers — ~23 hardcoded values without named constants
Full report: tasks/smell-report-2026-05-27-1530.md