KEEL · 龙骨 · A CURRICULUM FOR THE AI ERA
02 · 场景 A:在已有代码库上迭代 — keel 龙骨
这一章回答:怎么在一个你不完全了解(或者 AI 完全不了解)的成熟项目上安全地改东西。
这一章回答:怎么在一个你不完全了解(或者 AI 完全不了解)的成熟项目上安全地改东西。
已有代码库的核心风险不是难度,而是AI 看不见"哪些东西不能碰"。它能读全部代码,却读不到隐含约定、历史包袱和"这段代码丑但有原因"。
一、技巧:先考古,再落笔
1. 改之前先让它答"三问",并且不许改文件
只做调研,不要改动任何文件。回答:
- 这个功能应该怎么接入?发生在哪个文件哪个函数?给出 file:line
- 现有的同类能力是怎么做的?指一个最接近的现有模块作为参照
- 改动会影响哪些调用方?列出调用点清单
拿到答案后,自己抽验一两个 file:line。这一步能挡掉大部分幻觉——这是成本最低、回报最高的一次投入。
2. 用"同类锚点"替代抽象描述
不要说"要写得优雅一点",要说"照着 xxx_service.py 的结构写,包括它的重试、日志和异常包装方式"。已有代码库里最好的规范,是一份已经通过评审的实现。
3. 一个 commit 一个意图
开工第一件事:把仓库里既有的未提交改动单独提交一次。这样新改动与旧改动不混——回滚粒度是在动手前决定的,不是出事之后。
4. 先立验收,再动手
改之前先确定一条可以复现的判断命令:跑测试、跑构建、跑项目自有的内容校验脚本,或一条能明显看出对错的 curl。AI 看不到你们的 CI,你给的这条命令就是它的 CI。
5. diff 审阅只问三个问题
- 这是我要的改动吗?
- 有没有顺手动的无关文件(格式化、重命名、"顺手优化")?
- 有没有超出预期的删除?
第三条最致命——AI 常用"重写整个文件"来达成小目标,静默删掉别人写的边界处理。GitClear 的数据给出了这种行为的大样本后果:重构占变更行比例从 21% 跌到 3.8%,而对旧代码的维护下降 74%。它在"完成任务",不在"维护系统"。
6. 保留回退点
改动前:git status 干净 + 一个新分支;改动后立刻 git diff --stat 抽查。涉及发布配置类改动时,先确认备份/回滚目录存在再执行。
二、注意点
| 维度 | 做法 |
|---|---|
| 变更范围 | 明确写"本次只改 X、Y 两个文件,其余一律不动,包括格式化和重命名" |
| 依赖 | 不要让它"顺手升级依赖"。版本变更必须是有意为之的独立改动 |
| 一致性 | 指定样板文件;要求沿用既有异常处理、日志格式、命名风格、注释语言 |
| 回归面 | 让它先 grep 出全部调用点并列出清单,改完逐个确认 |
| 并发与幂等 | 已有系统里最容易漏的是重试、并发、部分失败。明确要求它指出这些边界 |
| 门禁 | 项目自带的 lint / test / 内容校验脚本,改完必须真跑一遍,不能只看 AI 说"应该没问题" |
三、避雷清单
- ❌ AI 臆造 API:它给出的函数名、配置项、参数名必须能在仓库里 grep 到或官方文档里查到。给不出 file:line 的结论,一律当没说。
- ❌ 擅自大范围重构:它倾向于"既然改了,不如重构成更合理的样子"。这是已有项目最大的技术债来源——一次几百行的重构,会把 review 成本全部转嫁给你。
- ❌ 破坏隐式约定:很多项目的约束藏在脚本里(生成脚本、校验脚本、发布脚本)。AI 改了代码却没过脚本,或压根不知道这些脚本存在。让它执行一次项目自带的校验/发布流程,比它的口头保证有效得多。
- ❌ 动构建与发布配置:base path、环境变量默认值、CI 注入项——这类配置的默认值常常和本地不一样,本地对了线上错。改这类东西必须显式问清"默认值是什么、线上由谁注入"。
- ❌ 半成品堆积:接受一次"先留个 TODO,后面补"就会有一百次。要求它写完要么能用,要么明确列出未完成项由你确认。
- ❌ 忽略失败路径:它默认写 happy path。明确要求:超时、空值、权限不足、依赖不可用各自怎么处理。
动手:可观察结果
挑一个真实需求,按下列节奏走一遍,并记录:
| 环节 | 判断标准 | 你的数据 |
|---|---|---|
| 考古三问答完 | 抽查 2 个 file:line,全部属实 | |
| 影响面清单 | 它列出的调用点,与你 grep 的结果一致 |
|
| 改动完成 | git diff --stat 的文件数与预期一致,无多余文件 |
|
| 门禁 | lint / test / 项目自带校验脚本全部通过(截图或输出) | |
| 回退 | 能在 1 分钟内回到改动前状态 |
输出物:一次带 file:line 证据的调研记录 + 一个意图单一、可回退的 commit。
故障注入:四种坏法,各做一次
在练习仓库里故意让它犯这四个错,记住每种的表现形态(真实项目里它们会更隐蔽):
| 注入方式 | 你会看到什么 |
|---|---|
| 不给范围句,只说"优化一下这段代码" | 一次性改动多个文件,包含重命名与格式化,diff 无法审阅 |
| 让它"顺便把相关的都改了" | 引入无人要求的行为变更,测试没覆盖到,上线后才暴露 |
| 让它改一个不存在的配置项 | 它可能"顺着你的说法"编出这个配置的用法,而不是指出不存在 |
| 让它修改发布脚本,但不提供线上环境变量说明 | 本地一切正常,线上资源 404 或整页白屏(见下一章真实案例) |
第四种是最贵的一类:代码 review 查不出来,只能靠"跑一遍真实流程"。
自测题
- 为什么要求是 AI 给出
file:line证据,而不是让它描述实现方案? - diff 审阅的第三个问题为什么必须是"有没有超出预期的删除"?举一个真实场景。
- GitClear 的"重构率跌到 3.8%"与 AI 的"顺手重构"看起来矛盾吗?分别指什么?
- 项目自带的校验脚本为什么是 AI 最容易忽略的东西?它怎么会知道?
- 如果这次改动一周后被发现有线上问题,你的回退路径是什么?现在就能说出来吗?