把它接进真实项目之后,我立了五条规矩
上个月第一次用它,最强烈的感受是习惯被改了——工具没换几个,我的写法变了。这一个月我试着把它真正接进手上的项目:不是玩具,是有三年历史、有测试、有 CI、有别人要看我 PR 的那种项目。
结论先放这儿:能用,但要立规矩。没有规矩的话,它会让你的仓库变乱,而且乱得很隐蔽。
玩具项目和真实项目的差别
我用一个小项目试的时候,什么都顺。换成真实项目,问题立刻出来了:
真实项目有历史。十几种写法混在一起,因为中间换过两轮技术选型。它不知道哪个是”现状”、哪个是”遗留”。
真实项目有约定。命名习惯、目录分层、错误处理方式、日志格式,这些东西人待久了会本能遵守,工具不知道。
真实项目有不能动的东西。数据库迁移脚本、发布流程、CI 配置、跟外部系统对接的适配层——这些地方改错的代价不是”跑个测试”,而是”线上出事”。
真实项目还有人。我改的代码有人 review,所以我得能解释每一行的来龙去脉。工具给的代码,解释不清就等于没写。
我立的几条规矩
一、先写项目约定文件。
我在仓库根目录放了一个说明文件,写清楚:测试怎么跑、用哪个 lint、不许新增第三方依赖、哪些目录不要动、错误处理统一走哪个封装。
这几条写进去之后,效果非常明显。它之前总爱顺手引入新库,写进去之后就不引了。
二、任务一次一个,目标是可验证的。
“把这个模块重构一下”这种指令我试过一次,回来的 diff 有三四百行,我看了二十分钟还是不敢合。
后来改成:”把 parse_config 里三个重复的取值逻辑提成一个函数,不要改动调用方的行为。”——范围小、验收标准明确,diff 十几行,一眼能看完。
三、让它先给方案,再动手。
这句话能省很多时间。它默认的倾向是直接改,但你说”先说说你打算改哪几个文件、为什么”,它就会先列计划。
计划里有不认同的地方,这时候拦下来最便宜。等它改完再说”不是这个意思”,diff 就得回退。
四、改之前先补测试。
我现在流程固定成:如果某块代码没有测试,先让它补测试(测的是现有行为,不是它想实现的行为),跑通并提交。然后才让它改。
这么做有一个额外好处:写测试的过程会暴露”我自己也没搞清楚这段逻辑”的地方。
五、看 diff,别只看结果。
它说”改好了”,测试也过了,不代表改的是我要的。我现在的做法是:过一遍 diff,重点看它有没有顺手改无关文件、有没有把错误处理吞掉、有没有为了通过测试而放宽断言。
一次具体的翻车
有一次它改一个边界情况处理。改完测试通过了,我差点就合了。
后来发现它顺手把那条测试里的断言从”应该抛异常”改成了”应该返回 None”。测试当然过——因为测试也被它改了。
这件事让我意识到它的问题不是”能力不行”,而是它会朝目标收敛:你告诉它”让测试通过”,它就会想办法让测试通过,包括修改测试。这不是恶意,是它理解了你的目标,但没理解你的意图。
从那以后我加了一条:测试文件默认只允许新增,不允许修改已有断言。要改断言,得先跟我说清楚为什么。
效果和边界
实实在在提速的地方有两块:机械性的大范围改动(改函数签名、统一命名、批量替换),和陌生代码库的认路(让它读完告诉我这块是干嘛的,比我翻半天快)。
需要人盯着的地方同样明确:架构决策、性能敏感的代码、跨系统的边界的处理、以及任何涉及数据和权限的地方。
还有一个隐性代价:review 的负担从”自己写”变成了”看它写”。写代码的时候脑子是连续的,读它写的代码时,脑子要在它的思路和我的预期之间来回切。累的方式不一样。
所以我现在用它有个很简单的判断标准:这件事如果出错,我能不能在五分钟内发现?能,交给它;不能,我自己写。