现如今,如果一个人还在手工写代码,似乎已经变得"十恶不赦"。真正符合时代潮流的合格的开发者必须得是AI全栈:只给AI提一个模糊的需求,剩下的工作全面放心大胆的交给AI,不要读AI所产生的代码,最好还要同时使用多个Agent来加速任务的完成,或者在同一时间同时完成多个需求。
每天一起床就总是不断看到这样的故事,这让还在手写代码的我,感觉已经完全被时代抛弃了。所以,我觉着自己必须得弄明白一件事,那就是,我不否认AI现在很强大,但是真的所有的开发工作都适合使用Vibe Coding吗?如果不是,那Vibe Coding适应的范围是什么?必须得弄明白这个问题,我才能继续安心做好自己的项目。
我个人思考得到的答案是 ------ Vibe Coding的适应范围,与AI的强大程度无关,而是与语言有关。你的需求越容易使用自然语言描述清楚,就越适合Vibe Coding;如果相对于自然语言,你的需求更适合用编程语言来描述,那么手工编码就更适合。
这样说可能太抽象了,来举个栗子 ------ 比如我们来开发一款KV存储引擎。
功能需求
乍看上去,KV引擎的需求非常简单,我们用接口来描述它的功能:
csharp
public interface KVStorageEngine {
void open(DBOptions options);
byte[] get(byte[] key);
void set(byte[] key, byte[] value);
byte[] delete(byte[] key);
Iterator newIterator();
void close();
}
从接口来看,就是增删改查还有迭代。你把这个需求提给AI,有的AI会直接实现,有的AI可能会多问你两句:比如,对持久化的要求、对事务和并发的要求等等。然后它们很快就会列出计划,然后开始实现。我的Claude Code制定的开发计划是这样的:
- 创建项目基础结构和核心接口
- 实现MemTable(内存表)
- 实现SSTable(磁盘表)和布隆过滤器
- 实现WAL(预写日志)
- 实现Compaction(合并压缩)
- 实现并发控制和锁机制
- 实现事务支持
- 实现快照功能
- 实现主存储引擎类
非功能需求
看起来不错,但这件事真的可以这样做吗?上述接口所描述的功能,仅仅只是露出水面的冰山,但是,除此之外,还有大量的非功能性需求需要思考,比如:
关于性能
这款引擎的主要定位的场景是什么?它是追求高性能随机写还是追求读写平衡?这会让我们采用完全不同的数据结构来实现,比如LSM-Tree还是B-Tree。如果像Claude Code的计划那样,我们打算采用LSM-Tree,那么LSM-Tree最大的问题就是读性能具有不确定性,另外还有在Compaction时会发生抖动。我们打算怎么应对这些问题?是引入Cache还是布隆过滤器?如果引入Cache,跟操作系统本身的缓存机制又该怎样对齐?是否要引入RateLimiter对IO进行限流?线程池又该怎样规划?
关于持久化
数据怎样落盘?WAL如何在事务完整性、备份快照、写缓冲以及磁盘同步之间取得折衷?是否允许用户可配置策略?如果可配置,那又采用一个怎样的默认配置来减少用户的心智负担,让他能够开箱即用?只有在特殊环境之下才需要手动配置?还有不同的存储介质之间,HDD和SSD之间的持久化差异?读写放大对它们的影响?是否支持分布式文件系统?是否支持TTL?
关于一致性
如何对事务进行隔离?如何在不同的操作之间,比如随机读写、迭代、Compaction、事务之间保持数据的一致?
关于抽象和扩展
MemTable是否可以支持不同的数据结构实现,比如使用SkipList来支持顺序存储,使用HashTable用于只有随机读的场景。持久化是否是可配置的?这样就可以将其应用在缓存场景?是否允许用户在Compaction时自定义操作?......
所以,我们可以看到,对于KV存储而言,这些非功能需求要远远多于功能需求,每个点细化下去,都可以写一篇长文。
非功能性需求与Vibe Coding
我们当然也可以把这些非功能性需求提给AI,我相信以AI目前的能力,肯定也会理解这些需求,甚至比我知道的还多。但是,这里面有以下几个问题:
- 如果你不把这些需求详细提给AI,AI并不会主动要去做,尽管它拥有这些知识。AI只会选择自己认为重要的,或者模仿市面上相似的产品来做,或者干脆选一个最简单的路径来实现。而一旦你需要把这些需求完整的提给AI,这就要求你必须对数据库领域有着极其深厚的理解,你对这个领域理解的越多,你能够从AI那里撬动的越多。
- 这些需求之间并不是孤立的,而是需要彼此平衡的,其中涉及到很多取舍的判断。而这些判断必须取决于你的产品定位是什么?你追求的架构哲学是什么?这个是AI无法帮你完成的,除非是AI主动有了自己的创造动机。而一旦AI有了这个能力,那么连所谓的Vibe Coding也不需要了,它甚至会起身自己去挣钱,去"赡养"人类。
所以,一旦你将这些非功能需求考虑清楚,做出决策和平衡,就远非"Vibe"这样简单了。但你也可能会想,只要我们用自然语言将这些需求给AI描述清楚,我们还是没有理由再去手工写代码,但真的是这样吗?事情还没有这么简单。
细节之中充斥着大量重要的决策
以上还只是功能和架构层次的决策,而对于KV引擎这样的需求,在实际开发中依然存在着大量的决策,这些决策难以用自然语言来描述,而这些决策是导致它们更适合手工编码的主要原因。
举个最简单的例子,比如加锁,每次加锁你都需要做以下的决策:
-
锁的粒度怎样才能合适?
- 粒度太大 --- 影响性能
- 粒度太小 --- 可能会造成线程安全的隐患
-
这块的竞争频率如何?该使用悲观锁还是乐观锁?
-
当发生锁竞争时,是否会影响到CPU的亲和性?导致性能陡然下降?
-
它和其它锁的关系?是否会发生死锁?
-
锁是必须的吗?是否可以通过局部化或者无锁算法来优化?
相对于自然语言,这个问题更适合用代码直接描述
加锁的代码可能只有一行,但是要说明白为什么这个锁要加在这里而不是加在别处,你可能需要写一篇很长的小作文来把这件事说清楚。
这个问题很重要吗?
对于数据库而言,非常重要。锁加错了位置会引发灾难性的后果,锁的粒度或者类型不适当,也会造成性能极大的衰减。这些细节的决策跟架构层面的决策一样重要,甚至在功能需求极简的情况下,这些对细节的极致追求极有可能是你的产品最大的竞争优势。
AI在写代码时不能判断这个问题吗?
AI可以判断这个问题,但你无法保证它每次都能判断精准,你也无从得知它到底是怎么理解这个问题的。最重要的,这样的决策充斥着整个代码,而这些决策之间并非是孤立的,它们之间也是彼此关联的。这种关联是属于隐性知识,你更是难以用自然语言来描述。
所以,对于这种技术细节很重要的项目,手工编码可能比AI编码更合适。而且对于这种项目,提高开发效率,意义也不大。它的功能就这么固定,但是细节却可以无限优化。
回归本质
最后,呼应开头的想法,那就是如果你要判断你手头的项目是不是适合Vibe Coding,那就去比较一下需求的描述。你的需求是采用自然语言描述起来更容易,还是采用编程语言更容易。如果是前者,那大胆的交给AI,如果是后者,那就放心的去手工编码吧,没什么丢人的。