寻找你代码中的臭味:一个让 AI 帮你嗅出架构腐化的开源 Skill
本机暂存
<p>你有没有过这样的经历:接手一个"跑了三年没人敢动"的项目,打开代码仓库一看——</p><p><code>src/</code> 下面 200 多个文件平铺在一个目录里,没有分层,没有模块边界。一个叫 <code>UserService</code> 的类 1800 行,发邮件、对接支付、状态管理全塞在里面,还挂着三个 <code>TODO</code> 标着"后面要重构"。业务逻辑全堆在 Service 层,Model 类只剩 getter 和 setter,贫血得像张纸。数据库查询藏在 for 循环里,每循环一次发一条 SQL。你问老员工这模块谁负责,得到一句:"这个……已经没人记得了。"</p><p>Martin Fowler 把这类问题叫做"代码坏味道"(Code Smell)。Brian Foote 和 Joseph Yoder 在 1997 年的论文里给了一个更直白的名字:Big Ball of Mud(大泥球)。</p><span id="more"></span><p>祖传的手搓代码是这样。到了 vibe coding 时代,AI 生成的代码也没好到哪去,能力差一些的模型甚至更糟糕。</p><p>闻到臭味容易,定位臭源难。更难的是知道该先处理哪个、怎么处理。</p><p>今天介绍一个叫 <code>/smell</code> 的开源 Skill,专门干这件事。</p><img src="/2026/06/11/code-smell-detector-ai-skill/image-20260528054646357.png" class=""><blockquote><p>它不是为你的新项目设计架构,而是对你已有的项目的分析架构,找到不合理的反模式的设计。</p></blockquote><h2 id="它是干什么的"><a href="#它是干什么的" class="headerlink" title="它是干什么的"></a>它是干什么的</h2><p><code>/smell</code> 是一个可以嵌入 Claude Code、Codex、Cursor 等 AI 编程 Agent 的 Skill。在项目里输入 <code>/smell</code> 或"帮我找找代码里的坏味道",它会扫描代码库,从架构层面到代码层面做一轮完整检测,最后输出一份 Markdown 报告。</p><p>报告精确到文件和行号:哪个模块是 God Object,哪里有循环依赖,哪段代码是 N+1 查询,哪个类违反了单一职责。每个问题都附带代码证据和重构建议。</p><p>知识库整合了软件架构领域几十年的积累:</p><ul><li>Martin Fowler 的《重构》和《分析模式》</li><li>Robert C. Martin 的 SOLID 原则</li><li>Eric Evans 的领域驱动设计(DDD)</li><li>Foote & Yoder 的 Big Ball of Mud 论文</li><li>Clean Architecture、Onion Architecture、Hexagonal Architecture 等主流架构风格</li><li>一套算法复杂度反模式检测规则</li></ul><p>相当于把一个资深架构师十几年攒下的"闻臭味"经验,塞进了一个命令里。</p><h2 id="八个检测维度"><a href="#八个检测维度" class="headerlink" title="八个检测维度"></a>八个检测维度</h2><img src="/2026/06/11/code-smell-detector-ai-skill/image-20260528055245726.png" class=""><h3 id="1-架构级反模式"><a href="#1-架构级反模式" class="headerlink" title="1. 架构级反模式"></a>1. 架构级反模式</h3><p>最严重的层级。Big Ball of Mud(根本没有架构)、Distributed Monolith(声称是微服务,改一个功能得同时部署五个)、Anemic Domain Model(贫血模型,逻辑全在 Service 层,Domain 对象只剩 getter/setter)、Violated Layer Boundaries(层级穿透,领域层直接 import 基础设施层)。</p><h3 id="2-耦合问题"><a href="#2-耦合问题" class="headerlink" title="2. 耦合问题"></a>2. 耦合问题</h3><p>循环依赖(A import B,B 又 import A)、Content Coupling(直接改另一个模块的内部状态)、Common Coupling(到处用的全局变量和单例)、Stamp Coupling(传一整个大对象进去只用两个字段)。</p><h3 id="3-内聚性问题"><a href="#3-内聚性问题" class="headerlink" title="3. 内聚性问题"></a>3. 内聚性问题</h3><p>God Object(一个类 800 行、30 个公开方法,管了十件不相关的事)、Shotgun Surgery(改一个需求要动 7 个文件)、Feature Envy(一个方法调别人家方法比调自己的还多)、Data Clumps(同样的参数组合反复出现在不同函数签名里)。</p><h3 id="4-设计原则违反"><a href="#4-设计原则违反" class="headerlink" title="4. 设计原则违反"></a>4. 设计原则违反</h3><p>SOLID 原则的各种违反场景、DRY 重复代码、KISS 过度工程化、YAGNI 为假设的未来需求提前写了一堆抽象。</p><h3 id="5-代码级坏味道"><a href="#5-代码级坏味道" class="headerlink" title="5. 代码级坏味道"></a>5. 代码级坏味道</h3><p>Long Method(超过 50 行的函数)、Long Parameter List(参数超过 4 个)、Primitive Obsession(用 <code>string</code> 表示 Email、<code>int</code> 表示金额)、Magic Numbers(<code>if (status == 3)</code> 到处都是)、Dead Code(注释掉的代码块和未使用的 import)。</p><h3 id="6-测试健康度"><a href="#6-测试健康度" class="headerlink" title="6. 测试健康度"></a>6. 测试健康度</h3><p>零测试覆盖的模块、测试耦合了实现细节(测私有方法、mock 内部调用)、依赖真实 I/O 的慢测试。</p><h3 id="7-命名质量"><a href="#7-命名质量" class="headerlink" title="7. 命名质量"></a>7. 命名质量</h3><p>满屏的 <code>Manager</code>、<code>Handler</code>、<code>Util</code>、<code>Helper</code>、<code>Service</code>——等于没起名。还有 <code>snake_case</code> 和 <code>camelCase</code> 混用、同一概念在不同地方用不同名字。</p><h3 id="8-算法复杂度热点"><a href="#8-算法复杂度热点" class="headerlink" title="8. 算法复杂度热点"></a>8. 算法复杂度热点</h3><p>这部分我觉得最有价值。很多工具只管"代码丑不丑",<code>/smell</code> 还管"代码跑不跑得动":</p><ul><li>N+1 查询:循环里发数据库请求,100 条数据 = 101 次 SQL</li><li>嵌套循环 O(n²):双层 <code>for</code>,数据量一大就原地爆炸</li><li>循环内线性扫描:<code>includes()</code> 写在循环里,本该用 Set 做 O(1) 查找</li><li>循环内排序:每次迭代都 <code>sort()</code> 一遍,K × O(n log n)</li><li>渲染路径重计算:React/Vue 组件里 <code>.filter().map().sort()</code> 没做 memoization</li><li>选错了数据结构:该用 Map 的地方用了 Array 做 O(n) 查找</li></ul><h2 id="怎么用"><a href="#怎么用" class="headerlink" title="怎么用"></a>怎么用</h2><p>在 Claude Code 中打开项目,输入:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/smell</span><br></pre></td></tr></table></figure><img src="/2026/06/11/code-smell-detector-ai-skill/image-20260528054357646.png" class=""><p>它会先问分析范围:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">你想分析哪个范围?</span><br><span class="line"> A. 整个项目(全面但耗时)</span><br><span class="line"> B. 指定模块/目录</span><br><span class="line"> C. 仅分析最近改动的文件(git diff)</span><br><span class="line"> D. 仅分析架构级问题(跳过代码级坏味道)</span><br></pre></td></tr></table></figure><p>选完之后它会启动多个并行探索任务:扫描项目结构、分析依赖关系、检测模块内聚性、识别反模式签名、评估测试覆盖,同时跑。</p><p>分析完成后在 <code>tasks/</code> 目录下生成报告,比如 <code>tasks/smell-report-2026-05-28-1430.md</code>,终端同时打印摘要:</p><img src="/2026/06/11/code-smell-detector-ai-skill/image-20260528054454478.png" class=""><p>看到摘要你就知道该从哪下手了。可以直接让 AI 按分析结果做架构优化:</p><img src="/2026/06/11/code-smell-detector-ai-skill/image-20260528054535740.png" class=""><p>重构完成后 AI 会给出修改报告:</p><img src="/2026/06/11/code-smell-detector-ai-skill/image-20260528054613646.png" class=""><h2 id="报告长什么样"><a href="#报告长什么样" class="headerlink" title="报告长什么样"></a>报告长什么样</h2><p><strong>Executive Summary</strong>:检测到的架构风格、整体健康评估、最关键的 3-5 个问题,三段话说完。</p><p><strong>架构风格识别</strong>:分层架构?模块化单体?微服务?六边形?Clean Architecture?还是 Big Ball of Mud。用一张表对比"期望 vs 现实"。</p><p><strong>严重度分级清单</strong>: Critical(必须修)、 Warning(应该修)、 Suggestion(建议修)。</p><p><strong>逐条问题分析</strong>:类别、严重度、反模式名称、文件行号、违反的原则、代码证据、重构建议。每一条都回答三个问题:这是什么坏味道?为什么它是坏味道?怎么改?</p><p><strong>依赖图分析</strong>:模块间依赖关系、循环依赖路径、耦合热点。</p><p><strong>模块健康度记分卡</strong>:每个模块的代码行数、God Object 风险、耦合度、内聚性、测试覆盖率。</p><p><strong>Smell 分布统计</strong>:八个维度各发现多少问题,按严重度排列。</p><p><strong>重构路线图</strong>:当前 Sprint 立即行动、1-3 个月短期改善、3-12 个月长期演进。分阶段治理,不用一口气全改完。</p><h2 id="一个实际场景"><a href="#一个实际场景" class="headerlink" title="一个实际场景"></a>一个实际场景</h2><p>接手一个迭代了两年的 Go 项目,不确定架构质量,跑一遍:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/smell 分析这个 Go 项目的架构质量</span><br></pre></td></tr></table></figure><p>它发现:</p><ol><li><code>internal/model/</code> 下的结构体全是贫血模型,只有字段没有方法,逻辑全在 <code>internal/service/</code>。典型的 Anemic Domain Model,应该把行为移回领域对象。</li><li><code>internal/service/order.go:233</code> 有 N+1 查询,遍历订单时逐条查商品。改成 <code>WHERE id IN (...)</code> 批量加载就完了。</li><li><code>pkg/auth</code> 和 <code>pkg/user</code> 互相导入,循环依赖。抽取一个公共接口可以打破循环。</li><li><code>internal/handler/</code> 下 6 个文件都有大段重复的参数校验逻辑,违反 DRY。提一个公共校验中间件。</li><li>23 处魔法数字,<code>if order.Status == 3</code> 随处可见。定义个 <code>const StatusCompleted = 3</code> 或者用枚举。</li></ol><p>报告附带了分阶段的重构路线图。你按优先级一步步来就行,不用面对整个代码库发呆。</p><h2 id="快捷模式"><a href="#快捷模式" class="headerlink" title="快捷模式"></a>快捷模式</h2><p>只想看致命伤,不等全面分析:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/smell 快速检查一下架构</span><br></pre></td></tr></table></figure><p>跳过代码级和命名级检测,只跑架构反模式、循环依赖、God Object、N+1 查询这些 Critical 级别。几分钟出结果。</p><p>也可以只关注一个维度:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/smell 只分析算法复杂度热点</span><br></pre></td></tr></table></figure><p>就只找嵌套循环、N+1 查询、选错数据结构这些性能问题。</p><h2 id="为什么需要它"><a href="#为什么需要它" class="headerlink" title="为什么需要它"></a>为什么需要它</h2><p>有人会说:"这些坏味道我自己也能看出来啊。"</p><p>如果你有十年架构经验、Fowler 和 Uncle Bob 的书都读过、反模式论文烂熟于心,当然可以。但大多数团队不是这样。</p><p>团队里的初中级开发看不出 Anemic Domain Model 有什么问题,他们觉得"Service 写逻辑、Model 放数据"天经地义。即使你自己有经验,一个 10 万行的项目你也看不过来。看了三天同一套代码,嗅觉已经疲劳了。Code Review 通常只关注功能正确性和代码风格,没人在 PR Review 的时候画依赖图、算圈复杂度。</p><p><code>/smell</code> 填补的就是这个空缺。它给你证据,你自己做决策。每个问题都标了严重度,你根据优先级和资源决定修哪些。</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>这个 Skill 内置在 <a href="https://github.com/smallnest/goal-workflow">goal-workflow</a> 的 skills 集合中。已配置 goal-workflow 的直接用 <code>/smell</code>。</p><p>安装命令:</p><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx skills add smallnest/goal-workflow --skill smell</span><br></pre></td></tr></table></figure><p>用 Claude Code 的话,也可以直接把 <code>SKILL.md</code> 放到 <code>.claude/skills/smell/</code> 目录下,立刻生效。</p><h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><img src="/2026/06/11/code-smell-detector-ai-skill/image-20260528055802460.png" class=""><p>代码坏味道是一天天堆出来的。每次赶 deadline 时的妥协,每次"只加一个 if"的侥幸,每次 copy-paste 省下的五分钟,都在给代码库增重。</p><p>治理的难点从来不是"要不要做",而是"从哪开始"。<code>/smell</code> 给你一张问题地图,标清楚了臭在哪、有多严重、该按什么顺序清理。路还是得你自己走。</p><p>代码是给人读的,顺便让机器执行。如果你自己都不想读自己的代码,让 AI 先帮你闻闻吧。</p>
同分类推荐文章
- Go 实验特性详解 (2026-06-21 10:05:27)
- amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)
- Loop Engineering 实践:我把 RDMA 开发库移植到 Go 语言,花费 239 块钱 (2026-06-17 04:00:24)
建议继续学习
- 抵制代码重写 (累计阅读 5,500)
- 如何避免重构带来的危险 (累计阅读 4,613)
- 什么是重构,什么不是重构 (累计阅读 4,592)
- 淘宝的一些架构 (累计阅读 4,057)
- 十种更好的表达“你的代码写的很烂”的方法 (累计阅读 4,030)
- 前端重构实践(一) —— 性能优化 (累计阅读 4,018)
- 关于重构和重写 (累计阅读 3,808)
- 智能输入法软件的社会责任问题 (累计阅读 3,368)
- 一个实例:为什么注释是愚蠢的 (累计阅读 3,363)
- 关于返回 Null 值的问题 (累计阅读 3,145)