找回密码
立即注册
搜索
发回帖 发新帖

6418

积分

0

好友

814

主题
发表于 昨天 22:37 | 查看: 10| 回复: 0

最近半年,我们在代码库里看到越来越多这样的提交:一个功能完整的 PR,包含数千行代码,单测覆盖率做到 85% 以上,本地集成测试也全绿。可一到提测演示或方案复盘,提交代码的工程师却无法在不借助模型的情况下,准确说出某条核心业务链路在异常分支下到底经历了怎样的状态流转。

代码由大模型直接生成,工程师负责给提示词、粘贴报错、运行命令、验收结果。这种被称为 vibe coding 的开发模式,正在研发团队里快速蔓延。开发人员对代码内部实现细节的掌控力,正在肉眼可见地流失。

如果一个开发人员完全不需要知道代码内部发生了什么,只靠外部输入输出来验证系统,这项工作换成一个不懂技术的产品经理或业务人员,结果并没有本质区别。 当编写代码的动作被模型接管之后,技术人员自己的专业壁垒就会受到直接冲击。

在实际工程推进中,不懂底层原理的使用者在与模型交互时,会很快撞上一堵墙。系统行为一旦偏离预期,或者出现跨多个子系统的复合型偶发故障,缺乏内部认知的人甚至连有效的排查提示词都组织不出来。他们只能把控制台的错误堆栈反复丢给模型,在尝试、失败、再尝试的低效循环里打转。

当然,随着模型和 Harness 能力的增强,这种情况正在减少。

眼下判断开发工程师价值的关键,在于具体业务子领域是否还需要我们深入理解系统的内部实现,以及需要理解到什么程度。

复杂系统的黑盒沉降

原本大家设想黑盒开发只会停留在低风险边缘地带。那些生命周期以周计的营销脚本、孤立的数据管道、一次性内部报表工具,被开发者直接当黑盒丢给模型演化。只要输入输出符合预期,内部堆了多少冗余逻辑,人类完全不用过问。

但这种边界在过去半年里被全面打破了。

真实研发场景中,长周期核心系统同样正在被迅速黑盒化,系统实现逻辑已经开始脱离我们的掌控。

有些系统已经没有任何一个工程师完整读过底层实现,所有人都在靠行为验收驱动迭代。

这一变化的驱动力来自工程吞吐量的极端失衡。过去一个六人资深团队要花三个月才能搭建完的复杂状态机,现在借助高阶推理模型,两天内就能生成数万行代码,还配套数千个通过的单元测试与端到端用例。当代码生成速率超过人类视网膜与大脑工作记忆的物理极限,逐行审查机制实际上已经丧失了防御能力。

我们开始被迫接受这种现实。 面对极其庞大的调用拓扑和复杂的上下文依赖,我们已经放弃了对抽象语法树的微观控制,转而把整个复杂系统视作一个自组织、不透明的动力学网络。

这种转变剥夺了传统工程学给我们的安全感。当不懂内部机理的团队开始掌控复杂黑盒系统,表面上的研发效率提升与潜伏的系统性崩溃风险就被牢牢绑定在一起。

演化哲学的工程代价

把软件代码视同生物 DNA 序列,认为系统可以在目标函数约束下自然演化,是黑盒派的核心立论。生物演化积累了海量无用突变、历史包袱与内含子序列,依然能在残酷的自然选择中维持机体运转。在很多开发者的设想里,只要外部行为约束足够严格,内部代码哪怕堆积成山,系统也能通过模型自我修剪持续向后演化。

但在复杂系统里套用演化哲学,会撞上物理层的硬约束。

软件系统与生物体有本质区别。生物体受物理世界绝对法则的限制,而软件的目标函数是由人类通过测试套件和契约规范手工定义的。人类工程师编写的测试用例,规模再大,也只能覆盖有限的状态空间。

