ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

代码卫生六步清扫:用AI技能系统治理代码库

代码卫生六步清扫:用AI技能系统治理代码库

代码卫生这件事,说起来不大,做起来真磨人。我自己的项目里,经常是"功能能跑就先跑",等回头要加需求或者排查线上问题的时候,面对一堆历史代码,那种"想清又不敢清"的憋屈感,估计写代码超过三年的人都懂。后来我把这套清理动作整理成一个可复用的技能包,起了个名字叫Hygiene-sweep,也就是标题里说的"代码卫生六步清扫.skill"。

我之所以强调是"skill"而不是"脚本"或者"检查清单",是因为现在主流的 AI 编程助手基本都支持自定义技能,把一套标准动作写进技能文件,再配合仓库里的真实代码做上下文判断,比我以前开十几个终端窗口人肉排查要稳得多。而且这套东西不挑语言,只要是工程级代码库,基本都能用。这篇文章就把这条 skill 从设计思路、六步内容、落地过程到踩坑记录,完整拆给你看。

1. 代码卫生为什么要做成一条 skill,而不是临时想起来扫一遍

1.1 代码卫生到底在扫什么

先说个真实场景。有一回我接手一个老服务,功能是正常的,但每次部署我都提心吊胆,因为根本不知道哪条 import 是没用的、哪个分支永远不会被执行、哪段注释已经和代码事实完全相反。后来我花了一个下午人肉扫了一遍,累倒是不怕,问题是扫完才发现,自己用的还是"凭感觉"那一套,根本没有形成可复用的标准。

代码卫生不是一个抽象概念,它其实可以被拆成几个非常具体的检查面:

  • 依赖卫生:有没有锁文件里躺着十几个不用的包,有没有明知有 CVE 还一直拖着的版本。
  • 结构卫生:有没有 Copy-Paste 出来的几百行重复代码,有没有一层套一层的包目录。
  • 表达式卫生:有没有一眼看不懂的魔法数字、又臭又长的 if 嵌套、根本没用的常量。
  • 信息卫生:有没有把数据库连接串、密钥、内部域名直接写死在代码里。
  • 流程卫生:有没有吞掉异常的 catch、打满日志却全是 INFO 噪音的打印、缺了兜底分支的网络调用。
  • 验证卫生:有没有那种"测试跑过了但断言形同虚设"的假测试,有没有改了业务代码测试却完全没响应的死角。

这六类问题,单看每一个都不致命,但叠在一起,就是线上事故和迭代变慢的温床。所以我把它们编排成一条固定的清扫流水线,让 AI 助手每次照着同一套流程去扫,而不是靠程序员当天的心情。

1.2 为什么是"六步"而不是一份普通检查清单

很多人会说,这不就是一份 checklist 吗?把它写成文档贴进 Wiki 不也一样?差别很大。普通 checkList 是给人看的,你得自己逐条对照,然后手动去找代码、改代码、验证改完的结果;而 skill 是给"人+模型+工具"这个组合体执行的。

拿我用的方式来说,一条 skill 通常包含三个部分:指令定义(告诉模型这个技能要干什么、按什么流程走)、规则上下文(把"什么是脏代码"的定义给模型,让它照着标准判断)、可执行动作(调用 grep、ast 解析、依赖审计等命令去仓库里捞真实线索)。说白了,就是我先当那个把经验沉淀成标准动作的人,然后让模型在我的标准里做苦力活。

六步的设计也不是随手拍脑袋。它有一个很关键的递进关系:前两步先扫"能不能跑得干净"(依赖、死代码),中间两步扫"人能不能看得懂"(命名、信息泄露风险),最后两步扫"出了问题能不能兜得住"(日志异常、测试闭环)。从运行层面到维护层面再到风险层面,一层一层往深里走,实际上模拟的是一个有经验的老开发拿到陌生代码库时的自然动作:先看工程配置,再看代码结构,然后翻业务逻辑,最后确认测试。

2. Hygiene-sweep 六步清扫的整体设计与思路拆解

2.1 第一步:清扫依赖与构建链路

这一步我把它放在最前,理由是依赖问题连锁反应最大。一个过期的传递依赖,可能在某个冷门接口上给你埋雷;一个删不掉的死依赖,则会让镜像体积无缘无故大出几百兆。

