xyz的技术笔记

Vibe Coding 实战:什么时候该放手,什么时候必须盯着

2026-11-10 · Vibe Coding · 工作流

“凭感觉写代码”最容易被误解成“不用理解代码”。我这一年的体会恰好相反:放手的前提,是你有一套不依赖读源码、也能判断“这东西对不对”的办法。这篇记录我现在用的做法。

1. 先划边界:什么活适合放手

我用“错了的代价”和“能不能快速验证”两个维度给任务分类:

验证快验证慢
代价低完全放手:一次性脚本、格式转换、小页面、数据清洗放手,但留好回退:定时任务、报表生成
代价高放手写,我来验:解析器、计费公式、权限判断不放手:数据删除、迁移、权限与认证、涉及钱的逻辑

第一格的活,我基本一句话描述完需求就开始运行结果;最后一格的活,模型只负责起草和解释,决定由我下。大部分日常工作落在中间两格,这也是下面这套流程主要解决的问题。

2. 工作循环:切片、运行、验证

一次性让模型“把整个系统写出来”,基本一定会得到一个看起来完整、实际每个环节都有点问题的东西,而且问题互相掩盖,很难定位。我现在的循环是:

  1. 写一页需求:输入是什么、输出是什么、不做什么。哪怕只有五行。
  2. 切成能独立运行的小片:每一片做完都能单独跑出结果。比如“读文件并打印前 10 条记录”就是第一片,而不是“完成解析器”。
  3. 每片做完先运行,再往下走:不要攒着一起测。
  4. 每片用一个验证手段确认(见下一节),通过了才提交。
  5. 提交一次,写一句话说明:出了问题可以二分回退。
经验:切片的粒度,以“出错时我能在两分钟内说清是哪一片的问题”为准。如果做不到,说明片太大。

3. 验证阶梯:从便宜到昂贵

验证方式按成本排成一个阶梯,能用低的就不用高的,但高风险的片必须往上爬。

第 1 级:运行它

最便宜,也最容易被跳过。模型说“已完成”不算数,亲眼看到输出才算。

第 2 级:看输出的“形状”

不看每个值,只看总体是否合理:行数、取值范围、有无空值、顺序是否符合预期。几行断言就能做到:

import pandas as pd

df = pd.read_csv("out.csv")
assert len(df) > 0, "结果为空"
assert df["voltage"].between(2.5, 4.3).all(), "电压越界,可能字节序或比例错了"
assert df["seq"].is_monotonic_increasing, "序号倒退,可能是记录对齐错位"
assert df.isna().mean().max() < 0.01, "有列大量为空"
print("形状检查通过:", len(df), "行")

这类断言写起来快,却能抓住“代码能跑、结果全错”的大部分情况。

第 3 级:用已知答案对照

找几条你手工能算出结果的样本,让程序跑一遍,对比。我习惯留一个 fixtures/ 目录,放 3 到 5 个输入和人工核对过的期望输出,每次改动后全部过一遍。

第 4 级:不变量与反向检查

写出“无论输入是什么,这一定成立”的性质,例如“拆分后各部分之和等于总数”“排序后再排序结果不变”。对解析、统计、金额类逻辑特别有效,因为模型最容易在边界条件上出错。

第 5 级:读 diff

最贵,留给高风险的片。读 diff 时我只问三件事:它改了哪些我没让它改的文件?有没有新增依赖?有没有吞掉异常?

4. 四类静默失败

vibe coding 的麻烦不在于报错,报错好办。麻烦在于“静默”:程序不崩,结果却不对。我总结了最常见的四类。

① 编造接口

调用一个听起来存在、实际不存在的函数或参数,或者用了旧版本的写法。对策:涉及第三方库或 API 的片,让模型先贴出它依据的官方文档链接或版本号,第一次运行时留意告警(尤其是 DeprecationWarning)。

② 悄悄兜底

遇到异常时返回一个默认值,而不是报错。例如:

# 看起来很稳健,实际会把所有失败伪装成“0 条数据”
try:
    rows = parse(path)
except Exception:
    rows = []

对策:在需求里写明“遇到无法处理的输入必须报错并带上位置信息,不要吞异常”;读 diff 时专门搜 except 和 or []、or 0。

③ 为通过测试而写

模型发现测试过不了,就去改测试,或者针对测试用例写死答案。对策:测试和期望输出由我提供,且告诉模型“不允许修改 fixtures/ 与测试文件”。

④ 范围蔓延

让它改一处,它顺手“优化”了十处:重命名变量、换库、调整目录结构。每一处单看都合理,合起来你已经无法审阅。对策:每次提示都写“只改某某文件,不要重构”;提交前看 git diff --stat,改动文件数超出预期就退回重来。

5. 上下文管理:让模型“记得”项目

模型没有跨会话的记忆,每次对话都像新来的同事。我用两个办法弥补:

  • 项目根目录放一份简短的说明文件(我叫 CLAUDE.md):技术栈、目录结构、怎么运行与测试、不要做什么。不超过一屏。这份文件的价值在“约束”而不在“介绍”。
  • 把踩过的坑写成一句话规则放进去。例如“日志文件不要按时间戳排序,设备时钟会复位”“数据库放在网站目录之外”。同一个坑不该踩两次,也不该让模型再踩一次。

反过来,对话越长越要警惕。会话里塞满了过期的尝试、被推翻的方案,模型会被这些噪声带偏。我的习惯是:一个功能片做完提交后,开新会话再做下一片,只带说明文件和必要的代码。

6. 提示词的写法

我现在写需求基本按下面这个模板,写得越具体,返工越少:

目标:把 input/*.csv 合并成一个按日期排序的 out.csv
输入:每个文件第一行是表头,列为 date,device,value;date 格式 YYYY-MM-DD
输出:同样三列;重复行(date+device 相同)保留 value 较大的一条
约束:
- 只改 merge.py,不要新增依赖,不要改 fixtures/
- 遇到格式不对的行:停止并报错,带上文件名和行号,不要跳过
验收:python merge.py && python check_shape.py 通过;fixtures/ 下 3 个样例输出与期望一致

其中“验收”一行最关键:它把“做完了”从模型的主观判断,变成一个可以运行的命令。

7. 必须亲自盯着的地方

  • 认证与权限:谁能看什么、谁能改什么。模型写的权限判断常常“看起来对”,但边界情况(空值、大小写、越权访问他人记录)容易漏。这部分我逐行读,并手动用不同身份测试。
  • 涉及删除和覆盖的操作:先让它只打印“将要删除什么”,确认后再执行。
  • 密钥与凭据:永远不让密钥出现在提示词、代码和提交里。用环境变量或单独的配置文件,并确认已加入忽略列表。
  • 金额与计费公式:用手算的样本逐条核对,不信“看起来对”。
  • 对外暴露的接口:输入校验、速率限制、日志里不能带敏感信息。

8. 一页检查清单

阶段问自己
开始前这活错了的代价多大?能快速验证吗?需求写成一页了吗?
每一片能单独运行吗?有一个可执行的验收命令吗?
每次提交前git diff --stat 改动范围符合预期吗?有吞异常吗?有新依赖吗?
上线前密钥在代码外吗?危险操作有“先预览”吗?权限用不同身份测过吗?
出问题后是哪一片?能二分回退吗?这个坑写进说明文件了吗?

最后一句:vibe coding 把“写代码”的成本压得很低,但把“判断对不对”的成本整个留给了你。想清楚怎么验证,比想清楚怎么写更重要。

← 返回笔记列表