半古法编程 vs Vibe coding:可持续开发的理性选择

2026-06-12阅读 0热度 0
编程

一场猛烈的Vibe Coding之后……

三万行核心代码,加上提示词和各种配置文件,总计二十六万行。一个基于LangGraph双图架构、支持语音交互与多轮对话、已上线并稳定运行的全栈应用——在逐步成型的过程中,也在不知不觉中为自己埋下了隐患。

听起来很夸张?整个过程几乎全靠Vibe Coding完成,传统手写代码介入极少。但现在回过头看,这恰似“温水煮青蛙”——等意识到问题严重时,早已深陷其中,难以抽身。

这不是危言耸听。来看这个项目的真实数据:

  • 开发周期:3月28日 - 4月14日
  • 总提交次数:63次
  • Python代码:131个文件,24,652行
  • TypeScript/TSX前端代码:40个文件,7,780行
  • 纯代码合计:约32,000行
  • 包含prompt模板、配置、文档:约262,000行

工作量确实不小,那么问题究竟出在哪里?

  • 超过500行的Python文件:8个(最大一个达到2,018行)
  • 超过500行的前端文件:4个(最大一个1,043行)
  • 节点函数文件coach_nodes.py中包含38个函数,覆盖整个图的节点
  • 服务函数文件coach_service.py中包含46个函数,prompt构建、辅助函数和LLM调用全部混杂在一起
  • 测试代码:约900行,不足源码的3%

乍看之下,这些数据似乎不算致命。

但实际情况是,这个项目目前已暂时停摆。原因很简单——改不动了。牵一发而动全身,而“全身”散落在各个文件,彼此纠缠不清。

到了这个阶段,本该进行精细的延迟优化和LLM回答稳定性提升,尤其是prompt微调。但AI编码到了这一步明显力不从心。若亲自下场改动,理解成本极高……说实话,项目已处于失控状态。

事情是如何一步步走向失控的

故事的开始总是光鲜。第一周,开发速度飞快,一切看似顺利。把一个简单的MVP丢给AI,十分钟内它就帮忙拆分好文件结构,引入了LangGraph和状态持久化。

接下来搭FastAPI脚手架、加前端、完善LangGraph图。

那会儿AI生成的代码还能被Review一下。反正等AI完成指令也需要时间,闲着也是闲着。框架清晰,文件不大,每个文件的职责一目了然。这给了人虚假的信心,让人开始相信:AI写的代码质量不错,只需描述需求就行。

中间那周,开始密集地添加功能,为部署做准备。知识库、积分系统、账号系统、监控系统,每天都往里面塞新模块。

但这个阶段,逐渐记不清每个文件的职责了。增加太快,连文件结构分几层、几块都开始模糊。尝试去Review,却越来越跟不上AI的思路:为何只是微调一个接口的功能,它却改动了五六个文件?

打开那些被修改的文件,能看懂单个函数,但拼不起来。要反应好一会儿才能明白为什么需要改这里。渐渐地,放弃了读代码。每次提需求或报错,就等AI改完,能跑就算过关。

真正让人心态有点崩的,是部署环节。

到了这一步,完全依赖AI改代码。部署又极其容易出错。镜像构建和传输到服务器的时间不可预测,有时一个半小时,有时两分钟就搞定。

开发者就这么干等啊等,常常等一个小时,部署失败,把报错复制给AI,让AI改,改完继续部署,继续失败,继续AI改。因为不看代码,又盲目信任AI,就这么反复等待。连续好几天卡在这里。每晚决定部署完就睡觉,然后反复出错、等待、AI改、再部署、再等……很快天就亮了。天亮还没部署成功时,真的非常绝望。

什么时候AI才亲手打碎了这份信任?

有一天晚上,疲惫地等待部署时,依然是反复失败。但发现怎么老是同一个错误?改了好几次还是不成功,心里懊恼极了。

AI说:“我给你个最稳妥的办法,你去云服务器改一下某某文件的某某参数,这次肯定能成。”

真的去改了。改之前还问了一句:“改这个文件会不会让云服务器和GitHub的代码版本产生差异,导致镜像构建完、自动部署那一步失败?”

AI笃定地说:“不会。”(因为同样的问题之前出现过。)