当模型在复杂业务系统里自我演化时,它会不断寻找让测试用例全绿的阻力最小路径。处理一个涉及分布式两阶段提交的业务分支时,模型为了修复一个并发竞争的偶发报错,可能会在关键路径上引入一段极其隐蔽的全局读写锁,或者擅自放宽事务隔离级别。从行为验收角度,那十几个报错的测试用例确实顺利通过了。

这种微小局部优化在多轮提示词和版本迭代后,会引发全局架构的雪崩。并发瓶颈从数据库行锁被硬生生搬运到应用层内存,系统吞吐量在特定流量脉冲下断崖式下跌。团队没人理解这段被模型演化出来的内部调度机制,监控指标只能显示 CPU 软中断升高和连接池耗尽,排查人员根本无法把宏观症状与某个被模型悄悄改掉的同步原语对应起来。

生物演化历经数十亿年,代价是无数个体的死亡与物种灭绝。商业软件工程中,任何一次演化走进死胡同,换来的都是真实的资损、核心数据损坏,以及长达数小时的服务不可用。

重写的大坑与暗知识

当代码产出变得如此快速,以前不敢动的重构操作,现在频频发生在现实里。

对黑盒代码,一旦无法维护,就会有人直接让 AI 来重写。

但对极度依赖隐性规则的复杂系统来说,重写是个大坑。

复杂系统内部实现中沉淀了大量暗知识。这些暗知识由线上历史事故、冷门协议的缺陷规避、上下游陈旧系统的怪异行为,以及特定硬件环境下的性能妥协交织而成。漫长迭代周期里,这些细节几乎不可能被完整记录进需求文档或提示词工程的上下文中。

当一个承载核心业务的复杂黑盒系统遭遇架构死锁时,工程师试图命令模型从零重写一套全新系统。模型根据现有规范文档和接口契约,迅速生成一个结构干净、抽象完美的新架构。但接入真实生产流量的瞬间,系统就会被各种意想不到的边缘异常彻底击穿。

老系统里那些看似丑陋的防重试逻辑、硬编码延时等待、反常的类型转换,恰恰是系统在生产环境存活多年的抗体。黑盒演化过程中,这些抗体没有被人类工程师转化为显性设计原则,而是散落在无法辨识的代码废墟中。

当模型完全接管代码、人类不再阅读实现细节,这些暗知识就会随着上下文窗口的更迭永久失传。重写一个复杂黑盒系统,等于把过去五年踩过的所有生产故障重新经历一遍。这种重写代价,根本不是代码生成速度能弥补的。

工程师的剩余价值

在长周期复杂系统也逐步被黑盒代码吞噬的周期里,技术团队内弥漫着技能贬值的焦虑。天天面对自己看不懂、或者懒得去细看的自动化生成代码,开发人员的专业尊严受到直接侵蚀。

但开发人员的剩余价值非但没有消失,反而在系统复杂度失控的悬崖边被急速放大。落点变了而已。

编写算法、拼装框架、修补语法的低阶体力劳动被彻底剥离。工程师的核心价值,收敛为以下几项不可替代的底层能力。

第一,定义系统物理边界与失败容忍度的系统论设计能力。实现细节彻底黑盒化后,整个系统的生存完全取决于外部边界是否足够坚固。如何设计熔断逻辑,如何划分数据强一致性与最终一致性的边界,如何定义灾难恢复时的降级降速策略,这些决策直接关系到企业商业生死,不可能托付给缺乏全局上下文责任感的模型。

第二,穿透软件抽象层、理解物理基础设施底层的能力。不管上层代码被模型演化成多么不可名状的黑盒,当它最终被编译成汇编指令落到物理 CPU、内存条、网卡与固态硬盘上时,依然必须服从物理世界的客观定律。