具体操作上,我会让 skill 先做三件事:列全量依赖清单,锁定文件和实际 import 清单做差集,再看安全公告。差集这一步尤其重要,很多项目里package.json写了 50 个包,实际代码里只用了 20 个,剩下 30 个纯粹是历史惯性留下来的。把差集列出来,标成"建议移除",但注意不要自动删,因为有些包是构建脚本、代码生成器在用的,直接删会炸。

实际操作里我最满意的工具组合是:Python 项目用pip-audit查已知漏洞,Node 项目用npm audit,同时不管什么语言都把requirements.txt、Pipfile.lock、package-lock.json这类文件翻一遍。特别提醒一句,别只盯着直接依赖,npm audit报的很多问题都在传递依赖里,这时候要看升级方案而不是一刀切升级,盲升大版本带来的破坏往往比漏洞本身更难受。

2.2 第二步:清扫死代码、重复代码与坏味道

死代码是那种"看着活着、其实没人调用"的东西。老项目里最常见的死代码有三种:被注释掉的旧逻辑块、导出了却从来没人 import 的函数、以及被硬编码条件挡住的永久不可达分支。

这一步的清扫逻辑是反着来的:先让 skill 建立"引用关系图",然后找那些出度为 0 的节点。简单说,就是一个函数如果有定义、有导出,但整个仓库里搜不到任何调用点,那它基本就是死代码候选。像 VSCode 里搜全局、IDEA 里的 Find Usages,都能干这件事,但问题是没法批量出报告。用 skill 的好处是,可以让模型去把所有候选结果汇总成一张"待人工确认"表格,每条附上文件路径、定义行号、最后一次 git 修改时间,方便我快速决定是删还是留。

重复代码的识别就更直接了,用简单的文本哈希或者 AST 对比都能做。但我要给个实用建议:重复三处以内先别急着抽公共函数,强行抽象在业务变化快的时候反而难维护。skill 在这一步真正该标记的,是那种"五行逻辑复制了七八遍,中间只改了一个参数"的高危重复,这才是抽函数、抽配置的最佳候选。

2.3 第三步:清扫命名、注释与可读性

命名问题我以前觉得不重要,直到有一次我 debug 一个线上问题,发现变量叫temp2,另一个叫temp3,而temp早就被注释掉了。从那以后我把命名列为固定清扫项。

这一轮的标准其实很朴素:不看注释,能不能读懂这段代码在干什么。skill 会专门挑出那些含义模糊的命名,比如单字母变量、带数字后缀的变量(data1、data2)、一个词干到底的函数名(handle、process、doWork),然后结合上下文给出更具体的替代名。举例来说,handle出现在订单模块里,替代名可能是handleRefundTimeout;出现在营销模块里,可能就应该是handleCouponExpiry。

注释这块我会让模型盯住两类:一类是和代码矛盾的错误注释,这类必须改,因为它会误导后来者;另一类是"代码本身丑到需要注释解释"的场景,这时候正确的做法不是补注释,而是把代码重构到不需要注释也能看明白。

关于可读性,我建议加入一个很主观但有效的指标:单函数行数和嵌套深度。超过 80 行的函数、嵌套超过 4 层的 if,清扫报告里必须出现。不用急着让模型重写,先标出来,标得多了自然会发现哪些模块最该重构。

2.4 第四步:清扫安全与敏感信息

安全清扫在六步里最不能省,但也最需要控制噪音。这一步我会让 skill 分两条线走:一条是硬编码敏感信息,另一条是高风险接口的误用。

硬编码敏感信息靠正则就能扫掉一大半:类似AKIA开头的访问密钥、password =后面跟字符串、Bearer后面贴 token、jdbc:mysql://连接串里带明文密码,还有提交到 Git 历史里的.env文件。特别提醒,.git 历史里的泄露比当前代码里更隐蔽,GitHub 上那些密钥泄露事故,多数是删了文件但没清历史。所以我会让 skill 在清扫时顺带检查最近 50 条提交里有没有新增的敏感文件。

高风险接口的误用稍微难一点,但模型做初步筛选完全够了。比如不校验输入就直接拼 SQL、把用户可控参数传进文件路径、在循环里执行加解密操作。这些不一定是漏洞,但至少是"值得人工复审一次"的代码位置。我把这轮标记为"审查点"而不是"必须改",避免清扫变成一场狼来了的闹剧。

2.5 第五步:清扫日志、异常与错误处理

