smell
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSmell — Architecture Bad Smell Detector
Smell — 架构坏味道检测器
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.
分析代码库,找出软件架构原则违规、反模式、代码「坏味道」以及算法复杂度热点问题。生成一份全面且可落地的Markdown报告。
知识库: 该技能整合了来自行业研究与实践的架构模式、反模式、代码坏味道以及算法复杂度启发式规则。
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, andgrep(Explore subagent) to gather evidenceAgent - 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
- 明确分析范围 —— 询问用户需要分析项目的哪一部分(完整项目、特定模块或最近变更内容)
- 使用 、
find和grep(Explore子代理)扫描代码库以收集证据Agent - 识别架构坏味道与反模式
- 生成详细的Markdown报告并保存至
tasks/smell-report-[timestamp].md - 向用户呈现发现结果的摘要
Step 1: Scope Clarification
步骤1:范围确认
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.
询问用户:
我应该分析哪些范围?
A. 整个项目(全面分析,可能耗时较长)
B. 特定模块/目录:[请指定]
C. 仅最近变更的文件(git diff)
D. 仅架构层面问题(跳过底层代码坏味道)若用户未指定,小型项目(<100个文件)默认选择选项A,大型项目默认选择选项C。
Step 2: Evidence Gathering
步骤2:证据收集
Use the Explore subagent ( with ) to scan the codebase for architectural patterns and anti-patterns. Run multiple parallel explorations:
Agentsubagent_type: "Explore"使用Explore子代理(的)扫描代码库以识别架构模式与反模式。并行执行多项探索任务:
subagent_type: "Explore"AgentExploration 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.)
- Dependency Analysis: Find import/include patterns, check for circular dependencies, identify coupling hotspots
- Module/Component Scan: Identify God Objects (files > 500 lines), check cohesion, check single responsibility violations
- Pattern Detection: Look for known anti-pattern signatures (static cling, service locator abuse, leaky abstractions)
- Testing Scan: Check test coverage patterns, test file locations, test-to-code ratios
- Naming & Clarity Scan: Flag misleading names, overly generic names (Manager, Helper, Util), inconsistent naming conventions
- Complexity Scan: Detect algorithmic complexity hotspots — nested loops, N+1 queries, repeated scans, sort-in-loop, expensive recomputation in render paths
并行执行以下命令以高效收集证据:
- 项目结构扫描: 映射目录树,识别架构风格(分层架构、模块化单体、微服务等)
- 依赖分析: 查找导入/引用模式,检查循环依赖,识别耦合热点
- 模块/组件扫描: 识别上帝对象(文件行数>500),检查内聚性,检测单一职责原则违规
- 模式检测: 查找已知反模式特征(静态依赖滥用、服务定位器滥用、抽象泄漏)
- 测试扫描: 检查测试覆盖模式、测试文件位置、测试代码比例
- 命名与清晰度扫描: 标记误导性命名、过于通用的名称(Manager、Helper、Util)、不一致的命名规范
- 复杂度扫描: 检测算法复杂度热点 —— 嵌套循环、N+1查询、重复扫描、循环内排序、渲染路径中的昂贵重复计算
Key Heuristics
核心启发式规则
| Category | Smell | Detection Heuristic |
|---|---|---|
| Architecture | Big Ball of Mud | No clear directory structure; everything in root or one flat folder; no separation of concerns |
| Architecture | Violated Layer Boundaries | Inner layers importing outer layers; infrastructure code in domain/core layer |
| Architecture | Missing Architecture | No |
| Architecture | Distributed Monolith | Microservices sharing a database; services that can't deploy independently |
| Architecture | Anemic Domain Model | Model/entity classes with only getters/setters and no behavior; all logic in services |
| Architecture | CQRS Without Need | Separate read/write models for simple CRUD; unnecessary complexity |
| Coupling | Circular Dependencies | Module A imports B, B imports A; detected via import graph analysis |
| Coupling | Content Coupling | One module directly accesses another's internal/private members |
| Coupling | Common Coupling | Excessive global variables/shared mutable state; singleton abuse |
| Coupling | Stamp Coupling | Passing large data structures when only a few fields are needed |
| Cohesion | God Object | Single class/module > 500 lines; > 20 public methods; handles unrelated concerns |
| Cohesion | Shotgun Surgery | A single change requires touching 5+ files across unrelated modules |
| Cohesion | Feature Envy | Method calls foreign class methods more than its own class methods |
| Cohesion | Data Clumps | Same group of 3+ parameters appearing together in multiple method signatures |
| Design | Leaky Abstractions | Implementation details (DB queries, HTTP calls) exposed through interfaces |
| Design | Static Cling | Excessive use of static methods; static state that prevents testability |
| Design | Service Locator Abuse | DI container passed around instead of proper constructor injection |
| Design | Violated SOLID | SRP violations, OCP violations (switch/if-else chains on types), ISP violations (fat interfaces) |
| Code | Duplicated Code | Identical/similar logic appearing in 3+ places; copy-paste patterns |
| Code | Long Method | Methods > 50 lines; deep nesting (> 3 levels) |
| Code | Long Parameter List | Methods with > 4 parameters |
| Code | Primitive Obsession | Using strings/ints instead of domain types (e.g., |
| Code | Magic Numbers/Strings | Hardcoded literals without named constants |
| Code | Comments as Deodorant | Excessive comments explaining bad code instead of refactoring |
| Code | Dead Code | Unused imports, unreachable code, commented-out blocks |
| Testing | No Tests | Modules with zero test coverage |
| Testing | Test-Implementation Coupling | Tests that assert internal implementation details instead of behavior |
| Testing | Slow Tests | Tests doing real I/O, database calls, network requests without mocking |
| Naming | Vague Names | |
| Naming | Inconsistent Naming | Snake_case and camelCase mixed; different patterns for same concept |
| Complexity | Nested Loops (O(n^2)+) | Loop inside loop; forEach inside for; map inside map; nested iteration suggesting polynomial complexity |
| Complexity | Repeated Linear Scan | |
| Complexity | Sort-in-Loop | |
| Complexity | N+1 Query Pattern | Database/API/HTTP call inside a loop; |
| Complexity | Render-Path Recompute | |
| Complexity | Pairwise Comparison | Nested iteration comparing every element with every other; O(n^2) when sort+two-pointer would be O(n log n) |
| Complexity | Unnecessary Recompute | Same expensive computation repeated without caching; missing |
| Complexity | Wrong Data Structure | Array used where Set/Map would give O(1) lookup; List where Queue/Heap/Stack is natural fit |
| 分类 | 坏味道 | 检测规则 |
|---|---|---|
| 架构 | 大泥球架构 | 无清晰目录结构;所有内容都在根目录或单一扁平文件夹;无关注点分离 |
| 架构 | 层级边界违规 | 内层导入外层;领域/核心层包含基础设施代码 |
| 架构 | 缺失架构设计 | 无 |
| 架构 | 分布式单体 | 微服务共享数据库;服务无法独立部署 |
| 架构 | 贫血领域模型 | 模型/实体类仅包含getter/setter,无业务行为;所有逻辑都在服务层 |
| 架构 | 不必要的CQRS | 简单CRUD场景使用分离的读写模型;引入不必要的复杂度 |
| 耦合 | 循环依赖 | 模块A导入B,B导入A;通过导入图分析检测 |
| 耦合 | 内容耦合 | 一个模块直接访问另一个模块的内部/私有成员 |
| 耦合 | 公共耦合 | 过度使用全局变量/共享可变状态;单例滥用 |
| 耦合 | 标记耦合 | 传递大型数据结构,但仅需其中少数字段 |
| 内聚 | 上帝对象 | 单个类/模块行数>500;>20个公共方法;处理不相关的关注点 |
| 内聚 | 霰弹式修改 | 单次变更需要修改5个以上不同模块的文件 |
| 内聚 | 特性依恋 | 方法调用外部类的次数多于自身类的方法 |
| 内聚 | 数据泥团 | 同一组3个以上参数多次出现在方法签名中 |
| 设计 | 抽象泄漏 | 接口暴露实现细节(数据库查询、HTTP调用) |
| 设计 | 静态依赖滥用 | 过度使用静态方法;静态状态导致无法测试 |
| 设计 | 服务定位器滥用 | 传递DI容器而非使用构造函数注入 |
| 设计 | SOLID原则违规 | SRP违规、OCP违规(基于类型的switch/if-else链)、ISP违规(臃肿接口) |
| 代码 | 重复代码 | 相同/相似逻辑出现在3个以上位置;复制粘贴模式 |
| 代码 | 过长方法 | 方法行数>50;深层嵌套(>3层) |
| 代码 | 过长参数列表 | 方法参数>4个 |
| 代码 | 原始类型痴迷 | 使用字符串/整数替代领域类型(例如用 |
| 代码 | 魔法数字/字符串 | 未使用命名常量的硬编码字面量 |
| 代码 | 注释掩盖坏味道 | 过多注释解释糟糕代码而非重构 |
| 代码 | 死代码 | 未使用的导入、不可达代码、注释掉的代码块 |
| 测试 | 无测试覆盖 | 模块测试覆盖率为0 |
| 测试 | 测试与实现耦合 | 测试断言内部实现细节而非行为 |
| 测试 | 缓慢测试 | 测试执行真实I/O、数据库调用、网络请求而未做Mock |
| 命名 | 模糊命名 | 过度使用无上下文的 |
| 命名 | 命名不一致 | 混合使用Snake_case与camelCase;同一概念使用不同命名模式 |
| 复杂度 | 嵌套循环(O(n^2)+) | 循环嵌套;forEach嵌套forEach;map嵌套map;嵌套迭代表明多项式复杂度 |
| 复杂度 | 重复线性扫描 | 循环内使用 |
| 复杂度 | 循环内排序 | 迭代代码中调用 |
| 复杂度 | N+1查询模式 | 循环体内执行数据库/API/HTTP调用;每次迭代执行 |
| 复杂度 | 渲染路径重复计算 | 组件渲染体内执行 |
| 复杂度 | 两两比较 | 嵌套迭代比较每一对元素;本可使用排序+双指针实现O(n log n),却采用O(n^2)实现 |
| 复杂度 | 不必要的重复计算 | 相同昂贵计算重复执行而无缓存;缺失 |
| 复杂度 | 错误的数据结构 | 使用Array而非Set/Map实现O(1)查找;使用List而非Queue/Heap/Stack |
Step 3: Report Generation
步骤3:报告生成
Generate the report in this structure:
markdown
undefined按照以下结构生成报告:
markdown
undefinedArchitecture Smell Report
架构坏味道报告
Project: [project-name]
Scope: [scope description]
Date: [date]
Analyzer: smell skill (Ducc)
项目: [项目名称]
范围: [范围描述]
日期: [日期]
分析工具: smell技能(Ducc)
Executive Summary
执行摘要
[2-3 paragraph summary: architectural style detected, overall health assessment, and top 3-5 critical issues]
[2-3段摘要:检测到的架构风格、整体健康评估、前3-5个关键问题]
Architectural Style Detected
检测到的架构风格
[Identify the architectural style: Layered, Modular Monolith, Microservices, Hexagonal, Clean Architecture, or Big Ball of Mud]
[识别架构风格:分层、模块化单体、微服务、六边形架构、整洁架构或大泥球架构]
Style Expectations vs. Reality
风格预期 vs 实际情况
| Expectation | Reality | Status |
|---|---|---|
| [e.g., Clear layer separation] | [what was found] | ✅/⚠️/🔴 |
| 预期 | 实际 | 状态 |
|---|---|---|
| [例如:清晰的层级分离] | [发现的情况] | ✅/⚠️/🔴 |
Findings by Category
按分类整理的发现结果
🔴 Critical Issues (Must Fix)
🔴 关键问题(必须修复)
[Issues that fundamentally undermine architecture]
[从根本上破坏架构的问题]
🟡 Warnings (Should Fix)
🟡 警告(应该修复)
[Issues that degrade maintainability but don't block function]
[降低可维护性但不影响功能的问题]
🔵 Suggestions (Nice to Fix)
🔵 建议(值得修复)
[Minor improvements that would increase quality]
[提升代码质量的微小改进]
Detailed Findings
详细发现
Finding #1: [Title]
发现#1:[标题]
- Category: [Architecture/Coupling/Cohesion/Design/Code/Testing/Naming/Complexity]
- Severity: 🔴 Critical / 🟡 Warning / 🔵 Suggestion
- Anti-Pattern: [Name of anti-pattern]
- Location: [file:line references]
- Principle Violated: [SOLID/DRY/KISS/etc.]
- Description: [What was found and why it's a problem]
- Evidence: [Code snippet or structure description]
- Recommendation: [Specific fix, with refactoring approach]
- 分类: [架构/耦合/内聚/设计/代码/测试/命名/复杂度]
- 严重程度: 🔴 关键 / 🟡 警告 / 🔵 建议
- 反模式: [反模式名称]
- 位置: [文件:行号引用]
- 违反原则: [SOLID/DRY/KISS等]
- 描述: [发现的问题及其影响]
- 证据: [代码片段或结构描述]
- 建议: [具体修复方案及重构方法]
Dependency Graph Analysis
依赖图分析
[Summary of module dependencies, circular dependencies found, coupling hotspots]
[模块依赖摘要、发现的循环依赖、耦合热点]
Module Health Scorecard
模块健康评分卡
| Module | Lines | God Object Risk | Coupling | Cohesion | Test Coverage | Health |
|---|---|---|---|---|---|---|
| [name] | [N] | [Low/Med/High] | [Low/Med/High] | [Low/Med/High] | [% or N/A] | 🟢/🟡/🔴 |
| 模块 | 行数 | 上帝对象风险 | 耦合度 | 内聚度 | 测试覆盖率 | 健康状态 |
|---|---|---|---|---|---|---|
| [名称] | [N] | [低/中/高] | [低/中/高] | [低/中/高] | [% 或 N/A] | 🟢/🟡/🔴 |
Smell Distribution
坏味道分布
| Category | Count | Critical | Warning | Suggestion |
|---|---|---|---|---|
| Architecture | [N] | [N] | [N] | [N] |
| Coupling | [N] | [N] | [N] | [N] |
| Cohesion | [N] | [N] | [N] | [N] |
| Design | [N] | [N] | [N] | [N] |
| Code | [N] | [N] | [N] | [N] |
| Testing | [N] | [N] | [N] | [N] |
| Naming | [N] | [N] | [N] | [N] |
| Complexity | [N] | [N] | [N] | [N] |
| 分类 | 数量 | 关键 | 警告 | 建议 |
|---|---|---|---|---|
| 架构 | [N] | [N] | [N] | [N] |
| 耦合 | [N] | [N] | [N] | [N] |
| 内聚 | [N] | [N] | [N] | [N] |
| 设计 | [N] | [N] | [N] | [N] |
| 代码 | [N] | [N] | [N] | [N] |
| 测试 | [N] | [N] | [N] | [N] |
| 命名 | [N] | [N] | [N] | [N] |
| 复杂度 | [N] | [N] | [N] | [N] |
Refactoring Roadmap
重构路线图
Immediate Actions (This Sprint)
立即行动(当前迭代)
- [Actionable fix 1]
- [Actionable fix 2]
- [可落地的修复方案1]
- [可落地的修复方案2]
Short-Term (1-3 Months)
短期(1-3个月)
- [Structural improvement 1]
- [Structural improvement 2]
- [结构改进方案1]
- [结构改进方案2]
Long-Term (3-12 Months)
长期(3-12个月)
- [Architectural transformation 1]
- [Architectural transformation 2]
- [架构转型方案1]
- [架构转型方案2]
Appendix: Anti-Pattern Reference
附录:反模式参考
[A condensed reference of anti-patterns checked, with brief descriptions]
---[检查过的反模式浓缩参考及简要描述]
---Step 4: Save and Present
步骤4:保存与呈现
Save the report to and present a brief summary to the user.
tasks/smell-report-[YYYY-MM-DD-HHmm].md将报告保存至 ,并向用户呈现简要摘要。
tasks/smell-report-[YYYY-MM-DD-HHmm].mdAnti-Pattern Knowledge Base
反模式知识库
This section documents the architectural anti-patterns and bad smells the skill knows about.
本节记录该技能识别的架构反模式与坏味道。
Architectural Anti-Patterns
架构反模式
Big Ball of Mud
大泥球架构
The most common de-facto architecture. A haphazardly structured, sprawling system with no perceivable architecture. Characterized by:
- Promiscuous sharing of information between distant elements
- Global or duplicated important state
- Structure eroded beyond recognition or never defined
- Repeated expedient repair ("duct tape and bailing wire")
- Forces: Time pressure, cost, inexperience, complexity, change, scale
- Remedy: Define architecture boundaries, refactor incrementally, apply SHEARING LAYERS, KEEP IT WORKING
最常见的实际架构。结构混乱、无明确架构的庞大系统。特征:
- 不同模块间随意共享信息
- 全局或重复的重要状态
- 架构被破坏或从未定义
- 反复临时修复(「胶带固定」)
- 诱因:时间压力、成本、经验不足、复杂度、变更、规模
- 修复方案: 定义架构边界、逐步重构、应用分层策略、保持系统可用
Distributed Monolith
分布式单体
Microservices that must be deployed together. Symptoms:
- Services share a database
- Synchronous chains of service calls
- Changes require coordinated deployments
- Remedy: Decouple data stores, introduce async messaging, enforce bounded contexts
必须一起部署的微服务。症状:
- 服务共享数据库
- 同步链式服务调用
- 变更需要协调部署
- 修复方案: 解耦数据存储、引入异步消息、实现限界上下文
Anemic Domain Model
贫血领域模型
Domain objects with only getters/setters (data bags), all logic in services. Violates:
- "Tell, Don't Ask" principle
- Rich Domain Model pattern from DDD
- Remedy: Move behavior into domain objects, use domain services only for cross-aggregate operations
仅包含getter/setter的领域对象(数据袋),所有逻辑在服务层。违反:
- 「Tell, Don't Ask」原则
- DDD中的富领域模型模式
- 修复方案: 将行为移至领域对象,仅在跨聚合操作时使用领域服务
God Object
上帝对象
A class that knows too much or does too much. Characteristics:
-
500 lines or > 20 public methods
- Handles unrelated concerns
- Difficult to test in isolation
- Single Responsibility Principle violation
- Remedy: Extract cohesive groups of methods into dedicated classes
知晓过多或承担过多职责的类。特征:
- 行数>500或>20个公共方法
- 处理不相关的关注点
- 难以独立测试
- 违反单一职责原则
- 修复方案: 将内聚的方法组提取到专用类中
Leaky Abstractions
抽象泄漏
Abstractions that expose implementation details. Signs:
- Interface methods named after implementation (e.g., ,
SaveToPostgres)FetchFromRedis - Consumers catching implementation-specific exceptions
- Configuration details exposed through abstractions
- Remedy: Design interfaces from the consumer's perspective, hide implementation details
暴露实现细节的抽象。迹象:
- 接口方法以实现命名(例如、
SaveToPostgres)FetchFromRedis - 消费者捕获特定于实现的异常
- 配置细节通过抽象暴露
- 修复方案: 从消费者视角设计接口,隐藏实现细节
Static Cling
静态依赖滥用
Excessive use of static methods/state. Problems:
- Untestable (can't mock static calls)
- Hidden dependencies
- Thread-safety issues with static state
- Remedy: Use dependency injection, convert stateless statics to instance methods
过度使用静态方法/状态。问题:
- 无法测试(无法Mock静态调用)
- 隐藏依赖
- 静态状态的线程安全问题
- 修复方案: 使用依赖注入,将无状态静态方法转换为实例方法
Service Locator Abuse
服务定位器滥用
Using a service locator instead of dependency injection. Issues:
- Hidden dependencies (dependencies not visible in constructor)
- Runtime errors instead of compile-time errors
- Testing difficulty
- Remedy: Use constructor injection, register dependencies at composition root
使用服务定位器而非依赖注入。问题:
- 隐藏依赖(构造函数中不可见)
- 运行时错误而非编译时错误
- 测试难度大
- 修复方案: 使用构造函数注入,在组合根注册依赖
Violated Layer Boundaries (Clean/Onion/Hexagonal Architecture)
层级边界违规(整洁/洋葱/六边形架构)
In layered architectures:
- Clean Architecture: Outer layers (frameworks) leaking into inner layers (use cases, entities)
- Onion Architecture: Infrastructure concerns in domain core
- Hexagonal Architecture: Business logic coupled to specific adapters instead of ports
- Remedy: Apply dependency inversion, define clear port interfaces
分层架构中:
- 整洁架构: 外层(框架)泄漏到内层(用例、实体)
- 洋葱架构: 领域核心包含基础设施逻辑
- 六边形架构: 业务逻辑与特定适配器耦合而非端口
- 修复方案: 应用依赖倒置原则,定义清晰的端口接口
CQRS Overuse
CQRS过度使用
Applying CQRS to simple CRUD. Signs:
- Separate read/write models for trivial data access
- Event sourcing when events don't add business value
- Unnecessary complexity
- Remedy: Use CQRS only when read/write models genuinely differ or have different scaling needs
将CQRS应用于简单CRUD场景。迹象:
- 简单数据访问使用分离的读写模型
- 事件溯源未带来业务价值
- 不必要的复杂度
- 修复方案: 仅在读写模型确实不同或有不同扩展需求时使用CQRS
Vertical Slice Contamination
垂直切片污染
In Vertical Slice Architecture:
- Cross-slice coupling (one feature directly calling another)
- Shared service classes undermining slice independence
- Remedy: Use events/messages for cross-slice communication, duplicate simple logic if needed
垂直切片架构中:
- 跨切片耦合(一个功能直接调用另一个功能)
- 共享服务类破坏切片独立性
- 修复方案: 使用事件/消息进行跨切片通信,必要时复制简单逻辑
Coupling & Cohesion Smells
耦合与内聚坏味道
Circular Dependencies
循环依赖
Module A → Module B → Module A. Detected via:
- Import graph analysis
- "Cannot access before initialization" errors
- Remedy: Extract shared interface/common module, apply dependency inversion
模块A → 模块B → 模块A。检测方式:
- 导入图分析
- 「Cannot access before initialization」错误
- 修复方案: 提取共享接口/公共模块,应用依赖倒置
Content Coupling
内容耦合
One module directly modifying another's internal state. Signs:
- Direct field access across module boundaries
- /package-private abuse
friend - Remedy: Use public APIs, encapsulate internal state
一个模块直接修改另一个模块的内部状态。迹象:
- 跨模块边界直接访问字段
- /包私有滥用
friend - 修复方案: 使用公共API,封装内部状态
Common Coupling (Global State)
公共耦合(全局状态)
Multiple modules depending on shared global mutable state:
- Global variables, singletons with mutable state
- Ambient context (e.g., static property)
CurrentUser - Remedy: Parameterize, use dependency injection, make state explicit
多个模块依赖共享的全局可变状态:
- 全局变量、带可变状态的单例
- 环境上下文(例如静态属性)
CurrentUser - 修复方案: 参数化、使用依赖注入、使状态显式化
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)
传递整个数据结构但仅需少数字段:
- 函数接收大型DTO但仅使用其中一个字段
- 修复方案: 创建聚焦的参数或更小的接口(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
单次变更需要修改多个文件:
- 添加一个字段需要修改5个以上不同模块的文件
- 修复方案: 整合相关行为,应用单一职责原则
Feature Envy
特性依恋
A method that uses another class's methods more than its own:
- Method calls ,
other.foo(),other.bar()with few self-callsother.baz() - Remedy: Move the method to the class it envies
方法调用外部类的次数多于自身类:
- 方法多次调用、
other.foo()、other.bar(),很少调用自身方法other.baz() - 修复方案: 将该方法移至它依赖的类中
Data Clumps
数据泥团
Same group of fields appearing together in multiple places:
- appearing in 5 method signatures
(street, city, zip) - Remedy: Extract into a value object
同一组字段多次出现在不同位置:
- 出现在5个方法签名中
(street, city, zip) - 修复方案: 提取为值对象
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
- 方法行数>50(或根据语言调整)
- 深层嵌套>3层
- 混合多个抽象层级
- 修复方案: 提取同一抽象层级的方法,进行组合
Long Parameter List
过长参数列表
- Methods with > 4 parameters
- Boolean flags controlling behavior
- Remedy: Introduce parameter object, split method, remove flag arguments
- 方法参数>4个
- 控制行为的布尔标志
- 修复方案: 引入参数对象、拆分方法、移除标志参数
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
- 相同或几乎相同的逻辑出现在3个以上位置
- 复制粘贴并略有修改
- 修复方案: 提取共享方法,应用模板方法或策略模式
Primitive Obsession
原始类型痴迷
Using primitives instead of domain types:
- for Email, PhoneNumber, URL
string - for Money, Age, Quantity
int - without Currency context
decimal - Remedy: Create value objects with validation and behavior
使用原始类型替代领域类型:
- 用表示Email、PhoneNumber、URL
string - 用表示Money、Age、Quantity
int - 无货币上下文的
decimal - 修复方案: 创建带验证和行为的值对象
Magic Numbers/Strings
魔法数字/字符串
- Hardcoded literals without explanation
- instead of
if (status == 3)if (status == Status.COMPLETED) - Remedy: Extract named constants or enums
- 无解释的硬编码字面量
- 使用而非
if (status == 3)if (status == Status.COMPLETED) - 修复方案: 提取为命名常量或枚举
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
- 注释解释代码功能(代码应自文档化)
- 注释掉的代码块
- 未解决的「TODO」注释堆积
- 修复方案: 重构代码使其清晰、删除死代码、将TODO作为问题跟踪
Dead Code
死代码
- Unused imports, variables, functions
- Unreachable branches
- Commented-out code in version control
- Remedy: Delete it (git history preserves it if needed)
- 未使用的导入、变量、函数
- 不可达分支
- 版本控制中注释掉的代码
- 修复方案: 删除(git历史会保留必要内容)
Testing Smells
测试坏味道
No Tests
无测试覆盖
- Modules with zero test coverage
- Business logic without unit tests
- Remedy: Write characterization tests first, then add behavior tests
- 模块测试覆盖率为0
- 业务逻辑无单元测试
- 修复方案: 先编写特征测试,再添加行为测试
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
- 测试断言内部方法调用、私有状态或实现细节
- 重构未改变行为但测试失败
- 修复方案: 通过公共API测试,断言行为而非实现
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
- 测试依赖文件系统、网络、数据库、系统时钟而未Mock
- 非确定性测试(不稳定测试)
- 修复方案: 使用测试替身、控制环境、使用依赖注入
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)
嵌套循环(O(n^2)及更差)
Two or more loops nested inside each other, producing polynomial complexity.
- Detection: /
forinside anotherwhile/for;while/forEachinsidemap/forEach; loop containing another loop (any depth)map - 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?
两个或多个循环嵌套,产生多项式复杂度。
- 检测: /
for嵌套;while/forEach嵌套;循环包含另一个循环(任意深度)map - 影响: 双嵌套为O(n^2),三嵌套为O(n^3);中等数据规模下性能急剧下降
- 修复方案:
- 为内部集合构建Map/Set索引 → O(n+m)
- 排序+双指针方法 → O(n log n)
- 迭代前分组/分桶数据
- 区间/范围问题使用扫描线算法
- 正确性检查: 顺序是否重要?是否有重复键?原逻辑是否选择第一个/最后一个/所有匹配项?
N+1 Query Pattern
N+1查询模式
A database query, API call, or I/O operation inside a loop body.
- Detection: /
fetch()/axios()/query()/execute()/findMany()/findOne()/findUnique()/select()inside any loop constructwhere() - Impact: 1 + N round-trips instead of 1; network latency multiplied by item count
- Remedy:
- Batch fetch by IDs: then join in memory
SELECT * FROM x WHERE id IN (...) - Use ORM eager-loading / /
include/ DataLoaderpreload - Bulk API endpoints accepting arrays
- Preserve: auth filters, tenancy isolation, ordering, pagination, error semantics
- Batch fetch by IDs:
- Correctness checks: Don't fetch records the original per-item logic wouldn't authorize; preserve missing-record behavior
循环体内执行数据库查询、API调用或I/O操作。
- 检测: 循环结构内使用/
fetch()/axios()/query()/execute()/findMany()/findOne()/findUnique()/select()where() - 影响: 1+N次往返而非1次;网络延迟乘以项数
- 修复方案:
- 按ID批量获取:然后在内存中关联
SELECT * FROM x WHERE id IN (...) - 使用ORM预加载 / /
include/ DataLoaderpreload - 接受数组的批量API端点
- 保留:权限过滤、租户隔离、排序、分页、错误语义
- 按ID批量获取:
- 正确性检查: 不要获取原单一项逻辑不会授权的记录;保留缺失记录的行为
Repeated Linear Scan (Missing Index)
重复线性扫描(缺少索引)
Linear search (, , , ) inside a loop, where a Set/Map would give O(1) lookup.
includesindexOf.findin_array- Detection: /
.includes()/.indexOf()/.find()/.findIndex()/in_array()inside a loop bodycontains() - Impact: O(n*m) instead of O(n+m) — each iteration scans the entire collection
- Remedy: Build a (for membership) or
Set(for key→value lookup) once before the loopMap - Correctness checks: Does equality semantics change after Set conversion? JavaScript object identity vs. value equality; Python hashability
循环内使用线性搜索(、、、),而Set/Map可实现O(1)查找。
includesindexOf.findin_array- 检测: 循环体内使用/
.includes()/.indexOf()/.find()/.findIndex()/in_array()contains() - 影响: O(n*m)而非O(n+m) —— 每次迭代扫描整个集合
- 修复方案: 循环前构建(用于成员检查)或
Set(用于键值查找)Map - 正确性检查: 转换为Set后相等语义是否改变?JavaScript对象标识 vs 值相等;Python可哈希性
Sort-in-Loop
循环内排序
Sorting inside a loop body, repeating O(n log n) work unnecessarily.
- Detection: /
.sort()/sorted()inside any iterative blocksort() - 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?
循环体内排序,重复执行不必要的O(n log n)操作。
- 检测: 迭代块内使用/
.sort()/sorted()sort() - 影响: O(k * n log n)而非O(n log n) —— 排序重复k次
- 修复方案:
- 循环外排序一次
- 若需要增量Top-K则维护堆(PriorityQueue)
- 使用二分查找/插入有序集合
- 正确性检查: 每个中间排序状态是否可外部观察?比较器是否依赖循环局部状态?
Render-Path Recompute (UI Complexity)
渲染路径重复计算(UI复杂度)
Expensive data transformation (filter→map→sort chains) inside UI component render bodies, recomputed on every render.
- Detection: chains inside React/Vue/Svelte component function bodies; inside
.filter().map().sort().reduce()orfunction Component()in JSX/TSXconst Component = () => - Impact: Re-derivation on every state change even if inputs unchanged; jank with large collections
- Remedy:
- /
useMemo/computedwith correct dependency arraysderived - 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
UI组件渲染体内执行昂贵的数据转换(filter→map→sort链式操作),每次渲染都重新计算。
- 检测: React/Vue/Svelte组件函数体内使用链式操作;JSX/TSX中的
.filter().map().sort().reduce()或function Component()内const Component = () => - 影响: 即使输入未变更,每次状态变更都重新推导;大型集合导致卡顿
- 修复方案:
- 使用/
useMemo/computed并配置正确的依赖数组derived - 将推导移至选择器、加载器或服务端
- 虚拟长列表(窗口化)
- 仅在影响子组件渲染时稳定回调和对象属性
- 使用
- 正确性检查: 依赖数组必须包含所有语义输入;缓存不能隐藏可变输入的突变
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
使用双重嵌套迭代比较每一对元素。
- 检测: 两个嵌套迭代遍历相同或相似集合,比较元素对
- 影响: 配对匹配、重叠检测、冲突检查、最近邻等场景为O(n^2)
- 修复方案:
- 排序+双指针进行配对/范围匹配
- 区间重叠使用扫描线算法
- 邻近性检测使用空间哈希或网格分桶
- 连通性检测使用并查集
- 正确性检查: 顺序稳定性;相等情况的平局处理
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/memoizeas appropriateuseMemo
相同纯计算使用相同输入重复执行而无缓存。
- 检测: 热点路径中相同参数的重复函数调用;重复的昂贵转换;无缓存的递归调用
- 影响: 线性/多项式的无用计算;递归斐波那契式模式尤其糟糕(O(2^n) → 缓存后O(n))
- 修复方案: 添加带正确失效策略的缓存;适当使用/
lru_cache/memoizeuseMemo
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()(O(n) per dequeue) → should use proper Queuepop(0) - 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
针对访问模式使用次优的数据结构。
- 检测:
- 使用Array/List进行频繁成员检查 → 应使用Set
- 使用Array/List进行键值查找 → 应使用Map/Object
- 使用Array作为队列并调用/
shift()(每次出队O(n)) → 应使用专用Queuepop(0) - 使用Array进行有序插入(每次插入O(n)) → 应使用Heap
- 修复方案: 替换为复杂度匹配访问模式的数据结构:
- Set → O(1)哈希/添加/删除
- Map → O(1)获取/设置
- Heap → O(log n)入队/出队(优先队列)
- Queue/Deque → O(1)入队/出队
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
- Already optimized: Map/Set already in use; batch loading already implemented; memoization already present
- 冷路径: 仅在启动、配置加载或小数据量(<100)时运行的复杂度问题,通常无需修复
- 有意的权衡: 清晰可读的O(n)代码,使用O(n log n)会增加复杂度但无明显收益
- 已优化: 已使用Map/Set;已实现批量加载;已添加缓存
Design Principle Violations
设计原则违规
SOLID Violations Checklist
SOLID原则违规检查清单
- S (SRP): Class/module has multiple reasons to change → God Object smell
- O (OCP): switch/if-else chains on type codes → Strategy/Polymorphism needed
- L (LSP): Subclass changes behavior of base class unexpectedly → Check pre/post conditions
- I (ISP): Fat interfaces with methods clients don't use → Split interfaces
- D (DIP): High-level modules depending on low-level details → Introduce abstractions
- S(SRP): 类/模块有多个变更原因 → 上帝对象坏味道
- O(OCP): 基于类型码的switch/if-else链 → 需要策略/多态
- L(LSP): 子类意外改变基类行为 → 检查前置/后置条件
- I(ISP): 客户端不使用的臃肿接口 → 拆分接口
- D(DIP): 高层模块依赖低层细节 → 引入抽象
Other Principle Violations
其他原则违规
- DRY Violation: Same knowledge repeated in multiple places
- KISS Violation: Over-engineered solutions; premature abstractions
- YAGNI Violation: Code for hypothetical future requirements; unused abstractions
- DRY违规: 同一知识在多个地方重复
- KISS违规: 过度设计的解决方案;过早抽象
- YAGNI违规: 为假设的未来需求编写代码;未使用的抽象
Edge Cases & Fallback
边缘情况与回退策略
| Scenario | Handling |
|---|---|
| User doesn't specify scope | Default to recent changes ( |
| 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 |
| 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 |
| 场景 | 处理方式 |
|---|---|
| 用户未指定范围 | 仓库文件>200个时默认最近变更( |
| 项目无清晰架构 | 报告「大泥球架构」并提供证据,建议逐步重构 |
| 空项目/单体仓库 | 报告架构分析需要代码;请用户指定模块 |
| 不支持的语言 | 报告通用结构观察结果;说明语言特定检查受限 |
| 报告文件路径冲突 | 文件名后追加 |
| 用户需要快速检查 | 仅运行关键级扫描,跳过代码和命名分类 |
| 用户仅需分析一个分类 | 聚焦该分类分析,跳过其他分类 |
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🔍 架构坏味道分析完成
项目:goal-workflow
风格:模块化单体(存在部分层级违规)
分析文件数:47
健康状态:🟡 一般
关键问题:3 | 警告:6 | 建议:9
🔴 关键问题:
1. 贫血领域模型 —— `models/`类仅包含getters/setters,
所有逻辑在`services/`中。违反DDD富领域模型原则。
2. N+1查询模式 —— `services/order.ts:142`在循环中逐个获取用户;
应按ID批量加载用户(O(n*m) → O(n+m))。
3. 静态依赖滥用 —— `util/ApiClient.ts`全部使用静态方法,
导致消费代码无法测试。
🟡 警告:
1. 上帝对象 —— `services/workflow.ts`有847行,承担过多职责
2. 嵌套循环O(n^2) —— `analytics.ts:89`中事件两两比较;
排序+双指针可实现O(n log n)
3. 抽象泄漏 —— `repositories/user.ts`暴露MongoDB查询语法
4. 重复代码 —— 验证逻辑在4个控制器中重复
5. 循环依赖 —— `auth` ↔ `user`模块相互依赖
6. 魔法数字 —— 约23个硬编码值未使用命名常量
完整报告:tasks/smell-report-2026-05-27-1530.md