别让AI再从零写一堆优美的屎山了

别让AI再从零写一堆优美的屎山了

TL;DR:AI 一次任务能写出 8000 行重复代码不是段子,是真实事故。本文从 hbcore(10 年、26 模块、10 万行 C++)实战经验出发,给出三条不依赖任何工具的工程纪律------古法打底 / 粒度即生死 / 写薄胶水,外加最后一节聊聊我做的小工具 GufaForge 怎么把这三条编进流程。


一、为什么 AI 写的越多,反而越乱

有时候 AI 能在一次任务里写出 8000 多行重复代码。听起来像段子,是我前几天亲身遇到的------让 Codex 重构一块视频输入源接入层,它给四类来源(本地设备采集、IPC 摄像头、ONVIF、GB28181)各自实现了一套解码器和缓冲队列。后面会详细讲这事故是怎么发生的。

问题是这种事远远不止一次。稍微用过 AI 编程一段时间的人,大概都有过同样的感觉:它不知道我们的代码库长什么样、不知道项目里已经有哪些成熟模块、不知道团队约定的命名规则。它每次都从训练样本出发,把"最常见的写法"套进来------但每个项目里最常见的写法,恰恰是它没见过的那一份。

更要命的一点是:AI 写得越多,这种偏移就放大得越厉害。生成几千行只要几分钟,但要理解、审完、合并这几千行,常常是好几天起步------而且代码库越大、协议越多、上下文越复杂,这个比例就恶化得越快。

8000 行只是单次事故,更普遍的体感是:加了 AI 之后整个项目反而越来越没人能改。每个文件单看都很专业,整体却像一座优美的屎山。真问题不是 AI 能力不够,而是缺约束的生成 = 优美屎山

那约束从哪来?是工具吗?后面会讲到 GufaForge,但坦白说工具只是把人本来就该做的事自动化------真正管得住 AI 的,是工程纪律本身。

下面是我在 hbcore(10 年、26 模块、10 万行 C++)和 GufaForge 实战里反复验证出来的三条。


二、古法打底:别让 AI 重新发明你已有的轮子

先讲一个尴尬事实:hbcore 这个我维护快 10 年的 C++ 流媒体技术栈,26 个模块、10 万行代码,覆盖从设备采集到服务端推流的全链路。听上去挺像样,但去年我用 repo-scan 做过一次全量交叉审计,结果让我自己都吓了一跳:

能力域 重复实现数量
H.264 NAL 解析 3 份独立实现(base / record_play / rtsp_server)
FAAC 音频编码 3 份独立实现
位流读写 4 份(base 3 套 + mp4_parser 1 套)
YUV 色彩转换 / x264 编码 / 缓冲区管理 / Base64 各 2 份

七个能力域全部有重复。光是 base 基础库,在整个代码库里被复制粘贴了 30+ 份

连我自己写代码都在重复造轮子------AI 来了只会更严重。它看不到其他模块里有什么,每次都从零写一份新的。如果不主动告诉 Claude Code"你应该用 base 模块的 h264_frame_parser",它会非常勤快地给我写第 31 份。

后来我做过一次实测对照:同一个"文件批量处理子系统"需求(支持并发、错误重试、进度回调),分别用两种方式让 AI 写。具体数字我没精确统计,大致是这个量级------

起点 业务代码量 幻觉 API 我审完合并的时间
让 AI 从零写 ~1800 行 几处虚构(印象最深的是 ConcurrentPool.batch_submit_async(),根本没这函数) 大半天
先把现成的并发池/重试装饰器/日志模块清单喂进去再写 ~400 行 没遇到 半小时

差四五倍的代码量、几乎没碰到幻觉、合并时间从大半天压到半小时------不是因为 AI 这次更聪明,而是因为它没机会去发挥。

这条原则真正落地比看上去费劲。AI 经常"礼貌地无视"你提供的现成模块------它读了,但生成时还是按训练里见过最多的 pattern 写。所以 prompt 里得强制约束:"使用 X 模块的 Y 接口,禁止重新实现"。光说"我有这个模块"不够。

另一件容易忽视的事:现成模块的接口契约(输入/输出/错误码/调用顺序)必须显式描述出来------AI 看不到 .h 文件背后的隐含约定,写过 5 年的常识对它来说是空白。


三、粒度即生死:规划归人,执行归 AI

回到开头那个 8000 行重复代码的事故。

任务我交代得挺清楚:"把本地设备采集、IPC 摄像头、ONVIF、GB28181 四类视频输入源的解码和缓冲管理收敛成一个共享层"------一句话,但里面其实有"判断哪些可以共享、哪些得保留差异"的隐含规划工作。Codex 接到任务后没问就开干,跑了一轮回来,我打开 diff 一看------它给四类输入源各自 写了一套解码器、各自 一个缓冲队列、各自一份帧时间戳处理逻辑,而且四份实现的接口风格还都不一样。

这是把规划和执行一起塞给它的典型后果。中段开始 lost in the middle,方向偏移、上下文污染------产出的代码单看还算专业,跟最初要"收敛成共享层"的描述已经走反了。

把同样的任务切成 5 到 15 分钟一轮的小粒度,每轮人审一次,AI 不再决策架构、只做执行------这一改,翻车率几乎归零。