日志和错误处理,是代码卫生里最"脏"也最容易被忽略的部分。我见过很典型的一个服务,平时日志量巨大,但真出了事,排查的人根本无从下手,因为满屏全是INFO: step 1 done、INFO: step 2 done,唯一的ERROR还是something went wrong。

这一轮清扫我会重点看三件事:第一,异常吞没。空catch块、catch 住了只打一行 debug 日志不往上抛的,全部拉出来示众。第二,日志能不能支撑事后复盘。关键业务入口、外部接口调用、数据库操作,这些位置必须有带上下文信息的日志,光有save failed等于没写。第三,错误码和错误信息是否可检索。理想状态下,每个明确的错误分支都应该有唯一标识,方便在日志系统里直接按标识搜。

我还额外加了一条很个人化的规则:警惕字符串拼接日志。日志框架的懒加载是有意义的,用字符串拼接会无条件执行拼接操作,在高频路径上会有性能损耗,这属于"看不见的脏"。这一类问题模型很容易发现,标记出来,改成占位符写法就行。

2.6 第六步:清扫测试闭环与可观测性

最后一步很多人会漏掉,但我觉得恰恰是"代码卫生"和"代码整洁"的区别所在。代码整洁是静态的"好看",代码卫生是动态的"健康"——一个功能能不能改完就放心上线,取决于测试有没有兜住。

测试闭环的清扫分三个层次:

  • 有没有测:核心业务模块有没有对应的单元测试或集成测试文件。
  • 真不真:测试文件里有没有形同虚设的断言。比如只断言返回不为空,不校验具体结果;或者断言写死了 Mock 数据,压根没测到真实逻辑。
  • 同步性:git 历史里某几个模块频繁改动,但对应测试文件的改动次数远低于业务代码,说明这些模块正在失去保护。

可观测性这部分我扫的是指标和链路。有没有给关键接口加耗时统计?有没有把错误率暴露成监控指标?这部分不像前五步那样能机器化地一刀切,所以我把它设计成"生成建议清单"的模式,让模型把"哪些模块快裸奔了"标出来,剩下的人工去排优先级。

3. 实操落地:从零配置一个 Hygiene-sweep skill

3.1 skill 文件结构与最小的可运行骨架

不管你是用 Claude 系的 SKILL.md,还是其他支持自定义技能的编程助手,底层逻辑都差不多:给模型一份带明确流程的 markdown 文件,让它知道现在该用什么样的身份、按什么样的步骤、调用什么样的命令来处理问题。

我习惯的最小骨架长这样:

# Hygiene-sweep 实现一个专门的代码卫生检查流程,对目标仓库执行六步清扫。 每次扫描必须以结构化报告形式输出结果。 ## 技能说明 - 适用场景:接手陌生项目 / 发版前自查 / 代码评审预检 - 输出语言:与项目代码注释语言保持一致,默认中文 - 扫描范围:排除 node_modules、dist、build、.git 等目录 ## 执行流程 1. 先扫描项目根目录,识别语言类型与包管理工具 2. 按六步清扫顺序逐项检查 3. 每步输出问题清单,标注文件路径、行号、严重程度 4. 合并结果,生成最终报告

注意看,这个文件里我只写了流程框架,没有写具体的"什么是脏"的标准。标准必须单独放,否则文件会很臃肿,模型也不容易记住重点。我会用类似下面的 YAML 片段把规则外置,让主文件保持清爽:

rules: dead_code: - "导出却无引用的函数" - "被注释掉的完整逻辑块" - "条件判断中永假的半支" sensitive_info: - "AKIA[0-9A-Z]{16} 类型的访问密钥" - "明文密码赋值" - "嵌入代码的数据库连接串"

3.2 六步清扫指令模板的核心写法

六步的指令写法,我这里直接给出我调试到还算稳定的版本。每一步都包含三个元素:目标描述、可执行动作、输出格式要求。

以第一步为例:

### Step 1: 依赖与构建链路清扫 目标:识别依赖声明与实际使用不一致的包,识别存在安全公告的版本。 执行动作: - 读取锁文件(package-lock.json / Pipfile.lock / go.sum / pom.xml) - 结合源码目录的 import 语句,生成未使用依赖列表 - 若本机具备安全审计工具(npm audit / pip-audit),执行并记录结果 输出要求: - 未使用依赖列表:按体积或引用数排序,标注"建议移除"或"谨慎保留" - 安全风险列表:标注 CVE 编号、影响版本、建议升级目标 - 不要自动修改 manifest 文件,只输出修改建议