改完,这次部署格外漫长……果不其然,一个多小时之后还是失败了。原因正是Git版本没对齐。

量变引发质变。至此,终于愿意相信:AI写代码没那么靠谱。

最后那周全在修Bug。移动端语音问题、回声问题、历史会话列表恢复,还有依然如旧的部署失败。当然还是只能用AI,此时代码已极其复杂,即使知道AI不靠谱,也回不了头了。

试着去理解代码,让AI一点点讲解。但真是积重难返。只是一个去重逻辑,都要一层一层架构往下深入,从整体到局部慢慢理解,极其费时。因为太陌生,又太复杂了。

终于看到具体代码的那一刻,真是无语。AI用英文常用的SequenceMatcher来给中文短文本去重。

SequenceMatcher主要基于最长公共子序列切分,按单个字或字母匹配。英文单词间有空格作为天然分隔,按单字匹配效果尚可;但中文没有空格分隔,这种匹配法会把“你好吗”和“你好啊”判为高度相似(前两个字一样),而“方案可行”和“可行方案”反而相似度不高(因为字的顺序不同)。

通常的做法应该是先分词(比如jieba),再在词语级别做相似度比较,或者干脆用Embedding做语义去重。如果觉得这些太重,自己写个函数,把中文转成拼音,用SequenceMatcher比较拼音字符串,也会有一定效果。

当时就奇怪,怎么这个应用用起来像“鬼打墙”一样,说过的话应用里的AI还会重复,无论提示词怎么强调都没效果。

再一深挖——好家伙,所有涉及去重的环节,AI全用的同一个SequenceMatcher。无论长短文,无论中英文。

再贴一下此时的项目复杂度,很少在百分制上获得这么高的分数(笑):

  • 圈复杂度Top(Radon)
    • 96分:langgraph_app/graph/coach_unified_nodes.py中的unified_coach_turn_node
    • 75分:langgraph_app/graph/coach_nodes.py中的diagnose_answer_node
    • 73分:api/routers/interview_kb.py中的interview_kb_train_answer
    • 73分:langgraph_app/graph/coach_nodes.py中的apply_tactic_node
    • 73分:langgraph_app/graph/coach_nodes.py中的generate_question_node
    • 70分:langgraph_app/kb/profile_build.py中的run_merged_profile

一般情况下,合格范围是11–20。

  • 认知复杂度Top(Cognitive-complexity)
    • 94分:langgraph_app/kb/interview_kb/migration.py中的migrate_progress_and_sessions
    • 92分:langgraph_app/graph/coach_unified_nodes.py中的unified_coach_turn_node
    • 89分:langgraph_app/kb/profile_build.py中的run_merged_profile
    • 84分:langgraph_app/graph/coach_nodes.py中的diagnose_answer_node
    • 68分:api/routers/interview_kb.py中的interview_kb_train_answer
    • 68分:langgraph_app/graph/coach_nodes.py中的generate_question_node

一般情况下,合格范围是16–25。

  • 耦合度(import可视化,Pydeps)

langgraph_app文件import耦合图

api文件import耦合图

Agent Coding 到底是工程未来,还是 Demo 幻觉?

逐渐认清一个事实:被吹得神乎其神的Agent Coding、Vibe Coding,它们适合的问题空间,可能比宣传中窄得多。

它并不是写代码的终极形态,而更像是适合作为:

  • 原型工具:极快地创建一个MVP进行验证;
  • 整理工具:重构已有的代码;
  • 分析工具:分析数据趋势和错误根因;
  • 半自动写代码工具:在严格的规模和架构约束下的代码生成。

而不是无监督地替代程序员。

所经历的,恰恰是过度放权后的一个经典模式:

初期代码少,Agent改得又快又容易Review;接下来改动越来越跨文件、跨层级;

再后来持续补丁式修复,哪里有Bug就补Fallback、补if/else分支,冗余代码飞速膨胀。

如果要给这个阶段定个分界线,可以认为是在理解成本增长速度超过代码增长速度之后,事情便开始一发不可收拾了。失去了全局掌控,项目开始被AI反向支配。

最可怕的是,这个过程是在静默中发生的。什么时候陷进去的,很难意识到。

