Vibe Coding 一年,我发现 AI 写代码其实是最简单的部分
用了一年左右的 Vibe Coding,从一开始的"卧槽,这也太强了",到后来逐渐冷静下来,我对这东西的看法发生了一个挺大的变化。
刚开始用 AI 写代码的时候,我最大的感受就是:
爽。
以前可能要花半天查资料、找代码、写一堆重复逻辑,现在一句话扔过去:
"帮我把这个功能实现一下。"
几秒钟代码就出来了。
有时候甚至会产生一种错觉:
以后是不是不用写代码了?
但用了几个月之后,我发现事情远没有这么简单。
现在回头看,我觉得:
AI 写代码,反而是 Vibe Coding 里最简单的部分。
真正难的,是怎么让 AI 写出你真正想要的代码。
一开始,我也喜欢"一句话生成整个功能"
最开始的时候,我基本就是这么用的:
帮我实现一个对象池。
然后 AI 噼里啪啦写一堆。
看起来挺漂亮。
再比如:
帮我把这个 Manager 重构一下。
AI 直接开始改。
改完之后:
编译一下。
发现报错。
继续:
帮我修一下这个报错。
修完。
然后又发现另外一个问题。
继续修。
最后一个原本十几分钟的小需求,变成了:
AI 改了二十几个文件,我已经不知道它到底改了什么。
这时候我才意识到一个问题:
AI 最大的问题很多时候不是"不会写",而是"太会写了"。
你给它一个模糊的目标,它真的会开始干活。
问题是,它可能会按照自己的理解,把整个事情做一遍。
而这个"自己的理解",未必和你的项目设计一致。
AI 最怕的不是难题,而是缺上下文
后来我慢慢发现,很多时候 AI 写错代码,并不是因为这个问题太难。
而是因为:
它不知道你的项目到底是什么样。
比如你问:
帮我实现一个对象池。
对于 AI 来说,这个问题一点都不难。
但对于你的项目来说,可能还有一堆隐藏条件:
- 项目里其实已经有对象池;
- 这个对象池必须支持异步创建;
- 对象从池里拿出来之后需要 Reset;
- 回收的时候不能 Destroy;
- 项目要求不能产生额外 GC;
- 某些对象还和 Addressables 有关系;
- 还有一堆历史代码依赖现有接口。
这些东西你不告诉 AI,它当然不知道。
所以现在如果我要让 AI 修改一个比较复杂的功能,我一般不会直接说:
帮我实现 XXX。
而是先让它:
markdown
先不要改代码。
帮我看看这个项目里 XXX 相关的代码都在哪里。
分析一下:
1. 现在是怎么实现的
2. 哪些类负责什么
3. 主要调用关系是什么
4. 如果我要增加这个功能,应该改哪里
先给方案,不要写代码。
这一步其实非常重要。
因为很多时候你会发现:
AI 搜完代码以后,给出的方案和你最开始想的完全不一样。
而这反而是好事。
所以现在我越来越看重 Context
以前大家特别喜欢讨论:
Prompt 怎么写?
现在我反而觉得,在实际项目里:
Context 往往比 Prompt 更重要。
你跟 AI 说:
请用专业、优雅、高性能的方式实现这个功能。
其实意义没那么大。
倒不如直接告诉它:
yaml
Unity 2022.3
这个模块运行在 iOS 和 Android。
不能引入第三方库。
现有 XXXManager 的接口不能修改。
这个模块每帧可能调用几千次,所以不要产生 GC。
相关代码:
XXXManager.cs
XXXController.cs
XXXConfig.cs
然后让它自己去搜索代码。
AI 有了这些信息以后,做出来的东西通常会靠谱很多。
所以现在我的感觉是:
Prompt Engineering 解决的是"怎么跟 AI 说话",Context Engineering 解决的是"让 AI 知道自己到底在干什么"。
后者在真实项目里重要得多。
第二个变化:我开始不喜欢让 AI 一次改太多
这个也是踩了很多坑以后总结出来的。
以前:
帮我重构这个系统。
现在:
先分析。
然后:
css
只修改 A。
再:
css
验证 A。
然后:
css
再修改 B。
为什么?
因为 AI 改得太快了。
人自己写代码的时候,一次改十几个文件已经会比较谨慎。
AI 不一样。
它可以几秒钟改几十个文件。
如果最后出问题,你会发现:
定位问题反而比自己写代码更麻烦。
所以我现在特别喜欢一种模式:
小步走。
比如一个比较大的重构,我可能会拆成:
分析
↓
拆出一个类
↓
编译
↓
测试
↓
再拆一个类
↓
编译
↓
测试
虽然看起来慢了一点。
但实际上整体速度通常更快。
因为你不会走到最后才发现:
"好像从半小时前开始就走歪了。"
我现在基本固定使用一个流程:Plan → Execute → Verify
这套东西说起来很普通,但真的挺好用。
第一步:Plan
先问:
先不要修改代码。
分析一下这个问题:
问题在哪里?
涉及哪些代码?
你准备怎么改?
可能有什么副作用?
怎么验证?
让 AI 先想。
第二步:Execute
如果方案没问题:
按照刚才的方案执行。
只修改必要的文件。
不要顺便重构其他地方。
完成之后停止。
这里我特别喜欢加一句:
完成之后停止。
因为 AI 有时候真的很热心。
你让它修一个 Bug,它修完之后发现:
"这里其实还可以优化一下。"
然后:
"这个设计也可以重构一下。"
然后:
"顺便把这个旧接口也清理掉。"
最后:
一个 Bug → 半个项目重构。
所以一定要控制 Scope。
第三步:Verify
代码写完之后,不要直接说:
OK。
让它自己检查:
diff
现在 Review 一下刚才的修改。
检查:
- 有没有编译问题
- 有没有 NullReference
- 有没有生命周期问题
- 有没有边界情况
- 有没有破坏原有行为
- 有没有不必要的修改
然后自己再编译、跑测试、跑游戏。
这时候就形成了:
sql
Plan
↓
Execute
↓
Verify
↓
Feedback
↓
Execute
↓
Verify
我觉得这才是 Vibe Coding 真正舒服的地方。
不要让 AI 猜,尤其是已有项目
这个习惯我现在特别看重。
比如我问:
"这个项目是怎么加载 AssetBundle 的?"
我不会希望 AI 根据 Unity 的知识直接回答。
我更希望它:
先搜索项目中所有 AssetBundle 加载相关代码。
然后再告诉我:
这里有 XXXManager。
它通过 XXX 调用 LoadFromFileAsync。
加载完成之后放进 XXXCache。
卸载的时候由 XXXManager 负责。
这就靠谱多了。
因为:
Unity 官方的最佳实践 ≠ 你的项目实际是怎么写的。
甚至很多老项目里,真正的规则都藏在代码里面。
所以我现在越来越习惯:
Search First,Answer Second。
先搜代码,再回答。
Debug 的时候,别只把最后一行报错扔给 AI
这个也非常明显。
比如:
NullReferenceException
然后问:
怎么解决?
AI 当然可以给你很多答案。
但这些答案其实都没什么价值。
如果你告诉它:
markdown
Unity 2022.3
iOS
IL2CPP
操作步骤:
1. 启动游戏
2. 下载资源
3. 加载 AssetBundle
4. 进入场景
5. Crash
最近修改:
XXX
完整日志:
XXX
这时候 AI 才真正开始做 Debug。
因为真正的 Bug 通常不是:
"这一行为什么是 null?"
而是:
"为什么这个对象在这个时间点会是 null?"
这背后可能涉及:
- 生命周期;
- 异步加载;
- 场景切换;
- 对象池;
- 事件;
- 协程;
- 多线程;
- 资源管理。
所以我现在 Debug 时会尽量把:
操作过程 + 环境 + 日志 + 最近修改
一起给 AI。
而不是只给最后一行 Exception。
还有一个很好用的技巧:让 AI 找反例
这个真的挺好用。
比如让 AI 实现一个购买逻辑。
不要只问:
帮我实现购买。
实现完之后再问:
现在想一下,这个功能有哪些可能失败的情况?
至少列出 5 个。
然后检查当前代码有没有正确处理。
AI 很容易想到:
网络超时
重复点击
重复请求
服务端成功但客户端没收到响应
商品下架
余额不足
奖励发放失败
很多代码的问题就在这里。
Happy Path 写得很好,Failure Path 一塌糊涂。
而真正的线上 Bug,往往都藏在 Failure Path。
AI 写出来的代码,一定要 Review
现在我已经养成一个习惯:
AI 写完代码以后,再让它自己 Review 一遍。
比如:
markdown
不要修改代码。
现在假设你是一个非常严格的 Code Reviewer。
检查刚才的实现:
1. correctness
2. edge cases
3. lifecycle
4. performance
5. memory
6. concurrency
7. API compatibility
8. maintainability
只告诉我发现的问题。
然后再根据 Review 结果决定要不要修改。
相当于:
markdown
AI Developer
↓
AI Reviewer
↓
Human
不过这里一定要注意:
AI Review 只能当 Review,不能当最终验证。
真正的验证还是:
diff
编译
+
测试
+
运行
+
日志
+
Profiler
+
真实设备
AI 说"没问题",不代表真的没问题。
我觉得 Vibe Coding 最大的风险,其实是"代码熵"
这是我用了比较长时间之后最担心的一件事。
AI 最大的优势就是:
写代码太快。
但这件事情反过来也可能变成问题。
比如今天:
帮我加一个 Manager。
明天:
再加一个 Manager。
后天:
这个 Manager 不太好用,帮我加个 Helper。
一个月以后:
XXXManager
XXXManagerNew
XXXManagerV2
XXXManagerHelper
XXXManagerUtils
XXXManagerService
XXXManagerController
每一个类单独看,好像都没什么问题。
但是整个项目开始越来越乱。
这其实就是 Vibe Coding 很容易带来的问题:
AI 非常擅长解决局部问题,但如果没人管架构,整个系统很容易慢慢失控。
所以我现在有一个比较明确的边界:
实现细节可以交给 AI,架构尽量自己掌握。
比如:
模块怎么拆
接口怎么定义
依赖关系怎么组织
生命周期怎么管理
状态机怎么设计
资源怎么管理
这些事情我不会轻易完全交给 AI。
不要追求"一次生成正确"
这是我后来最大的一个认知变化。
以前我会希望:
"Prompt 写得足够好,AI 一次就把代码写对。"
现在我觉得这个方向本身就有点错。
AI Coding 更像是:
我提出目标
↓
AI 实现
↓
编译
↓
运行
↓
出现问题
↓
把真实反馈给 AI
↓
AI 修改
↓
再运行
这其实就是一个反馈闭环。
所以真正高效的 Vibe Coding,不是:
让 AI 一次写对。
而是:
让 AI 能够快速地根据真实反馈迭代到正确。
这两种思路其实差别挺大的。
最后,我觉得程序员真正需要改变的地方
用了一年 Vibe Coding 之后,我反而越来越觉得:
程序员并不会因为 AI 会写代码而变得不重要。
只是重要的能力正在发生变化。
以前我们特别在意:
API 怎么调用
这个函数怎么写
这个算法怎么实现
这个语法怎么写
现在这些东西越来越容易问 AI。
但另外一些东西反而越来越重要:
这个问题到底是什么?
应该怎么拆?
系统应该怎么设计?
这个方案有什么风险?
怎么验证它是正确的?
这个 Bug 到底是什么原因?
这段代码以后会不会失控?
也就是说:
AI 正在降低"写代码"的门槛,但提高"判断代码"的价值。
所以我现在对 Vibe Coding 的理解已经和一年前完全不一样了。
它不是:
"我懒得写代码,让 AI 帮我写。"
也不是:
"以后程序员不用写代码了。"
更像是:
把 AI 当成一个执行能力很强、速度很快,但需要你不断给它上下文、目标和反馈的工程师。
你负责:
diff
目标
+
上下文
+
架构
+
判断
+
验证
AI 负责:
diff
搜索
+
实现
+
重构
+
Debug
+
测试
最后形成一个循环:
markdown
人
目标 / 判断 / 架构
↓
AI
搜索 / 实现 / Debug
↓
真实运行
↓
Feedback
↓
AI
↓
人
这可能才是我目前觉得比较舒服的 Vibe Coding 方式。
不是让 AI 替你写代码。
而是让你从"写每一行代码的人",变成那个真正决定代码应该往哪里走的人。
而且说实话,用 AI 写代码一年以后,我最大的感受反而是:
以前觉得写代码慢,是因为代码难写。
现在才发现,很多时候真正慢的,是没想清楚自己到底要写什么。