
不是因为代码不好。
它能编译。
能运行。
甚至看起来还挺干净。
但它解决的是 错误的问题。
我明明要的是 Supabase 的 auth flow + Row-Level Security。
结果我拿到的是一套精美、生产级、架构优雅的认证系统——
用的是 Firebase。
那一刻的感觉,就像点了一份披萨,送来的是摆盘完美的意大利烩饭。
技术上无可挑剔。
战略上完全跑偏。
那一瞬间我意识到一件很不舒服的事:
我不是在和 Claude Code 协作。
我是在用它赌博。
如果你正在用自然语言给 Claude Code 下指令,然后“希望它理解”,
那你也在掷骰子。
区别只是——
你的地下城主是一个 零上下文、无限自信的 AI。
Vibe Coding 的陷阱
现实中的 vibe coding 是这样的:
给我做一个 SaaS 仪表盘
Claude Code 输出 14 个文件,3,000 行代码。
你盯着输出看,像在看 Magic Eye 立体画。
感觉有些地方好像对。
运行。
炸了。
你说:“修一下。”
它修了一处,顺手又弄坏三处。
这不是调试。
这是 $100/月订阅的打地鼠游戏。
几周前,我看到一位前 OpenAI 工程师提出的概念:Prompt Contracts。
45 分钟后,你确实上线了一个能跑的功能。
但你完全不理解它。
恭喜你。
你正在维护一个由外星智慧写的代码库,而它自己都不记得写过什么。
听起来很稳。
我在这个循环里活了几个月。
两个 SaaS 项目几乎全靠 Claude Code 写。
不夸张地说,我 30% 的时间在“撤销 Claude 自信满满写错方向的代码”。
它像一个天才合伙人。
只是失忆。
而且完全不理解你的仓库。
问题从来不是 Claude 的智力。
Opus 4.6 的能力夸张到离谱。
它像一个读完所有 Stack Overflow 答案的资深工程师——
包括那些错误答案。
问题是:
我的提示词是 vibe,不是 specification。
“做成响应式。”
“加上错误处理。”
“用最佳实践。”
这不是需求说明。
这是星座运势。
Claude 不是算命师。
它是承包商。
你不会把一张写着“建个房子,弄好看点”的餐巾纸交给施工队。
它确实会给你一个房子。
甚至有墙。
但厕所可能在厨房里。
因为你没说不行。
Prompt Contract:解法
Prompt Contract 的核心思想很简单:
把提示词写成“法律合同”,而不是创意简报。
它包含四个可执行条款:
- Goal —— 明确成功标准
- Constraints —— 不能越界的硬边界
- Output Format —— 交付结构
- Failure Conditions —— 不可接受情况
我当时想:
“这适合写博客,不一定适合凌晨三点让 Claude 写后端。”
于是我测试了三周。
两个项目。
所有提示都改成 Prompt Contract。
剧透一下:
我现在能睡觉了。
Component 1:GOAL —— 别再说“给我做 X”
最大升级,是强迫自己定义“完成长什么样”。
之前(vibe):
给 app 加一个订阅系统
这就像对导航说:
“带我去个不错的地方。”
你会到某个地方。
可能是米其林餐厅。
也可能是俄亥俄州的加油站。
之后(contract):
GOAL: 实现 Stripe 订阅系统,支持 free/pro/team 三档,
支持即时升级降级,
用户可在 /settings/billing 查看账单状态。
成功标准:free 用户升级到 Pro,
5 秒内看到 Stripe 扣款记录并解锁功能。
关键不在细节多。
而在 可验证性。
Claude 写完后,我 1 分钟内就能验证。
没有“差不多”。
没有薛定谔式 Demo。
这一条直接把我和 Claude 的来回次数砍半。
当 AI 知道终点线长什么样,
它就不会在赛道上自拍。
Component 2:CONSTRAINTS —— 拯救你的墙
没有约束,Claude 会帮你重构整个栈。
像是装修厨房的工人顺便拆了客厅墙,
因为“风水更好”。
我现在每个项目根目录都有一个:
CLAUDE.md
这是永久约束层。
示例(节选):
# CLAUDE.md — Project Constraints
## Stack (non-negotiable)
- Frontend: Next.js 14 App Router + TypeScript strict
- Backend: Convex
- Auth: Clerk (never custom auth)
- Styling: Tailwind only
## Hard Rules
- Never install new dependency without asking
- Never modify DB schema without migration plan
- All API calls via Convex
- Env vars in .env.local only
## Patterns
- Server components by default
- Error boundaries on every route
- Zod validation on all input
在有 CLAUDE.md 之前:
Claude 会突然改用 Prisma。
或者把 Clerk 换成 NextAuth。
因为“更常见”。
我没让你投票。
我指定的是 Clerk。
现在?
它老实多了。
专业建议:
每次新 session 开头第一句话:
读取 CLAUDE.md 并确认理解项目约束。
这叫“握手协议”。
没有这步,你就是把车钥匙给一个不知道开什么车的人。
Vibe prompt 会让 Claude 自己决定结构。
像对厨师说“给我惊喜”,
然后震惊地发现凯撒沙拉被拆解装在鞋里。
之前:
写一个用户 onboarding API
Claude 可能给你一个 800 行巨型文件。
认证、数据库、发邮件全塞一起。
能跑。
但未来的你会想辞职。
之后:
FORMAT:
- Convex mutation 在 convex/users.ts
- Zod schema 在 convex/schemas/onboarding.ts
- 类型定义在 convex/types/user.ts
- 带 JSDoc
- 返回结构 { success, userId, error? }
现在输出是模块化、类型完整、可维护的。
Claude 默认优化的是“生成速度”,
不是“可维护性”。
你要它打长期赛,不是速通。
Component 4:FAILURE CONDITIONS —— 真正的秘密武器
这一条改变了一切。
Goal 是胡萝卜。
Failure Conditions 是大棒。
Claude 对大棒的反应,
像工程师对“生产挂了”的 Slack 消息。
例如我上周的真实 Prompt:
Build /dashboard
GOAL: 实时展示用户项目,支持创建/归档/重命名,
FMP < 1 秒
CONSTRAINTS:
• Convex useQuery
• 不用 polling/SWR
• Clerk useUser
• 未登录跳转
• 每文件 <=150 行
FORMAT:
• app/dashboard/page.tsx
• components/dashboard/ProjectList.tsx
• convex/projects.ts
FAILURE CONDITIONS:
• 用 useState 存本应在 Convex 的数据
• 超过 150 行
• 客户端 fetch 可服务端获取的数据
• 使用 Tailwind 以外 UI 库
• 缺少 loading/error 状态
• 缺少 TS 类型
第一次生成——
干净、实时、符合约束。
没有 Firebase。
没有 Prisma。
没有 2019 年 12 星 GitHub 包。
Failure Conditions 是护栏。
Claude 不用猜什么是“好”。
因为你已经定义了什么是“坏”。
“安全驾驶”是祈祷。
“限速 80,不闯红灯,不高峰上高速”是导航系统。
三周结果
不是科研数据。
只是一个人在黑屋里的统计。
回滚率:
从 1/3 降到约 1/10。
平均交付轮次:
3 次 → 1.2 次。
CLAUDE.md 违规:
几乎归零。
最大的惊喜:
提示词变短了。
因为永久约束在 CLAUDE.md 里,
合同结构已内化。
复杂功能 Prompt 写 60 秒。
省 45 分钟 debug。
今天就能开始
不用大改流程。
两步:
- 在项目根建 CLAUDE.md
- 下次写 prompt 时,增加:
- 一个 GOAL
- 一个 CONSTRAINT
- 一个 FAILURE CONDITION
30 秒差距。
从:
“希望它对”
到:
“我知道它对”
这种确定感,在软件工程里应该算受控物质。
Prompt Contract 不是让你写更多。
而是让你思考 60 秒,
避免 Claude 猜 60 分钟。
我从赌博变成稳定发货。
这种将模糊的“vibe”转化为可执行“合约”的思维方式,不仅仅适用于 Claude Code,它本质上是一种更清晰的技术沟通与协作框架。如果你也在探索如何更高效地与 AI 协同工作,或者对 Next.js、TypeScript 等技术栈在现代全栈开发中的应用感兴趣,欢迎来我们 云栈社区 的 后端 & 架构 或 前端框架/工程化 板块一起交流讨论。
轮到你了。