但这里的问题是,为什么Agent模式容易造成代码膨胀?可以从以下几个视角分析。

这个AI倾向于“加”而不是“改”

从实际表现来看,这个AI天然偏向“打补丁”,而不是去找全局最优解。这样既安全,又省Token。

遇到需要改功能的情况,它不是删旧功能、重整架构、压缩复杂度,而是加封装、加兼容、加工具、加兜底。“既可以、又可以”是它的信条。于是复杂度逐渐爆炸。

它非常擅长保守路径,明显是觉得“多做点总没错的”。

究其机制,这可能跟LLM的训练方式有关——LLM在逐Token预测的时候,接触的代码修改样本大多以新增代码为主(GitHub上的diff日志里,新增远多于删减)。另外,大多数LLM受限于上下文窗口长度,没法理解跨度较大的代码,导致它倾向于在局部打补丁,而不是全局重构。

架构层面的约束缺失

没有提前做文件结构约束、文件大小限制、模块定义等等。AI没有约束,“自由发挥”也很正常。

Review 没跟上产出

这个的确就是慢慢沉沦的过程。正如前文提到的“温水煮青蛙”,刚开始挺舒服,后果却不太美妙。

正常的开发流程应该是写几十上百行就测一下。而AI写五百行只需要几分钟,但Review这500行就久多了。心生倦怠,慢慢就变成“跑通就行”了,渐渐失去了掌控。

是在唱衰 Vibe Coding 吗?非也

既然让AI Coding去写代码而不加节制的话,项目极容易失控。那Vibe Coding就没有前景了吗?

从实际表现来看,并非如此。已经有模型在这方面做得不错了。可以借以下例子来说明这种感受,但需要强调:这些内容并非严格对比,只是主观使用感受。

当在选择某个论文实验中需要用到的对比模型时,Cursor(底层是Codex 5.3)的建议是:需要有足够证据证明强模型比弱模型强,建议构建三层证据链——公开Benchmark成绩作为先验证据、自己实验中的模型准确率作为内部判定依据,再加一层参数规模作为辅助论据。听起来很有道理。

当把这三层证据链加到实验计划里,拿去给ClaudeCode(Opus 4.6)审一审时,它的回应是:你这是过度设计,用自己的实验准确率这一项就够用了。

对啊!只要实验梯度成立,另外两层证据链不改变结论。

这点让人有些吃惊,因为它在主动做减法。

另一个例子是,当某个Pilot实验中,返回结果绝大部分为空时,发现是因为实验模型的深度思考开关默认是打开的。设置的2048 Max_tokens限制,模型在思考过程就用完了,导致“content”来不及产出就被截断了。

针对这个问题,Codex 5.3的建议是:“针对空内容做一次短答重试”,经典的保守思路。那Opus 4.6怎么说?——“这不只是一个参数问题,而是一个实验设计决策。推理模型 vs 非推理模型:你必须做一个选择”。

局部 vs 全局,一目了然。

当然这种对比并不公平,只是为了展示使用过程中长期感知到的现象,没有严格控制变量。

那么,Claude为什么能反其道而行之,既擅长做减法,又具有全局思维?由于A社没有公开过Claude的相关模型训练细节,可以从以下几个方面进行推测。

Claude 被训练得更像架构工程师,且有很强的反过度设计偏好

普通模型往往更关注怎么完成这个局部任务,Claude却常常从整体视角去看问题。与其说它是个系统构建者,不如说它更像是个系统维护者。它内部就像有一个老到的资深工程师在审视:“这东西能删掉吗?”

它可能训练出了避免不必要的复杂设计、避免做维护成本过高的设计、偏向简洁风格的倾向。很多其他模型奖励的是“回答完整、覆盖全面、别漏”。后者明显是加法。这是偏好,不只是智力。

Claude 似乎眼光更长远,不仅关注任务完成,同时也擅长优化长期成本

即使没说,但感觉Claude会默认在完成给定任务的同时,尽量降低后续维护成本和项目复杂度。它在“方案能不能成立”的基础上,默认增加了二阶考虑。这点在日常使用中很明显。

推测Claude的模型和Agent的目标,都接近“在任务成功的前提下尽量付出最小成本”。特别是如果它的目标函数中隐含了复杂度惩罚项,它做出减法的可能性就更大了。(纯猜测)