跨机房专线延迟突增、底层存储硬件出现坏块、操作系统内核调度产生微秒级锁死……在这些极端场景下,缺乏真实物理世界感知的黑盒系统会瞬间丧失全部应对能力。此刻能挽救全局的,永远是清楚 Linux 内核参数配置、深刻理解 TCP 拥塞控制算法、明白数据库 B+ 树与 LSM-Tree 存储引擎底层权衡的工程师。

第三,决定何处必须保留白盒的战略决断力。在复杂业务全景中,我们必须划出一条绝对红线。红线之外,允许模型疯狂试错、野蛮生长、黑盒演化,换取交付速度;红线之内,涉及资产结算、核心密码学协议、权限鉴权核心、数据持久化原子性的基石模块,必须死守白盒阵地。每一个状态位、每一行锁逻辑、每一个并发屏障,都必须由人类大脑完全理解、推演与严格审计。

拥抱黑盒演化,是应对代码生产力爆发的必然妥协;但盲目迷信黑盒,则是工程理性的自杀。

白盒边界的落地

不过,把红线画出来还远远不够。

如果白盒意味着所有代码都靠人逐行理解,几万行关键模块很快就会耗尽团队审查能力。如果白盒只意味着安排一位资深工程师签字,它又会退化成形式上的责任分配:代码已合入,签字的人却解释不了失败路径。

从实际出发,白盒要求可以落到几个能检查的问题上:

这个模块维护哪些状态?哪些状态转换绝对不允许发生?一次操作在什么位置产生不可撤销的影响?执行中断以后,如何判断它到底完成到了哪里?修改这里,会影响哪些调用方的既有假设?

负责的工程师需要能独立回答这些问题,并指出对应的实现位置。

这里依然允许模型生成代码。我们限制的是未经理解的关键变更进入系统。生成与理解可以由不同过程完成,但理解不能被一份自动生成的说明替代。

边界还需要沿着依赖关系检查。

假设一个关键模块本身经过严格审查,但它依赖的公共组件被模型改掉了失败处理方式,原有结论就可能失效。白盒边界如果只按目录划分,很容易漏掉这种变化。把关键模块依赖的行为约定一起纳入审查范围,尤其是超时、重试、错误返回和状态写入的语义。

这样做会拖慢部分公共组件的修改速度。我们需要接受这笔成本,也需要控制规模。如果一个普通改动总要召集半个团队评审,说明关键模块依赖了过多外部细节,应当考虑收缩接口和状态共享范围。

白盒区域也不必永久固定。一个模块的状态被拆出、接口变得稳定、替换方式得到验证之后,可以降低内部审查强度。一个原本独立的工具开始承接共享数据写入,就应该重新评估。

「核心业务」四个字无法覆盖整个代码库。范围画得太大,最后往往只能整体降低执行标准。

验证独立

黑盒能被接受到什么程度,取决于我们能从外部验证什么。

这里最容易出现的问题,是实现、测试和验收结论都出自同一条生成链路。模型先理解需求,再生成代码,随后根据代码补齐测试,最后告诉工程师所有测试都通过了。

如果最一开始的理解就遗漏了一个条件,这个条件就可能同时从实现与测试里消失。

覆盖率无法自动发现这种遗漏。某一行代码被执行过,只能证明测试路过了那里。异常分支执行之后留下的状态是否正确,仍需另外检查。

因此,关键验收条件必须在实现之前确定。至少把正常结果、禁止出现的结果,以及失败后允许留下的状态写清楚。

尤其要检查跨多个动作的过程:前一个动作已完成,后一个动作失败,系统应该停在哪里?用户重新发起请求时,允许重新执行哪些部分?超时之后,调用方能否把结果当成失败?

这些问题没有答案时,继续堆测试数量并不能补上设计缺口。

模型可以帮忙枚举场景、生成数据、执行验证。但涉及业务承诺的条件,需要由了解系统的人确认。验收阶段还应保留独立于当前实现的依据,避免模型通过修改预期结果,让实现重新获得通过。

测试本身的变更也要审查。