这里有两个边界,划在哪儿决定翻不翻车。一个是规划与执行必须分离 ------人决定"做什么"和"怎么拆",AI 只决定"具体写什么代码"。规划这件事 AI 现在还做不好,也许过几代会变,但今天硬让它做就是要 lost in the middle。另一个是每轮 50 到 200 行 diff 的上限,超过就该拆。10 个文件混着改的时候,已经看不出哪一行有问题了。

还有几个反复踩到的小坑:

  • AI 会主动问"要不要顺便把 X 也改了"------99% 的情况应该说不。
  • "继续"这个词是糖衣陷阱------多数情况下应该停下来重新规划。
  • "上下文窗口够大就一次性丢全部"这个诱惑也得警惕------窗口够大不等于注意力分布均匀,长上下文里中段位置的信息几乎会被忽略。

四、胶水代码要尽量写薄:不要追求完美抽象

我现在删 AI 写的代码,比让它新写还多。

光是 hbcore 最近一个月的重构,git 统计下来累计删了 5.8 万多行------清单里排在前面的是一批"看起来很专业、其实没怎么用过"的中间层:几个用了一年也没复用过的接口塔、只有一个实现的 Adapter、连过总线的事件类型都没几个的 Observer。删完之后,业务代码读起来反而顺了。

一开始以为是 AI 能力不够,后来才发现是我让它去做了它不该做的事------一上来就抽象。

让 AI 写一个"小工具",它给你一套 IoC 容器、工厂模式、Observer 总线、Adapter 层的"企业级"架构。看到那一刻的惊艳能撑半天,第二天打开来改的时候就开始头疼:没人记得每层为什么存在,删起来比当初写还慢。

我现在的做法是先写最薄的胶水层------直接调用、最少封装。等同样的逻辑真正出现第 3 次再抽象。这就是工程经验里的 rule of three:3 次出现才抽象,2 次的硬编码就硬编码。

这条原则成本结构很扭曲:AI 写抽象的成本是 0,删抽象的成本是无穷大。它一句话生成 200 行接口塔,要删的时候得先理解每一层为什么存在。所以一开始就要拒绝"看起来很专业"的设计模式------一个还没复用过的接口、一个只有一个实现的工厂,本质上都是为未来的不确定性付的预付款,而未来 90% 不会兑现。

养成每周 review 一次"哪些抽象其实没用"的习惯,删代码比新增代码更重要。AI 在 review 阶段经常舍不得自己之前写的抽象,需要强制下令"删掉"------它的偏差是保留,开发者的偏差应该是删除。


五、从原则到工具:GufaForge 是怎么把这三条编进流程的

三条原则我自己都同意,难的是每次都要从头复述。每次让 AI 干活前,都得重新解释一遍"我已有什么、什么不能重写、粒度该多大"。这件事我手动做了大半年,受够了,于是有了 GufaForge。

它做的事很直接:扫描代码库 → 自动产出"成熟模块清单 + 接口契约" → 自动生成 prompt 前缀,一次性附在每次让 AI 干活的最前面。再加一条强制约束:"必须使用清单里的模块 X,禁止重新实现"。

hbcore 50 多个子项目用下来,我没做精确统计,但有一点直观感受------不带古法清单时,AI 重新发明轮子是常态,本文开头那 8000 行重复代码就是典型;加上之后,大部分新需求都能落到"复用 + 薄胶水"上,重复造轮子从默认状态变成了稀有事件。

不一定要装 GufaForge------今天就把最熟的 3 到 5 个模块整理成一份"古法清单"(每个模块 50 字接口说明 + 一句"什么时候必须用"),下次让 AI 干活前先粘进 prompt 顶部,立刻见效。如果想要工具自动化的版本:[GufaForge GitHub 链接 --- 待回填],pip 安装一行。

也欢迎在评论区或公众号留言,把你自己的"古法清单"长什么样发过来。我会精选几份做下一篇拆解。


作者 :海滨code | 公众号:海滨code | GitHub:@haibindev | 个人主页

WX:hbstream

相关推荐
Zhang~Ling3 小时前
C++ 模板初阶:从函数模板到类模板
c++
蜕变的土豆3 小时前
Visual Studio编译时,报错windows sdk 不匹配,找不到windows sdk
c++
雪度娃娃3 小时前
转向现代C++——优先选用限定作用域的枚举型别,而非不限作用域的枚举型别
java·jvm·c++
咩咦3 小时前
C++学习笔记17:析构函数
c++·学习笔记·类和对象·构造函数·析构函数·动态内存
小碗细面3 小时前
Agents 编排工具 - OMC 和 Ruflo,到底该怎么选?
ai编程·claude
历程里程碑3 小时前
54 深入解析poll多路复用技术
java·linux·服务器·开发语言·前端·数据结构·c++
名不经传的养虾人4 小时前
从0到1:企业级AI项目迭代日记 Vol.28|企业AI的交付不是给工具,而是给搭好的能力
大数据·人工智能·ai编程·ai工作流·企业ai·多agent协作
无限进步_4 小时前
【C++】可变参数模板与emplace系列
java·c++·算法
计算机安禾4 小时前
【c++面向对象编程】第28篇:new/delete vs malloc/free:C++中正确动态内存管理
开发语言·c++·算法