Claude 是在整个项目上下文里思考,而不是单轮答题

尽管Cursor看起来也是基于项目回答,还有模有样地按项目区分对话,但使用中能明显发现,它的回答只是基于这轮对话的内容,只有在需要的时候才会调用工具看代码。它不会在最开始的几轮对话中尝试尽量对项目建立整体理解。

而正是因为Claude的长Context,它能存下更多更丰富的项目相关信息,这给它提供了从项目整体去看问题的素材。

这点从文件结构就可以验证。在触发Claude的记忆功能之后,就会出现“~/.claude/projects/<项目文件夹>/memory/”。记忆有触发条件,对话后Claude会在后台默默整理一下,还挺有意思的。

这些既有模型本身的因素,也有产品层的功劳。可能来自模型本身的训练偏好,也可能来自System Prompt中的指令引导,或者两者兼有。

这些特点让人看到了AI Coding从“劳动者”向“治理者”转变的苗头。AI开始会做“熵减”。这种维护型智能、治理型智能的方向,似乎比一味追求速度更有前景,比展示模型反复左右脑互博的形式主义更是强到不知道哪里去了。

而回到代码膨胀和失控的问题上,可以这样说:失控也许是因为大多数Coding Agent的优化目标只奖励功能增长,不奖励复杂度下降,或者至少在后者的成本上付出了不够。

半古法编程策略

现在可以在实践中尝试一种新的AI Coding方法。既然昂贵的Claude暂时用不起,那就从策略层做些改变。

先手工做约束

项目开始前,可以先写一个详尽的MD文档,里面包含项目背景、功能、目标、模块分层、评估方案、分析计划、文件结构等内容。最重要的是增加硬性代码预算:单文件不超过300行,单函数不超过50行。同时手动建好文件结构。

严格限制任务粒度

最好一次只生成1个函数,最多一次生成一个一两百行的脚本。绝不能直接让它一口气完成一整个Pipeline。

建立AI禁令

禁止AI未经允许修改文件结构,禁止AI自行创建新文件,禁止AI在文件间联动修改,禁止AI未经允许引入不了解的库,禁止文件间双向Import。

保留适度摩擦

目前的项目中,已经很久没用Agent模式了,一直在用Ask模式。宁可自己手动复制粘贴一遍AI给出的内容,也不想再让它在项目里进行文件的联动修改。追求速度的模式可能会让人觉得摩擦越低越好,但工程里适度摩擦更安全。最简方式就是把“复制粘贴一遍”当成摩擦。

当代码膨胀速度超出人类 Review 速度时……

显然,这个失控的应用不是用Claude做的。原因无他,Claude太贵了——Token哗哗地往外流。所以暂时还没法尝试如果用Claude做的话,这个应用还会不会像这样失控。主观感觉应该是会好得多。

总结来看,或许当下AI Coding的核心问题不再只是生成速度,而是代码控制权设计。

AI Coding的瓶颈不再是AI写的代码够不够快、能不能解决问题,而是怎么能让人类始终走在代码前面。

就像在这个项目中,AI让生成代码的成本趋近于零,但代码的维护成本没变——甚至更高了,因为对它的理解程度更低。最昂贵的环节——理解、调试、重构——全都得靠人工。当代码产出速度远超理解速度时,其实是在给未来的自己挖坑,制造Review债务。另外,这个项目的周期太紧张了,时间约束也是代码膨胀问题的根因之一。

没办法,这个项目只能单独再拿出两周来重构。目前的计划大概是:

先用Vulture检测死代码,那些被AI和稀泥增加的同一功能的不同函数需要找出来,进行合并精简;再按单一职责拆超大文件,目标是每个文件不超过300行;然后用Pydeps梳理反向依赖、循环依赖,保证所有Import都是单向的;接着深入核心代码,把主要功能的函数逻辑Review一遍,捋清所有分支判断的依据;最后再运行pytest --cov --cov-fail-under=60检查关键路径的测试覆盖率;最后的最后,补一个CI门禁防止膨胀,Flake8校验单行长度,搭配文件行数限制防超标。

在可持续的开发中,“慢就是快”确实是有一定道理的。

免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策