Appearance
用 Claude Code 实践古法编程:P1/P2/P3 三阶段实战
古法编程的三大戒律落到 Claude Code 使用上,对应三个具体阶段:P1 预设(你写架构和契约)、P2 受控实现(你拆 Prompt,AI 填充一个函数一个函数地来)、P3 验证(测试通过不算完,你能解释才算完)。每个阶段都有对应的操作要点和常见错误。
问题的根源在哪里
用 Claude Code 翻车,通常不是因为 AI 能力不够,而是因为 Prompt 写得像下命令:
"帮我写一个用户登录功能"AI 于是开始猜:用 JWT 还是 Session?密码怎么存?错误怎么返回?超时怎么处理?这些决策 AI 全做了,你不一定知道,你也不一定认同,但代码已经在那里了。
古法编程不是少用 AI,而是改变你和 AI 的权责分工:你做决策,AI 做实现。
下面用一个真实场景演示三个阶段。
P1 — 预设:AI 动手前你要先做的三件事
1. 写清楚这个模块的意图
不是一句话,是一段话。覆盖:这是什么、解决什么问题、不包含什么。
示例(薯条的 frontmatter 解析模块):
markdown
## parseFrontmatter 模块
用途:解析 VitePress Markdown 文件的 YAML frontmatter,分离元数据和正文。
负责:
- 检测 --- 分隔符
- 解析 key: value 格式的字段
- 返回结构化的 frontmatter 对象和干净的 body
不负责:
- YAML 嵌套对象(只处理一级键值对)
- 多行字符串
- 写回文件这段说明写完,你已经做出了几个架构决策:不引入 YAML 解析库、只处理一级结构、职责边界清晰。
2. 先定接口类型,不是先写实现
typescript
// 先写这个,不是让 AI 直接写实现
interface Frontmatter {
title?: string;
description?: string;
keywords?: string;
[key: string]: string | undefined;
}
interface ParseResult {
fm: Frontmatter;
body: string;
hasFm: boolean;
}
// 函数签名已经是一份契约
declare function parseFrontmatter(content: string): ParseResult;类型写完,你已经确定了输入和输出的形状。AI 实现时不会偏离,你 review 时也有对照。
3. 写至少一个核心测试用例
typescript
// 边界情况比正常情况更重要
test('无 frontmatter 时返回空对象和原文', () => {
const result = parseFrontmatter('# Hello\n\nsome content');
expect(result.hasFm).toBe(false);
expect(result.fm).toEqual({});
expect(result.body).toBe('# Hello\n\nsome content');
});
test('正常解析 title 和 description', () => {
const content = '---\ntitle: 测试标题\ndescription: 描述\n---\n# Body';
const result = parseFrontmatter(content);
expect(result.fm.title).toBe('测试标题');
expect(result.body).toBe('# Body');
});这三步完成后,你才打开 Claude Code。
P2 — 受控实现:怎么拆 Prompt
错误写法(一次性扔给 AI)
帮我实现 parseFrontmatter,解析 Markdown 的 frontmatter,
要处理各种边界情况,返回 frontmatter 对象和 bodyAI 会给你一段能跑的代码,但里面有多少假设、多少边界处理方式,你不知道。
正确写法(一次只实现一件事)
我已经有了接口定义和测试用例(附上 P1 产出的内容)。
请实现 parseFrontmatter(content: string): ParseResult
约束:
- 不引入外部 YAML 解析库,用正则或字符串操作
- frontmatter 检测:以 ---\n 开头,找到第二个 ---\n 为止
- 解析规则:每行 key: value,value 去掉首尾引号和空格
- hasFm 为 false 时,body 返回原 content
不需要处理:YAML 嵌套、多行字符串、注释关键差异: 你在 Prompt 里已经做出了所有决策(不用外部库、正则/字符串操作、边界定义)。AI 只是在你的约束内填充实现,没有决策空间。
拆 Prompt 的核心原则
一次 Prompt 只实现一个可验证的函数。 如果你在一次 Prompt 里让 AI 写三个函数,你就需要同时 review 三份代码,出了问题很难定位到哪里偏了。
Prompt 里的约束越具体,AI 的决策空间越小,你对代码的掌控越高。
P3 — 验证:测试通过不是终点
AI 写出来的代码,你要能做到:
- 说出每段逻辑在处理什么 — 不是逐行注释,是你能用自己的话描述
- 知道边界情况是否覆盖到了 — 你写的测试用例通过了,没写到的情况呢?
- 能独立 debug — 如果测试失败,你能判断是实现错了还是测试用例写错了
反例: 测试全通过,但你只是在不断修改 Prompt 直到绿了,不知道为什么绿了。这是 vibe coding。
正例: 测试失败,你看了一眼知道是正则没处理 Windows 换行符(\r\n),加一个边界测试,修一行正则,重新绿了。全程没问歌 AI。
P3 不是新增工作,是你主动去理解 P2 产出的代码,确认自己掌握它。
完整流程对比
Vibe Coding 的路径:
- 想到要做什么
- 让 AI 写
- 跑一跑,好像能用
- 上线
- 出问题,让 AI 修
- 循环
古法编程的路径:
- 想到要做什么
- P1:写意图 + 接口类型 + 测试用例(10-20 分钟)
- P2:逐函数给 AI,每次都有明确约束
- P3:能解释每个函数在做什么,再 merge
- 上线
- 出问题,知道从哪里下手
P1 的投入让 P3 变得容易,也让以后维护代码的人(包括未来的你)不用重新猜一遍这段代码的意图。
FAQ
Q: P1 要写多少才够?
够你自己把意图解释清楚就行。一段话 + 接口类型 + 一两个测试用例,通常 15-20 分钟。不需要完整的设计文档,但不能是空的。
Q: 每次都这样做,效率不会很低吗?
对新功能和核心模块坚持做。小的工具函数、配置文件、脚手架代码,P1 可以简化。古法编程是原则,不是仪式,关键在于高风险代码不能省。
Q: AI 没有遵守我的约束怎么办?
这说明你的约束还不够具体。遇到这种情况不要直接让 AI "重新写",而是找出它偏离的地方,在下一条 Prompt 里把那个边界说清楚。每次纠偏都是在完善你的约束,以后同类需求会越来越顺。