删除一个失败用例,有时是因为旧需求确实失效,有时却只是当前实现暂时无法满足它。二者在测试报告上没有任何区别。对稳定约束的删除、放宽和跳过,我会要求单独解释,不能夹在几千行功能改动里一起合入。

验证强度当然有成本。复杂故障场景需要准备环境,大规模数据验证消耗资源,长时间运行的检查会增加反馈延迟。我的做法是分开执行频率:局部检查跟随每次修改,影响面较大的验证放在合入和发布环节,昂贵的故障验证集中覆盖关键路径。

目标是让不同风险都有对应的检查位置,同时避免每次修改都等一套庞大验证流程。

限制单次变化

代码生成速度上来以后,团队很容易把更大的任务直接交给 agent。

一个需求里同时包含接口调整、状态机修改、数据结构迁移和历史代码整理。模型可能一次完成,最终差异也显得相当统一。但人工审查时,很难再把每一处变化与最初动机对应起来。

一旦发生回归,定位范围同样会扩大。

我们要控制单次变更所包含的独立决策数量。功能增加、结构整理、历史兼容性清理,尽量分开进行。每一部分都需要有能单独解释的目的,以及对应的验证结果。

这里不适合机械地规定 PR 行数。一次自动生成的重复性修改可能涉及很多文件,实际语义变化很少;几行条件调整,也可能改变整个系统的状态约束。

这次只改变了哪些行为?哪些证据支持这些变化?如何撤销?

让 agent 先提交修改计划也有帮助。人可以在大量代码生成之前,检查它准备触及哪些模块、是否引入新的共享状态、是否扩大已有接口的职责。

当然,计划不能当成执行事实。模型在修复过程中可能偏离原计划,最终仍需核对实际改动。尤其要检查那些为了让测试通过而新增的兼容分支、默认值和异常处理。

另外,权限应该跟着风险划分。修改实现、调整验收标准、操作生产数据,是三个不同等级的动作。不能因为 agent 已获得代码仓库的写权限,就顺带允许它自行决定另外两件事。

保留接管能力

即便验证做得足够认真,团队仍需准备一种情况:系统出了问题,agent 连续几轮都没找到原因。

这时工程师需要接管什么?

首先要接管的是故障判断和损失控制。当前还有哪些请求正在写入?哪些动作可以暂停?哪些状态已经无法直接撤销?继续重试是否会重复产生副作用?

如果这些问题无法回答,贸然修复代码可能让后续处理更困难。

因此,关键路径上的可观测信息需要围绕状态变化设计。只记录异常堆栈,通常不足以判断一个业务动作完成到了哪里。需要能把一次操作的开始、关键写入和最终结果关联起来,同时区分「没有执行」与「执行完成但没有返回」。

信息越多,记录与检索成本也越高。涉及数据内容时,还需限制记录范围。我不会要求把所有局部变量都写进日志,而是优先保留能判断执行阶段和副作用的信息。

恢复方案也要区分代码与数据。

代码版本退回后,新版本已写入的数据仍然存在。旧实现能否读取这些数据?正在执行的任务能否继续?外部已经发生的动作如何处理?这些都需要提前确认。发布平台上的回滚按钮,不能证明业务状态真的能恢复。

团队可以让模型生成恢复方案,但需要通过演练检查其中的假设。演练时暴露出一个无法判断完成状态的步骤,就应该补充相应记录或操作方式。

这些工作会占用原本拿来交付功能的时间。我们要优先安排给失败后难以恢复、影响范围又大的模块。对于可以直接丢弃并重新生成的结果,没有必要采用同等投入标准。

我们也不该假设所有故障最终都需要人手工解决。agent 能完成的排查,继续交给它。人需要保留的,是判断它是否仍在有效推进的能力,以及必要时改变排查方向、停止危险操作的权限。

把暗知识留下

关于暗知识,还有一点需要说清楚。