其他五步的写法和这个完全同构,只是把"目标、动作、输出"换成对应的清扫主题。这种统一的结构对我特别有用:模型在跑的时候不会迷路,我拿回报告的时候也知道每部分该怎么读。

有一点必须强调:所有指令里都要写明"不要自动修改"。因为代码清扫这件事,最大的风险不是扫不到,而是模型自作主张帮你改了一堆,你以为它是对的,结果破坏了某个隐式约定。我在早期版本里踩过这个坑,后来所有动作默认只输出报告,只有拿到报告、人工确认之后,才允许进入下一步修复模式。

3.3 与真实工具链的绑定

指令写得再漂亮,最终还是要落到真实命令行工具上。我在 skill 里绑定的工具清单大概是这样:

用途工具说明
Python 依赖审计pip-audit直接依赖和传递依赖的 CVE 检查
Node 依赖审计npm audit输出 JSON 格式方便解析
死代码扫描vulture/knip/ 自定义 grep 组合按项目语言选
重复代码检测jscpd支持多语言,直接标重复率
敏感信息搜索trufflehog/gitleaks扫描当前文件和历史提交
测试覆盖率对照pytest --cov/jest --coverage拿数据和 git 修改频率做交叉验证

但是,不是所有人都装了这些工具,所以我给 skill 加了一条降级策略:如果执行环境里没有对应命令,就用模型自带的代码阅读能力去扫,同时把"未安装工具"记进报告备注。这样无论环境多简陋,技能都不会直接罢工。

实际跑一轮的效果我很满意,模型会把每个工具的原始输出先汇总、再过滤、再标注严重级别。比如npm audit一次能吐几十条,里面绝大多数是低危的传递依赖,直接展示价值不大。skill 会把这些按"是否有修复路径""是否影响直接依赖""是否被主流程调用"三层过滤,最终汇报里只留几条真正要人做决策的。

3.4 一次真实清扫的输入与输出对比

这是我目前看到的最直观的效果展示。有一次我拿一个已经有两年多没做技术债清理的后端仓库做测试,仓库不算大,四十多个 Python 文件加三个入口脚本。传统人肉扫的话,我估计要半天到一天;用这条 skill 跑,整个过程大概十几分钟(包括模型阅读上下文的时间)。

输出报告的关键摘要长这样:

  • 依赖层面:发现 3 个只出现在 requirements.txt 却从未被 import 的包,其中 1 个已经维护状态不明。
  • 死代码层面:12 个无引用函数,共涉及约 900 行代码;6 段被注释掉的旧逻辑块,最早注释时间跨度两年。
  • 信息卫生层面:发现 1 个明文的 Redis 密码、2 个疑似 AWS 密钥字样的字符串,以及 1 份误提交的.env文件。
  • 错误处理层面:17 个空 catch 块,集中在文件上传和支付回调两处。
  • 测试同步性:业务代码两个文件的 git 提交次数超过 60,但对应测试文件的提交不到 5 次。

拿到这个报告,我第一反应是有点丢人,第二反应是踏实。因为所有这些以前都藏在我"感觉好像没什么问题"的盲区里。更重要的是,报告不是模型拍脑袋编的,每条都附了路径、行号、引用关系,我可以直接点过去验证。

4. 常见问题与排查技巧实录

4.1 权限报错:SKILL 读取文件时提示 setNamedSecurityInfo failed

这个坑我遇到过,而且特别典型。有的 skill 平台在 Windows 环境下执行时,会尝试调整文件权限或继承安全属性,如果当前终端进程没有管理员权限,就会冒出来一行类似setnamedsecurityinfow failed (win32)的报错。

排查思路很简单,分三步走:

  1. 先分清是"读不了"还是"改不了"。如果是读取阶段就报错,大概率是平台配置里给了 skill 太高的权限请求;如果是写报告阶段报错,那就是输出目录的 ACL 有问题。
  2. 把 skill 的输出目录从系统盘临时目录挪到当前用户目录下,很多权限问题是 Windows 的路径保护策略导致的。
  3. 如果还不行,就用icacls手动给工作目录加当前用户的写权限,或者直接换一个纯用户态的目录当工作区。

我的建议是:不要为了省事全局给启动器管理员权限,图一时方便,后面 skill 版本迭代时权限边界会越来越难收敛。把这个报错当成环境治理的信号,反而能逼你把目录权限理顺。

