这个词描述什么
“Vibe Coding”是对一种工作流的非正式称呼:开发者用自然语言表达意图,AI Agent 提出、修改或解释代码。它不是软件工程的替代品,也不保证结果可执行、安全或可维护。
有效的边界可以写成:
意图 -> 受约束的提案 -> 检查 Diff -> 运行证据
-> 处理失败 -> 修改或拒绝 -> 经过常规控制后合并
模型可以加速草稿,Repository、Tests、Policy 检查和负责人决定变更是否可接受。
适合与不适合的地方
AI 辅助通常适合:
- 探索陌生模块;
- 为窄范围 Adapter 或 Test 生成初稿;
- 解释现有错误路径;
- 提议重复性重构;
- 比较实现方案。
它不能替代:
- 威胁建模和授权设计;
- 数据所有权与留存判断;
- Migration 和外部副作用 Review;
- 医疗、法律、金融或安全关键行为验证;
- 维护一个团队无人能解释的系统。
当任务有精确 Oracle 时,优先使用更小的确定性工作流。只有额外自主能力值得承担上下文和控制风险时,才使用 Agent。
写出任务契约
把“构建整个应用”替换成契约。可复现的 Vibe Coding 实战流程展示了如何把契约应用到完整 Go 示例:
Goal:
为只读 invoice list Endpoint 增加分页。
In scope:
packages/billing/src/list_invoices
Out of scope:
Schema Migration、定价、授权 Policy、部署
Inputs and invariants:
保留 Tenant 与 Principal 检查;
拒绝无效 Cursor;
不暴露完整客户记录。
Evidence:
相关 Tests、Type Check、Formatter 和 Diff 摘要。
Escalate:
新依赖、外部写入、凭证访问或公共 API 变化需要升级。
契约为模型提供有用上下文,却不允许模型文本重定义身份、所有权、定价或权限。
上下文是一种预算
上下文工程既是预算决策,也是信任决策。上下文越多可能噪声越大,也可能暴露数据,因此要按信任分开:
| 上下文 | 示例 | 处理方式 |
|---|---|---|
| Canonical | 已提交 Schema、Tests、Build Scripts | 记录 Revision 与负责人 |
| Task | Issue、验收标准、当前 Diff | 验证范围 |
| Generated | Index 结果、摘要、Tool 输出 | 未验证前视为不可信 |
| Sensitive | Credential、客户记录、生产 Dump | 排除或获批脱敏 |
优先选择最小相关文件,包含 Interface 和附近 Tests;排除无关 Repository、生成噪声、Secret 和过时副本。记录文件路径与 Revision,重要建议才能复现。
Issue 文本、注释、检索文档和测试输出可能包含 Prompt Injection。它们是数据,不是更高优先级的 Policy。
小步、可观察地迭代
使用留下证据的循环:
- 检查当前代码和 Tests;
- 要求只在声明范围内提出计划;
- 请求一个相干变更;
- 检查 Diff 和依赖变化;
- 运行相关 Tests 与静态检查;
- 对失败分类,不要盲目重试;
- 理解上一步后再扩大范围。
小步降低 Review 面积并便于回滚,但不会自动把不安全任务变安全。
不易过时的 Prompt 模式
优先使用可观察指令:
复用现有 Error 类型和依赖注入模式。
不要虚构 SDK 方法;引用已安装版本,或明确标为伪代码。
每个行为变化都添加或更新相关 Test。
报告运行过的命令、失败、跳过的检查和假设。
如果需求与 Repository 契约冲突,停止并解释冲突。
避免:
写出完美的生产代码。
自主做出所有决定。
永远不要提问。
一次响应返回完整应用。
后者奖励自信并隐藏不确定性。好的助手可以说“无法验证这个依赖”或“这需要审批”。
Review 草稿,而不是 Review 氛围
检查每个生成 Diff:
- 行为和错误路径;
- 授权、Tenant 隔离和输入校验;
- Secret、日志、依赖和 License 变化;
- 并发、重试、幂等、取消和资源限制;
- Test 覆盖与负面案例;
- Observability、回滚和 Migration 影响。
不要要求模型证明隐藏推理。要求简短的变更摘要、假设和证据链接即可。流畅解释不是测试结果。
示例:安全的数据转换边界
AI 生成的数据转换,在运行和数据假设被显式定义之前不能称为“完成”。下面这段 Go 边界只接受有类型的 Record,限制批次大小,只规范化已声明字段,拒绝格式非法的地址,也不会把输入中的未知字段复制到输出:
package records
import (
"fmt"
"net/mail"
"strings"
"unicode/utf8"
)
const maxRecords = 10_000
type InputRecord struct {
Email string
DisplayName string
}
type Record struct {
Email string
DisplayName string
}
func NormalizeRecords(input []InputRecord) ([]Record, error) {
if len(input) > maxRecords {
return nil, fmt.Errorf("too_many_records")
}
output := make([]Record, 0, len(input))
for index, item := range input {
email := strings.ToLower(strings.TrimSpace(item.Email))
if email == "" {
continue
}
address, err := mail.ParseAddress(email)
if err != nil || address.Address != email {
return nil, fmt.Errorf("record_%d_invalid_email", index)
}
displayName := strings.TrimSpace(item.DisplayName)
if utf8.RuneCountInString(displayName) > 200 {
return nil, fmt.Errorf("record_%d_display_name_too_long", index)
}
output = append(output, Record{
Email: email,
DisplayName: displayName,
})
}
return output, nil
}
这段函数仍然没有定义 JSON 或 CSV Parsing、File Path、字符编码、输出原子性、数据留存、加密,以及谁有权处理这些记录。这些边界属于调用方契约与测试。Prompt 绝不能授予任意路径访问,也不能允许把记录上传到不可信目的地。
Tests 是需求的一部分
要求覆盖行为,而不是只复刻实现:
- 有效和无效输入;
- 空输入和超大输入;
- 重复与重放请求;
- 超时和部分失败;
- 未授权与跨 Tenant 标识符;
- 转义、注入和畸形外部数据;
- 取消与清理;
- 向后兼容和回滚。
随后运行项目真实的 Test、Type、Format、Lint、安全和 Build 命令。没有运行的命令应明确说明。
Tool 与权限
AI Agent 可以执行命令时,Executor 应强制:
- 已认证 Principal 与 Repository Scope;
- 默认只读;
- 命令和网络 Allowlist;
- 隔离 Worktree 或 Sandbox;
- 时间、输出、文件和进程预算;
- 取消和清理;
- Merge、Release、删除、凭证变化和外部写入需要审批;
- 经过脱敏的审计 Event。
Prompt 约束和模型拒答只是提示,不是授权。Coding Assistant、IDE Extension 和 Terminal Agent 都适用这一点。
使用显式执行策略:
| 能力 | 安全默认值 | 升级时必须提供的证据 |
|---|---|---|
| 读取受版本控制的源码与测试 | 允许,但仅限任务仓库 | 审计记录中的路径与版本 |
| 运行 Formatter 和聚焦测试 | 允许,但来自命令白名单 | 命令、退出码与有界输出 |
| 安装或更新依赖 | 询问 | 原因、Lockfile Diff、License、漏洞扫描 |
| 读取 Secret 或生产数据 | 拒绝 | 独立审批流程与脱敏方案 |
| 访问任意网络地址 | 拒绝或 Host 白名单 | 目的地、用途、数据分类 |
| 写入外部系统 | 询问 | 幂等键、Dry Run、负责人、回滚 |
| 合并、部署、删除、迁移 | 人工审批 | 已评审证据包与恢复方案 |
策略必须由宿主工具或 Sandbox 强制执行。在 Prompt 里反复写“禁止部署”,不等于真正移除部署权限。
建立分层验证阶梯
先运行成本最低、但能做出决定的检查:
- 格式化并编译被修改的 Package;
- 运行聚焦 Unit Test 与 Contract Test;
- 运行 Type、Lint、Static、Dependency 与 Secret 检查;
- 在隔离环境执行 Integration Test;
- 审查完整 Diff 与生成产物;
- 执行更广的 Build 或 E2E 门禁;
- 对外部副作用要求显式审批。
每一层都必须产生 Artifact:命令结果、测试报告、评审决定或部署记录。模型信心和文字摘要不属于验证阶梯。
团队协作
把共享指令当作版本化工程产物:
- 项目契约短小且与 Provider 无关;
- 指定负责人和复查日期;
- 链接实际脚本与 Canonical 文档;
- 用合成示例替代 Secret 和生产 Dump;
- 用代表性和对抗任务测试指令变化;
- 测量成功结果、Review 工作量、缺陷、成本和开发者学习;
- 高风险切片回归时回滚配置。
不要对所有 Repository 强制同一工作流。只读文档项目不应获得部署 Agent 所需的权限。
衡量成功验收结果
不要把 Prompt 数、Token 消耗或生成代码行数当作生产力指标。应测量:
- 每名开发者每周成功验收的任务数;
- 从接受任务到评审合并的 Lead Time;
- 一次验收通过率;
- 人工评审与返工分钟数;
- 逃逸缺陷、事故与回滚;
- 每个成功变更的成本;
- 通过 Review 或后续维护任务抽样验证的开发者理解度。
结果应按 Greenfield 与成熟代码、任务类型、风险等级和工具版本拆分。一个汇总平均值可能同时掩盖“测试起草明显提效”和“授权逻辑严重退化”。
不做排行榜的 Provider 选择
产品能力、路径、Model、价格和留存条款会变化。针对真实工作负载比较当前版本:
| 标准 | 证据 |
|---|---|
| Context | 选择、Provenance、排除和过时行为 |
| 执行 | Sandbox、Command Policy、Network、取消 |
| 隐私 | 留存、训练使用、驻留、删除 |
| Review | Diff、Tests、审批、审计导出 |
| 可迁移性 | 可导出指令与 Provider 独立性 |
| 运营 | 配额、故障、延迟、版本固定 |
| 经济 | 订阅、推理、Review、迁移 |
根据证据选择,不维护永久的“最佳 IDE”结论。
常见失败模式
- 因为代码看起来惯用就信任它;
- 提供整个生产 Repository,而不是少量必要文件;
- 把 Issue 或检索文档当作可信指令;
- 授予宽泛 Shell、Network 或凭证权限;
- 让模型选择身份、所有者、价格或角色;
- 把局部片段称为完整可运行;
- 在设计已被默认正确后才生成 Tests;
- 因为模型说 Tests 通过就直接合并;
- 没有用途和删除路径却长期保留 Prompt 与源码;
- 测量速度却隐藏重工、缺陷和 Review 成本。
实践清单
- [ ] 写出有范围、约束和升级路径的任务契约。
- [ ] 选择最小、版本化、可信的上下文并排除 Secret。
- [ ] 将检索内容和 Tool 输出视为不可信数据。
- [ ] 请求小 Diff,检查依赖和权限变化。
- [ ] 要求确定性 Tests 和安全检查。
- [ ] 对命令使用 Sandbox,副作用需要审批。
- [ ] 记录假设、跳过的检查、失败和回滚步骤。
- [ ] Review 行为、授权、数据处理、并发和运营影响。
- [ ] 版本化并 Replay 共享指令。
- [ ] 测量成功结果、质量、成本和学习,而非 Prompt 数量。
总结
Vibe Coding 最有价值的形式是一种有纪律的交互循环:表达意图、约束变更、检查证据,并让工程师和 Repository 保持所有权。自然语言可以加速草稿,但不能替代 Tests、Policy、安全 Review 或对系统结果负责。