IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

寻找你代码中的臭味:一个让 AI 帮你嗅出架构腐化的开源 Skill

鸟窝 2026-06-21 18:40:38 累计浏览 11 次
本机暂存
<p>你有没有过这样的经历:接手一个&quot;跑了三年没人敢动&quot;的项目,打开代码仓库一看——</p><p><code>src/</code> 下面 200 多个文件平铺在一个目录里,没有分层,没有模块边界。一个叫 <code>UserService</code> 的类 1800 行,发邮件、对接支付、状态管理全塞在里面,还挂着三个 <code>TODO</code> 标着&quot;后面要重构&quot;。业务逻辑全堆在 Service 层,Model 类只剩 getter 和 setter,贫血得像张纸。数据库查询藏在 for 循环里,每循环一次发一条 SQL。你问老员工这模块谁负责,得到一句:&quot;这个……已经没人记得了。&quot;</p><p>Martin Fowler 把这类问题叫做&quot;代码坏味道&quot;(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> 或&quot;帮我找找代码里的坏味道&quot;,它会扫描代码库,从架构层面到代码层面做一轮完整检测,最后输出一份 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 &amp; Yoder 的 Big Ball of Mud 论文</li><li>Clean Architecture、Onion Architecture、Hexagonal Architecture 等主流架构风格</li><li>一套算法复杂度反模式检测规则</li></ul><p>相当于把一个资深架构师十几年攒下的&quot;闻臭味&quot;经验,塞进了一个命令里。</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&#x2F;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&#x2F;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>这部分我觉得最有价值。很多工具只管&quot;代码丑不丑&quot;,<code>/smell</code> 还管&quot;代码跑不跑得动&quot;:</p><ul><li>N+1 查询:循环里发数据库请求,100 条数据 &#x3D; 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&#x2F;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。用一张表对比&quot;期望 vs 现实&quot;。</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>有人会说:&quot;这些坏味道我自己也能看出来啊。&quot;</p><p>如果你有十年架构经验、Fowler 和 Uncle Bob 的书都读过、反模式论文烂熟于心,当然可以。但大多数团队不是这样。</p><p>团队里的初中级开发看不出 Anemic Domain Model 有什么问题,他们觉得&quot;Service 写逻辑、Model 放数据&quot;天经地义。即使你自己有经验,一个 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 时的妥协,每次&quot;只加一个 if&quot;的侥幸,每次 copy-paste 省下的五分钟,都在给代码库增重。</p><p>治理的难点从来不是&quot;要不要做&quot;,而是&quot;从哪开始&quot;。<code>/smell</code> 给你一张问题地图,标清楚了臭在哪、有多严重、该按什么顺序清理。路还是得你自己走。</p><p>代码是给人读的,顺便让机器执行。如果你自己都不想读自己的代码,让 AI 先帮你闻闻吧。</p>

同分类推荐文章

  1. Go 实验特性详解 (2026-06-21 10:05:27)
  2. amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)
  3. Loop Engineering 实践:我把 RDMA 开发库移植到 Go 语言,花费 239 块钱 (2026-06-17 04:00:24)

查看更多 后端 文章 →

建议继续学习

  1. 抵制代码重写 (累计阅读 5,500)
  2. 如何避免重构带来的危险 (累计阅读 4,613)
  3. 什么是重构,什么不是重构 (累计阅读 4,592)
  4. 淘宝的一些架构 (累计阅读 4,057)
  5. 十种更好的表达“你的代码写的很烂”的方法 (累计阅读 4,030)
  6. 前端重构实践(一) —— 性能优化 (累计阅读 4,018)
  7. 关于重构和重写 (累计阅读 3,808)
  8. 智能输入法软件的社会责任问题 (累计阅读 3,368)
  9. 一个实例:为什么注释是愚蠢的 (累计阅读 3,363)
  10. 关于返回 Null 值的问题 (累计阅读 3,145)