4.2 大仓库扫描导致上下文爆掉

代码卫生技能最怕的就是超大仓库。一次扫描读几百个文件,命令输出几十万行,模型上下文直接红温,轻则报告质量下降,重则平台直接拒绝继续执行。

我的处置经验是给 skill 加"采样模式"和"全量模式"两个档位。

采样模式适用于第一次接触的巨型仓库:按目录树抽样,每个主要模块挑 2-3 个代表性文件,加上锁文件和全局配置文件,先跑一轮"粗扫"。输出的是风险热力图:哪个目录问题密度高、哪类问题占比大。这样你不需要把全仓库喂给模型,也能快速定位重灾区。

全量模式只适合中小仓库,或者已经采样之后的定向深扫。真要在超大仓库里做全量清扫,我会先把待扫描范围切割成模块级任务,一次只扫一个模块,最后在报告层面合并。多跑几次,比一次硬塞进去的效果稳得多。

4.3 误报太多:如何区分真实问题与噪音

清扫技能刚跑通的时候,我差点被误报劝退。最典型的误报有两种:一种是把第三方库的代码当成项目死代码,因为扫描范围没排干净;另一种是把刻意保留的兼容代码标记成"该删",比如为了兼容老版本字符串格式特意保留的解析分支。

处理误报,我的办法是给 skill 加了三层过滤:

  • 路径黑名单:把非业务目录(site-packages、node_modules、vendor、build)彻底排除出问题扫描,只在统计背景信息时引用。
  • 模式白名单:在规则里增加"跳过已知合理模式",比如支付回调里的空 catch 如果有注释说明,就不要当作异常。
  • 置信度分级:建议输出结果的时候分成"高置信建议修改""中等置信需要人工确认""仅作为提示信息"三档,这样不会被几十条噪音淹没有效信息。

三层过滤下来,有效信息比例提升非常明显。我现在看一眼报告开头那张分级汇总表,就能判断今天要不要安排一次清扫。

4.4 清扫结果如何沉淀成团队规范

最后一个值得聊的问题是:清扫报告看完了,问题也修了,然后呢?如果每次都靠一条 skill 扫完就完事,代码卫生永远停留在"事后救火"状态,不会真正改善。

我的做法是把 skill 的输出格式往"提交规范"上靠。具体就是让报告末尾自动生成三条沉淀建议:

  • 哪些问题应该加进 CI 的检查脚本,比如硬编码密钥的正则扫描、必须通过依赖审计的硬门槛。
  • 哪些模块的测试覆盖率需要补,直接生成一个"补测优先级列表"。
  • 哪些命名和注释模式要在评审规范里明令禁止,比如禁用temp1这类无意义命名。

这样每次清扫不只是清一次垃圾,而是给项目留下一套越来越厚的护城河。我用这个思路跑了一个季度,最明显的变化是仓库的"技术债增长速率"慢下来了,因为规则一旦写进 CI 和评审规范,新代码在进门的时候就被拦掉了一大截。

5. 写在最后的一点个人体会

这条 Hygiene-sweep skill 前前后后迭代了四个版本,从最初只有一句"帮我检查代码质量问题",到现在完整覆盖六步清扫流程,我最深的体会是:代码卫生的价值不在于一次性把仓库收拾得多干净,而在于它把"什么是脏、怎么算干净"这件事定义出来了,让整个团队在同一个标准下协作。个人开发者用它,能在接手旧项目时快速建立全局风险认知;小团队用它,可以在评审前多一道机器预检;在更大的组织里,它甚至可以作为技术债治理的自动化底稿。

如果你也想做一条类似的 skill,我的建议是别一上来就追求大而全,先聚焦你最痛的那一类问题。比如你被线上密钥泄露搞怕过,就先把敏感信息扫描做到极致;你被屎山代码坑过,就先把死代码和重复代码的识别做好。一个够用的 skill,永远好过一个追求完美结果却根本跑不起来的 skill。

还有个小技巧值得分享:skill 里的规则标准,不要闭门造车,直接从你最近三次手写代码评审记录里提炼。你现实中真正会挑出来的问题,才是技能里最该有的规则;通用文档里的最佳实践,很多时候落到具体业务场景根本不好使。

希望这条六步清扫的经验能帮你的代码库少一点"陈年污垢"。代码卫生这事,打扫一次不难,难的是每次都按同一个标准打扫,而 skill 恰好让后者变成了一件可以自动执行的事。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进