只要代码、历史记录和运行证据仍在,很多知识就有机会被重新找回。但随着版本累积,重新发现的耗时只会增加。故障发生时,团队未必等得起。

每次关键修改,顺手留下三类信息:这段特殊处理保护了什么约束,当初在什么条件下出现过问题,以及什么证据能证明它将来可以被删除。

单独写一句「兼容历史逻辑」,对后来者帮助有限。模型也无法据此判断这段逻辑究竟承担必要保护,还是早已失效的补丁。

记录要贴近决策发生的位置。约束可以进验收条件,历史问题可以转化为回归场景,难以自动验证的条件则保留在模块说明和变更记录里。没必要把所有知识塞进一份不断膨胀的架构文档。

尤其要防止另一种浪费:模型生成了大量说明,团队却没检查哪些是事实,哪些只是根据代码推测出来的意图。

「当前代码这样执行」和「系统必须这样执行」需要分开记录。前者描述实现,后者约束未来修改。混在一起,模型可能把历史偶然行为永久固化,也可能把必须保留的规则当成可整理细节删掉。

重写之前,这些记录应当成为核对清单。找不到来源的特殊逻辑,需要进一步追踪,不能仅凭实现难看就判定它没有价值。

同样,也不能把所有历史补丁都当作不可触碰的要求。长期保留失效约束,会让新系统继续背负旧系统的复杂度。删除它们需要证据,保留它们也应该能说明理由。

重新分配时间

这会改变工程师日常工作的时间分配。

一部分原本用于编写常规实现的时间,可以转移到约束定义、关键路径审查、失败恢复和演化设计上。另一部分确实可以省出来,用于交付更多需求。我们没必要为了证明专业价值,把节省的时间全部重新填满。

不必要求所有工程师都深入内核、网络协议和存储引擎。基础设施知识在某些问题上非常关键,但大量故障仍然来自业务状态、依赖关系和错误的边界假设。团队需要根据系统的风险分布建立能力,不能用一套底层知识清单替代具体判断。

对个人而言:能否识别模型没有回答的问题?能否发现验证结论依赖了未经确认的假设?能否在陌生实现中定位关键状态?能否解释一次技术选择会给后续修改增加什么成本?

这些能力需要通过实际接触代码来培养。年轻工程师如果长期只负责转发需求和粘贴错误,却没有机会追踪实现、分析失败、参与关键决策,团队几年后可能就会出现知识传承断层。

因此,我们需要让工程师对完整的局部问题负责:提出约束、使用模型实现、解释关键行为、验证异常路径,并参与上线后的问题处理。模型可以承担其中大量工作,但负责的人需要能检查结果。

团队层面的评价也要变。生成了多少代码、完成了多少轮对话,都不适合作为主要指标。我们该观察的是一次需求从提出到稳定运行花了多久,评审与返工占了多少时间,线上问题是否反复出现,以及一个模块是否越来越难由其他人接手。

如果编码时间缩短了,排查和返工时间却持续增长,就需要调整当前流程。交付数量暂时增加,也无法掩盖恢复能力和知识覆盖的下降。

回到最初那个数千行代码、测试全绿的 PR,我们不会因为提交人无法背出全部实现就否定它。

但如果他解释不了核心异常分支的状态变化,我们就得要求他补上这部分理解:追踪关键写入,核对失败后的状态,检查重复执行的后果,再决定是否需要增加验证或修改实现。

这个 PR 仍然可以由模型完成修改。合入之前,负责的工程师需要知道哪些结论已经得到验证,哪些风险仍然存在,以及出问题之后应该从哪里开始处理。

以上。关于 vibe coding 之后团队如何在黑盒与白盒之间划界,也欢迎到云栈社区继续交流。




上一篇:REA 逆向工具解析:AI 时代,闭源软件还有理解门槛吗?
下一篇:DeepSeek Harness 到底做了什么?详解 Agent 循环、Session 与工具管道
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-10-25 02:09 , Processed in 0.071072 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表