这次我偷懒是用 GPT-5.4 写的。
上篇:AI编程之我见2
第一篇写的时候,我还在被 WordPress 迁移那点破事折磨得死去活来。那时候最大的感受是,AI 确实能一下子帮你干完前 80%,但剩下那 20% 的小问题,足够把一个外行反反复复按在地上摩擦。
第二篇写的时候,我开始意识到,vibe coding 绝对不是撒手不管,回归测试才是底线。没有测试,你根本不知道 AI 是在帮你写代码,还是在悄悄把项目拆掉。
而到现在,我越来越觉得,这件事还得再往前走一步。
AI 编程到了今天,对我来说已经不是“怎么让 AI 多写几行代码”,而是“怎么驾驭 AI 这架马车”。
马当然是 AI。真正决定车翻不翻的,是你手里有没有缰绳。
到现在为止,我觉得自己最重要的三个抓手,已经慢慢固定下来了:纯用 AI 写代码,回归测试,契约文档。
这三个东西,不是三条零散小技巧,而是我这段时间一点点摸出来的一套系统。
驾车 vs. 拉车
第一个抓手,其实反而是最“野路子”的那个:纯用 AI 写代码。
这听起来好像很不稳,甚至像是在作死,但我现在反而觉得,这是一个绕不过去的阶段。因为如果你一直只是把 AI 当成一个高级搜索引擎,或者当成一个自动补全工具,那你其实根本没有真正进入 AI 编程。
只有当你真的让它从 0 到 1 去做,让它自己写、自己接线、自己跑、自己修,你才会真正看清楚这玩意到底是什么水平。
它强在哪,蠢在哪,什么时候会突然像开窍了一样,什么时候又会一本正经地胡来,这些都得在“真用”的过程中才看得出来。
而且也只有到了这个阶段,人类的角色才会开始变化。你不再只是一个亲手敲代码的人,而会慢慢变成一个调度 AI 的人。你开始关心的,不再只是“这段代码写出来没有”,而是:这个任务到底该怎么拆?这段上下文是不是已经污染了?哪些信息该给它,哪些信息现在给了反而坏事?它现在是在局部修 bug,还是已经开始顺手漂移整个架构?
说白了,不纯用 AI,你就永远只是把它当 copilot。纯用 AI 之后,你才会被迫长出“驾驶”的意识。
测试 vs. 侥幸
第二个抓手,就是我上一篇已经说过很多次的回归测试。
这玩意对 vibe coding 来说,真的是命根子。
因为 AI 最大的问题,从来不是写不出来,而是它太容易顺手改坏别的地方。眼前这个功能也许修好了,但昨天还能跑的流程、上周还正常的入口、某个角落里你根本想不到的东西,说不定已经被它悄悄带崩了。
没有测试的时候,每次改代码都像蒙着眼睛开车。你只能靠肉眼看,靠感觉猜,靠 AI 自己拍着胸脯说“应该没问题”。这种“应该”基本上没什么价值。
一旦有了回归测试,局面就不一样了。你不用跟它争辩,也不用靠主观感觉判断。过不过测试,就是唯一标准。挂了就继续修,过了再说。
所以我到现在依然觉得,外行做 vibe coding,第一原则还是死磕测试。每实现一个功能就补测试,每改一次代码就跑回归。这个习惯养不出来,后面项目一变大,迟早会吃大亏。
但后来我发现,光有测试,其实还是不够。因为测试能解决“别改坏”,却解决不了“别写丑”。
测试 vs. 质量下滑
AI 有一个很典型的特点,就是它特别擅长先把东西做出来,但也特别擅长在反复迭代里偷偷堆技术债。
死代码越来越多,helper 越来越散,重复逻辑到处都是,明明一个模块该负责的事,慢慢渗到另一个模块里去了。更麻烦的是,这些问题很多时候并不会立刻炸,测试也照样能过。
也就是说,项目可能功能上是健康的,但结构上已经开始发霉了。
所以这段时间我还自己实现了一个 Code Elegance Reviewer,它在开发过程中起到了很大的作用。
这玩意不查“功能对不对”,而是专门盯代码优雅性和架构质量。比如有没有重复实现、有没有废代码、有没有越堆越厚的兜底逻辑、哪些职责已经开始膨胀、哪些边界已经发虚、哪些地方该抽 helper、哪些地方该收回模块内部。
如果说回归测试是在防止功能回归,那 Code Elegance Reviewer 更像是在防止代码质量慢性下滑。
它不是第四大支柱,更像是开发过程里的巡检员。很多问题短期内不会把项目炸掉,但你不早点收拾,后面一定会越滚越大。尤其是 AI 参与开发的时候,这种“看起来都能跑,但实际上越来越臃肿”的趋势特别明显。
文档 vs. 说明书
而第三个抓手,就是我最近越来越重视的东西:契约文档。
我现在的感受已经很明确了,防止架构漂移这件事,文档系统太重要了。
但这里说的文档,不是传统意义上那种技术文档、开发文档,不是那种写给人类看、写完就放在角落里吃灰的说明书。那种文档当然也有用,但不是我现在说的重点。
我说的这个文档,是专门锁定契约的,是为了 AI 并行开发而设计的。
它锁的不是实现细节,而是边界。
哪些模块各自负责什么,哪些路径归谁,哪些入口是稳定的,哪些文件是共享瓶颈点,哪些改动一碰就自动升级成跨模块任务,哪些是硬约束,哪些只是默认建议。这些东西必须被写清楚,而且最好是让 agent 直接能读。
因为测试只能告诉你“结果炸没炸”,它不能提前告诉 AI:“这里不是你该动的地方。”
契约文档干的,就是这个事。
渐进式披露
我最近在 SnowFox 这个独立应用上,对这件事的体会特别深。
那个仓库里的文档系统,本质上就不是为了介绍项目,而是为了让 coding agent 能安全开工。最前面先是 AGENTS.md,规定阅读顺序;然后是 docs/00-03,分别去讲约束模型、系统架构、模块地图、共享瓶颈点;再往后才是每个模块自己的文档和对应的 runbook。
这套东西最核心的思想之一,就是“渐进式披露”。
这个词听起来有点学术,说白了其实很简单:别一股脑把所有信息全塞给 AI。
因为 agent 的上下文空间是有限的。你把整个仓库背景、所有历史、所有恩怨情仇一次性喂进去,它不一定更聪明,很多时候只会更乱。上下文一多,它反而更容易抓错重点,更容易把局部任务升级成全局乱改。
所以正确的做法,不是“把所有信息都给它”,而是“在正确的时候,只给它当前任务真正需要的信息”。
先让它知道任务属于哪个模块。再让它去看这个模块相关的文档。如果命中了共享瓶颈点,再补读跨模块流程。如果只是一个局部 GUI 修改,就不要让它背着整个库存核心和迁移系统的历史包袱上阵。
这才是真正适合 agent 的文档方式。
并行 vs. 冲突
契约文档的另一个核心思想,就是模块化解耦。
这也不是为了画一个漂亮架构图,而是为了并行开发不互相踩踏。
AI 单兵已经很强了,但真正可怕的地方其实在并行。你可以开多个 agent 一起干活,但并行的前提不是“多开几个窗口”,而是边界必须足够清楚。否则效率不一定翻倍,冲突和覆盖倒是一定翻倍。
所以我现在越来越在意的一件事,就是把共享瓶颈点单独拎出来。
像 SnowFox 里,lib/tool_api.py、lib/tool_registry.py、app_gui/main.py、app_gui/tool_bridge.py 这种文件,就不是普通文件,而是模块汇合口。它们不是谁都不能碰,但你一旦碰它,就别再假装这是一个普通小任务了,这本质上已经是跨模块任务。
然后再按一种很朴素的方式去拆:能在模块内部解决的,就别上升到共享层。必须改共享入口的时候,只让一个 agent 去收口。其他 agent 围绕它,各改各自模块内部实现。
这套东西一旦立起来,AI 并行开发才开始真正变得可控。
文档也要进测试
更妙的一点是,契约文档最好别只靠大家自觉遵守。
我现在越来越喜欢的一种做法,就是让文档本身也进测试。把 Markdown 里的契约块做成机器可读,然后用 contract tests 去检查:哪些契约块必须存在,哪些路径在文档里写了但磁盘上根本不存在,哪些模块文档和 runbook 对不上,哪些共享瓶颈点少了一个都会直接报红。
到了这一步,文档就不再是“建议你看一看”的参考资料,而是正式进入系统本身。
测试负责防止功能回归,契约文档负责防止架构漂移,而文档测试则负责防止“契约本身变成一句空话”。
最后
所以我现在对 AI 编程的理解,已经慢慢从“让 AI 替我写代码”,变成了“我来设计它应该怎么写、写到哪、什么地方不能越界”。
如果继续用“马车”这个比喻,那我现在的感觉就是:
纯用 AI 写代码,是先让马跑起来。
回归测试,是给车装上刹车。
契约文档,是先把路、车道线和路标画出来。Code Elegance Reviewer,则像是时不时下车看看这辆车有没有越跑越歪、越跑越散架。
少了第一个,你根本不知道这匹马能跑多快。少了第二个,你迟早翻车。少了第三个,你就算不翻车,也会越跑越偏。而少了对代码优雅性和技术债的持续审视,车也许还能开,但会越来越笨重,越来越难修。
第一篇的时候,我还在问 AI 能不能帮我写。第二篇的时候,我开始问它写完以后怎么验收。到了现在,我越来越关心的已经变成:我到底有没有本事不被它带着跑。
模型当然重要,马力越大越爽。但马力不是全部。再强的马,没有缰绳、没有刹车、没有路标,照样能把车拉进沟里。
说白了,以前是自己拉车,现在是学会怎么驾车。
---
name: code-elegance-reviewer
description: 代码优雅性和架构质量审查与整改专家。用于发现并处理代码中的重复、第二真相源、契约漂移、共享瓶颈点过载、跨入口规则重复、冗余逻辑、不一致性、过度设计、紧耦合、技术债务等问题。当用户要求“审查代码质量”“找代码坏味道”“做重构建议”“清理技术债务”,或要求“先落档发现、再按契约文档+代码+测试逐项整改”时使用。专注于架构和设计层面的改进,忽略安全漏洞和纯代码风格问题。
---
# 代码优雅性审查与整改指南
聚焦影响可维护性、可读性、可扩展性和并行协作效率的设计层面问题。
优先处理会导致以下后果的坏味道:
- 改一个功能时不知道该改哪里
- 同一规则被多个入口各自维护
- 对外披露契约与真实执行语义漂移
- 共享入口、bridge、主装配层逐渐成为默认垃圾桶
- 测试锁定旧实现细节,放大边界漂移
默认忽略安全漏洞和纯代码风格问题。
## 模式选择
根据用户意图选择两种模式之一。
### 审查模式
在以下情况下使用:
- 用户要“review”“审查代码质量”“找坏味道”“给重构建议”
- 用户要先看问题清单,再决定是否整改
输出目标:
- 给出排序后的 findings
- 标出模块归属、共享瓶颈点、最小验证建议
- 不直接进入整改执行
### 整改模式
在以下情况下使用:
- 用户要求先落档发现,再逐项修复
- 用户要求契约文档先行
- 用户要求每修一项就补回归测试
- 用户要求最终全量测试和回填问题记录
输出目标:
- 先形成稳定问题记录文档
- 明确整改顺序和每项回归测试
- 一项一项完成闭环,直到回填最终验收
## 前置探索协议
先探索,再下结论。按以下顺序收集上下文:
1. 查找仓库级入口与契约信号:
- `AGENTS.md`
- `docs/00-*`、`docs/01-*`、`docs/02-*`、`docs/03-*`
- `docs/modules/*`
- `docs/runbooks/*`
- `CLAUDE.md`
- `tests/`、测试运行手册、契约测试说明
2. 若存在顶层契约文档,先按仓库规定顺序阅读,再做模块归类。
3. 先判断任务属于哪个模块,再判断是否命中共享瓶颈点。
4. 命中共享瓶颈点时,自动按跨模块问题处理。
5. 识别项目形态,再选择审查主线:
- 单仓多模块应用
- GUI / Agent / Core 多入口系统
- 多服务后端系统
- 缺少契约文档的普通仓库
6. 仅在项目确实是多服务 HTTP 系统时,重点展开 health、CORS、跨服务路由等扫描。
优先使用以下命令:
- `rg --files`
- `rg -n "pattern"`
- `git grep -n "pattern"`
避免默认用 `grep -rn` 作为首选。
## 契约式披露原则
先判断什么该写进契约,什么不该冻结。
### 该紧的地方要紧
优先收进硬契约或顶层设计的内容:
- 跨模块边界
- 稳定真相源归属
- 高风险协作面
- 对外 API / 工具 / 桥接入口
- 共享瓶颈点 ownership
- 影响多个入口的一致性规则
### 该松的地方要松
不要误写进硬契约的内容:
- helper 命名
- 局部类拆分
- 文件如何细分
- use case / service / helper 的具体落点
- 内部实现选用 dataclass、dict、descriptor 还是其他表达方式
### 判定规则
- 如果某项变化会影响多个模块的协作正确性,把它收紧。
- 如果某项变化只影响模块内部实现自由度,让它保持松弛。
- 如果边界仍不稳定,明确标注“暂定”或“不稳定区域”,不要把猜测包装成定论。
## 并行友好原则
优先保护并行开发效率,而不是只盯单文件优雅度。
- 按模块切片并行,不按“文件夹看起来相近”并行。
- 同一批任务里,只允许一个 agent 持有共享瓶颈点的写权限。
- 共享瓶颈点优先拆为:
- 契约/共享层变更
- 消费者接入
- 测试补齐
- 不要鼓励多个 agent 同时改同一装配入口、bridge、统一注册表或主窗口协作层。
## 系统层问题分类
把跨文件、跨模块、跨入口、跨桥接层、跨服务的问题都视为系统层。它们优先于单文件问题。
### S1. 跨边界不一致
审查以下问题:
- 相同职责在不同模块/入口/服务中用不同方式实现
- 相同概念命名不统一
- 错误返回、异常语义或对外响应结构不一致
- 同一个能力在 GUI、Agent、CLI、API、后台任务间行为不一致
- 多服务项目中的 health、auth、timeout、retry、headers、路由路径不一致
包含但不限于:
- 路由漂移
- 统一端点替代旧端点后只有部分调用方完成迁移
- sync/async 风格混用导致行为偏差
### S2. 第二真相源 / 契约镜像漂移
优先寻找以下高风险模式:
- 路由定义和能力文档分离维护
- 工具注册表和展示列表分离维护
- 执行逻辑和参数说明分离维护
- 运行时能力文档与真实实现分离
- 配置注册表与实际消费语义分离
如果一个系统既有“执行真相源”,又有“对外披露镜像”,默认优先怀疑这里。
### S3. 共享瓶颈点过载
重点关注:
- 装配层开始承载业务规则
- 主窗口协作层开始承载领域校验
- bridge / facade / registry 成为默认需求落点
- 高冲突文件持续变厚,且修改时常需要跨模块一起改
这类问题会直接降低并行开发效率,应优先处理。
### S4. 跨入口规则重复
把以下模式视为高优先级问题:
- GUI / Agent / Core 各自维护一套校验或归一化
- API / CLI / 后台任务各自维护一套同领域规则
- 预演、dry-run、输入清洗、写前校验、参数别名解析分散多处
优先把规则收口到真相源;允许薄适配层存在,但不要允许多份领域语义并存。
### S5. Shotgun Surgery
重点关注:
- 一个业务变更需要同时改 3+ 个文件或 2+ 个模块
- 常量、路径、能力列表、字段语义在多处硬编码
- 自引用列表导致“新增/删除一个组件要同步改多处”
### S6. 层级违反
重点关注:
- 表示层直接写存储或领域规则
- 协调层跨越层级直调内部细节
- 共享层对下暴露了本应私有的实现细节
- 模块边界被临时捷径绕开
### S7. 契约披露不当
重点关注两种错误:
- 该收紧的边界没有写进契约,导致实现漂移
- 不该冻结的内部实现被误写成契约,导致重构空间被锁死
## 单体层问题分类
### 1. 重复代码
重点关注:
- 同一规则或转换逻辑存在多份副本
- 同一算法在多语言实现中各维护一份
- 同一字段语义在 validator、executor、presenter 中各定义一遍
判断标准:
- 应消除:两处代码会因为同一个原因一起变化
- 可保留:两处代码看似相似,但代表不同概念
- 可保留:测试代码中的重复 setup
额外规则:
- 规则重复优先级高于展示重复
- 适配层的薄重复只有在语义开始分叉时才升级
### 2. 废弃代码
重点关注:
- 无调用者的导出、函数、方法
- 被注释掉的大段旧代码
- 无消费方的兼容 facade
- 永远不生效的参数、状态或分支
### 3. 冗余逻辑
重点关注:
- 同样的数据转换重复执行
- 仅做透传的多层包装
- 可以合并的条件分支
- 重复 try/except 或重复 guard
### 4. 不一致性
重点关注:
- 相同概念术语不统一
- 相似逻辑实现风格不统一
- 错误处理语义不统一
- 同一模块内 sync / async 或返回结构混用
### 5. 过度复杂化
重点关注:
- 简单需求被策略模式、工厂、注册表过度包装
- 为未来可能性付出当前复杂度
- 参数过多但大多数调用不需要
- 可以用标准库却发明复杂实现
### 6. 紧耦合
重点关注:
- 硬编码具体实现
- 循环依赖
- 全局状态过多
- 通过路径或导入 hack 绕过统一入口
### 7. 经典坏味道
优先识别:
- Feature Envy
- Primitive Obsession
- Boolean Blindness
- Temporal Coupling
### 8. 技术债务
重点关注:
- TODO / FIXME / HACK 长期留存
- schema 迁移残留
- mock 与真实行为漂移
- 无法解释原因的临时 workaround
### 9. 性能反模式
重点关注:
- N+1 查询
- 可缓存结果反复计算
- 异步上下文中的同步阻塞调用
- 重复创建 HTTP 客户端
- 嵌套事件循环
- 构造函数中的无效参数
## 严重程度定义
### 高
优先判为高的情况:
- 第二真相源导致契约或能力漂移
- 共享瓶颈点过载,显著影响并行开发
- 同一领域规则在多个入口重复维护
- 路由漂移、契约漂移、跨边界行为不一致
- 严重紧耦合或循环依赖
- 异步上下文中的同步阻塞调用
### 中
优先判为中的情况:
- 局部重复或中度散弹式修改
- 命名/术语不一致
- 轻度过度包装
- 契约披露略松或略紧,但尚未造成明显故障
### 低
优先判为低的情况:
- 个别魔法数字
- 轻微风格/命名不统一
- 可以更简洁但尚未造成维护风险的写法
## 修复优先级
按以下顺序推进:
1. 第二真相源 / 契约镜像漂移
2. 跨边界不一致
3. 共享瓶颈点过载
4. 跨入口规则重复
5. Shotgun Surgery
6. 紧耦合 / 层级违反
7. 废弃代码
8. 性能反模式
9. 过度设计 / 冗余逻辑 / 一般不一致性
10. 低优先级技术债
## 审查流程
### Phase 0: 建立地图
1. 用 `rg --files` 看顶层结构。
2. 找出模块边界、稳定入口、共享层和测试入口。
3. 识别共享瓶颈点和高冲突文件。
4. 判断仓库是否有契约文档骨架。
### Phase 1: 系统层扫描
1. 对比多个入口、多个模块、多个消费者是否维护了相同规则。
2. 搜索注册表、能力表、文档表、schema、manifest、bridge、facade、入口装配点。
3. 搜索可能的第二真相源:
- `rg -n "capabilities|manifest|registry|routes|allowed|supported|schema|normalize|validate|dry_run"`
4. 搜索共享瓶颈点是否持续吸收流程与规则:
- `rg -n "main.py|bridge|registry|flow|window|bootstrap|compose|wire"`
5. 仅在多服务项目中,再展开跨服务契约与路径检查。
### Phase 2: 单体层深入
1. 通读可疑模块,确认职责边界。
2. 判断重复是否是真重复,还是巧合相似。
3. 判断抽象层是否真的必要。
4. 用 `rg -n "TODO|FIXME|HACK"`、调用点搜索、导出搜索验证债务和废弃代码。
5. 检查测试是否锁定旧内部结构,而不是稳定接口。
### Phase 3: 汇总
1. 先给出系统层问题。
2. 合并同类问题,不做碎片化罗列。
3. 对每个问题明确:
- 模块归属
- 是否命中共享瓶颈点
- 是否需要先改契约文档
- 最小验证建议
4. 若多个问题共享根因,补一段根因评估:
- 主因
- 放大器
- 固化器
## 审查模式输出格式
每个问题按以下格式报告:
```text
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
严重程度:高 / 中 / 低
模块归属:模块名
共享瓶颈点:否 / 是(列出入口或共享文件)
类型:系统层 / 单体层 + 具体类型
位置:文件路径:行号(跨文件问题列出所有相关位置)
问题:一句话描述问题
详情:
说明哪里不优雅、为什么会出问题、为什么现在值得修
建议修复:
给出重构方向或真相源收口方案
最小验证:
列出建议优先跑的最小测试集、契约测试或回归场景
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
如果用户明确要求 review,优先输出 findings,不自动展开整改实施。
## 整改模式协议
在整改模式中,按以下顺序执行:
1. 先确定问题记录文档路径,并保持稳定。
- 优先复用仓库已有 review / remediation 文档路径。
- 若没有约定路径,在项目内选择一个稳定的 docs 或 review 路径,不要把记录散落在聊天里。
2. 先把 findings 落档,再开始改契约或代码。
3. 先判断契约文档是否要改:
- 若触碰稳定边界、真相源归属、共享瓶颈 ownership,先改契约文档。
- 若只是模块内部实现细化,不要误改顶层契约。
4. 一次只修一个 backlog item。
5. 每完成一项修复,立刻补对应回归测试并跑最小测试集。
6. 测试失败时,先修到通过,再进入下一项。
7. 所有项完成后,再跑全量测试或约定的更高层测试集。
8. 把每一项整改结果回填到同一份问题记录文档。
## 整改模式记录模板
优先使用以下结构:
```markdown
# YYYY-MM-DD Code Elegance Review / Remediation
## 任务归属
- 主要模块:
- 共享瓶颈点:
## 契约收口原则
- 该紧的地方:
- 该松的地方:
## 问题清单
### R1. 问题标题
- 严重程度:
- 模块归属:
- 共享瓶颈点:
- 相关路径:
初始问题:
整改目标:
契约文档:
- 需先更新 / 无需更新
- 相关文档路径:
实施状态:待修复 / 已完成 / 风险待定
修改记录:
回归测试:
现状:
跨模块风险:
## 最终验收
- 全量测试命令:
- 全量测试结果:
- 文档回填状态:
```
在整改模式中,强制补齐以下字段:
- 模块归属
- 共享瓶颈点命中情况
- 契约文档是否需先改
- 每项最小回归测试
- 最终全量验收结果
## 降级策略
如果项目缺少完整契约文档骨架,不要中断;按以下方式降级:
1. 明确报告缺失项:
- 缺 `AGENTS.md`
- 缺 `00-03` 顶层文档
- 缺模块地图
- 缺共享瓶颈点文档
- 缺测试运行说明
2. 改为通用审查/整改流程:
- 用目录结构和导入关系推断模块归属
- 用入口文件、bridge、registry、main、bootstrap 推断共享瓶颈点
- 用现有测试目录和构建脚本推断最小验证集
3. 把这些推断标记为“暂定”,不要伪装成仓库官方契约。
## 忽略项
不要报告以下问题:
- 安全漏洞
- 纯代码风格
- 仅因个人审美产生的架构偏好争议
- 测试代码中为可读性保留的重复 setup
- 尚无证据表明会造成漂移或维护风险的轻微格式差异
## 审查原则
- 先看系统,再看文件。
- 先看真相源,再看局部实现。
- 先保护并行协作,再谈局部优雅。
- 以证据驱动,不凭印象给问题定性。
- 区分“真正重复”和“巧合相似”。
- 优先让“改一个功能时不会踩坑”,而不是追求抽象上的完美。
harness工程吗,文章的提示词很值得学习啊
哈哈哈,也就是有人起了 Harness 这个名字,但无论起不起这个名字,其实实践中都会注意到要有这些事情。
我讨论的重点不是提示词,而是架构中必须要有文档、代码和测试三者之间的相互约束。