我决定放弃 Vibe Coding 回到手写代码 — AI 编程 7 个月亲历的反面教材 2026
Published: 2026-05-11
2026 年 5 月 9 日,一位名叫 shvbsle 的开发者在他博客上发布了一篇文章《I'm going back to writing code by hand》,迅速登上 Hacker News 首页。这不是一篇反对 AI 编程的檄文,而是一个诚实且痛苦的自述:他用 Claude 进行 vibe coding 7 个月,写了 234 个 commit,构建了 k8s GPU 仪表盘 k10s,最终发现代码已经烂到无可救药,决定归档整个项目从头手写。
这个故事不是个例。随着 Claude Code、Cursor、GitHub Copilot 等 AI 编码工具的普及,越来越多开发者正在经历相似的困境:第一周觉得效率提升 10 倍,第三个月发现系统已经不可维护。这篇文章将深入剖析 k10s 项目从辉煌到崩塌的全过程,以及从中学到的 5 个关键教训。
故事的起点:30 个周末的 AI 编码狂欢
shvbsle 在 2025 年 9 月底开始了 k10s 项目 — 一个面向 NVIDIA GPU 集群的 Kubernetes 仪表盘。它的定位是 k9s 的替代品,但专门为运行 GPU 工作负载的团队设计:显示 GPU 利用率、DCGM 指标、节点温度、功耗、以及哪些节点在空转烧钱($32/hr)。
前几周就像是魔法。他描述道:
"我会对 Claude 说 '加一个带实时更新的 pods 视图',然后 boom,它就能用了。资源列表、命名空间过滤、日志流式传输、详情面板、键盘导航。每个功能都是干净利落落地的,因为当时项目足够小,AI 能把整个项目上下文都装进来。"
k9s 的基本克隆版用了大约 3 个周末就完成了:Pod/Node/Deployment/Service 资源视图、命令面板、基于 Watch 的实时更新、Vim 风格快捷键。一切正常,一切都在单次 vibe coding session 中完成。速度大约是平时的 10 倍,感觉无可阻挡。
转折点:GPU 舰队视图的诅咒
然后他想要 k10s 的核心卖点 — GPU 舰队视图。一个专用屏幕,显示每个节点的 GPU 分配、DCGM 利用率、温度、功率、内存。不是埋藏在 kubectl describe node 输出里,而是一个带颜色编码状态的专业仪表盘。空闲节点黄色、忙碌绿色、满载红色。
Claude 一次就完成了。它生成了 FleetView 结构体、标签过滤(GPU/CPU/All)、带分配条的自定义渲染。看起来很美。
但当他输入 :rs pods 想切回 pods 视图时,什么都渲染不出来了。表格是空的。实时更新停了。切到 nodes 视图,显示的是舰队视图的过期过滤数据。
God Object 把自己吃掉了。
God Object:1690 行代码埋下的定时炸弹
shvbsle 第一次坐下来真正阅读 Claude 写的代码。他打开了 model.go — 全部 1690 行。以下是他看到的景象:
type Model struct {
// 第三方 UI 组件
table table.Model
paginator paginator.Model
commandInput textinput.Model
help help.Model
// 集群信息和状态
k8sClient *k8s.Client
currentGVR schema.GroupVersionResource
resourceWatcher watch.Interface
resources []k8s.OrderedResourceFields
listOptions metav1.ListOptions
clusterInfo *k8s.ClusterInfo
logLines []k8s.LogLine
describeContent string
currentNamespace string
navigationHistory *NavigationHistory
logView *LogViewState
describeView *DescribeViewState
viewMode ViewMode
viewWidth int
viewHeight int
err error
pluginRegistry *plugins.Registry
helpModal *HelpModal
describeViewport *DescribeViewport
logViewport *LogViewport
logStreamCancel func()
logLinesChan <-chan k8s.LogLine
horizontalOffset int
mouse *MouseHandler
fleetView *FleetView
creationTimes []time.Time
allResources []k8s.OrderedResourceFields
allCreationTimes []time.Time
}
36 个字段挤在一个结构体里。没有模块化,没有边界,没有接口隔离。只有一个「万物控制器」来统治他们。AI 的每个新请求都在这个结构体上追加字段,因为这是最快捷的方式,而 AI 从不主动重构。
这就是 vibe coding 的核心陷阱:AI 擅长加功能,从不擅长重构。每次新功能都是「在前面的基础上加」,而 AI 没有「这样下去系统会死」的感知。7 个月积累下来,1690 行代码变成了一个无人能理解的怪物。
五个核心教训
教训一:AI 写的是功能,不是架构
vibe coding 最大的幻觉是「AI 在帮你写代码」。实际上 AI 在帮你在当前状态上堆叠功能。架构设计 — 模块边界、职责分离、依赖方向 — 这些需要人来做。AI 不会突然说「等等,我们应该把 Model 拆分一下」。
shvbsle 的原话:
"AI 写功能,不写架构。你让它自由发挥越久,残骸就越严重。速度让你以为自己赢了,直到所有东西同时崩塌。"
教训二:不要跳过代码审查
7 个月里,shvbsle 只看 diff,验证编译通过,测试 happy path,然后继续。他从来没坐下来完整读过 AI 写的代码。这是他最后悔的事。哪怕每周花 30 分钟整体过一遍代码结构,God Object 可能在几百行时就暴露了,而不是等到 1690 行。
教训三:AI 生成的 Go 代码天然倾向 God Object
这不是 Go 的问题,是 AI 编码模式的普遍问题。当每次对话都在原有文件上追加时,AI 倾向于在已有结构体上加字段而非新建,倾向于扩展已有函数而非拆分。没有哪种编程语言能免疫这种趋势,但如果使用支持模块化的语言(Rust、Haskell 等),类型系统至少能帮你更早感受到痛苦。
教训四:技术债在 vibe coding 中呈指数级增长
传统开发中技术债是线性增长的 — 偷懒一次欠一笔,下次还。在 vibe coding 中,AI 每次都在错误架构上继续加码,债上加债。速度掩盖了一切,直到负债率超过临界点,系统瞬间崩塌。
教训五:vibe coding 最好的用途是原型,不是生产
k10s 的前 3 个周末(基本 k9s 克隆)是 vibe coding 的甜蜜区:项目小、需求明确、AI 能装下全部上下文。但当需求复杂到需要自己设计架构时,vibe coding 就不再是加速器,而是债务加速器。
如何避免走入同样的死胡同
参考 shvbsle 的经历和社区讨论,以下是可以立即应用的策略:
1. 架构先行,AI 补充
在让 AI 写任何代码之前,先手写或详细描述好接口边界、模块划分、数据流。让 AI 在约束内填充,而不是从零创造。可以这样做:手动建立好接口签名和类型定义,然后让 AI 实现具体函数。
2. 对 AI 代码进行定期架构审查
设定一个节奏 — 每 50 行或每次新功能后,完整通读 AI 生成的代码。问自己三个问题:这个新代码有没有打破模块边界?有没有创建不必要的耦合?如果下个需求来,它能干净地扩展吗?
3. 拥抱「手写接口,AI 写实现」模式
最有效的 AI 编码模式可能是:你写接口和类型定义,AI 填充具体逻辑。接口本身就是一种「强制约束」— AI 不能随意跨边界加字段。
4. 不要怕「砍掉重练」
shvbsle 做了一个艰难但正确的决定:归档整个项目,从头手写。他吸取了教训,保留了领域知识,抛弃了无法维护的代码库。有时候从头开始比在废墟上修补更快。Git 历史不会消失,但你留住了学到的经验。
5. 对 AI 设定明确的「不允许做的事」
在你的开发规范中明确列出:不允许在同一个结构体上追加超过 N 个不相关字段、不允许在已有函数中增加超过 M 个新分支、每次新增必须新建文件/结构体。AI 会遵循明确约束,但它不会自动设置约束。
作者的最后决定
shvbsle 在 2026 年 5 月 9 日将 k10s 标记为存档,正在从零开始手写重写。他对于这段经历的评价很坦诚:
"这 7 个月学到的教训比扔掉的那 1690 行 model.go 宝贵多了。任何认真做 vibe coding 的人都能从中受益,因为这一点很少被讨论 — 它被淹没在 demo 视频和速度胜利的叙事里。"
这不是「AI 编码无用论」。AI 编码工具仍然是 2026 年最强大的开发者工具之一。但就像任何强大工具一样,如果不理解它的局限,它反噬起来也特别疼。vibe coding 不是银弹,它是需要配约束、配架构审查、配设计纪律的半自动步枪。