月刊:2026年4月
monorepo新实践
好几年前,我就已经在项目中实践前端项目monorepo的组织形式了。
初衷是为了做模块划分,例如不同的构建平台分别放在不同的npm包里。但是在实践中发现,至少我当时所采用的yarn monorepo体系,并没有有效地达成我的目标,主要表现在两个方面:
一是“幻象依赖”问题(Phantom Dependencies)。也就是说,一个子包内不需要显式地声明某个依赖项,只要在同一个monorepo工程中其他兄弟包有这个依赖,它也可以直接访问这个依赖项中的内容。例如,antd-mobile这个库本来应该限制在web-mobile包内进行访问,但实际上在web包中也依然可以意外访问到。
二是“封装泄露”问题。“封装”是软件工程的基础理论之一,任何一个稍微大一点的项目或者模块都必须或多或少地依赖它。但是在传统的nodejs模块系统中,并没有提供文件夹层级的代码封装。我们只能在一个文件中通过export或者exports.moudule来实现代码封装,但是不能在文件夹这一层级实现,调用方可以通过相对路径的方式定位到对应的js或ts文件并直接访问其中导出的成员。
这两个问题其实非常严峻,它们直击一个本质问题:monorepo的意义到底是什么?现在这个半吊子的能力,似乎还不如放弃monorepo方案,就在一个repo中声明一个包,用传统的文件夹的方式来划分不同的模块。
而在现在,针对以上两个问题,分别有了更新的、更可靠的解决方案:
第一,用pnpm代替yarn来解决“幻象依赖”问题。每个包都必须自己显式地声明自己所有的依赖项,否则无法访问对应的资源。
第二,用nodejs提供的package-entry-points能力,以包为单位进行高层次的代码封装。只有在显式指定的文件入口中导出的成员,才能被其他包访问。
这两个方案在我看来已经相当可靠,几乎解决了大部分的痛点。但是,从旧的代码基础、或者从旧的架构思维中切换到这个新的模式,还是需要不少的努力和充沛的经验储备的,有不少的坑要踩。
前端开发工具提效
js作为一门脚本语言,不管实现引擎例如v8等实现得再怎么精妙,其运行效率始终都是低下的,除了极少数特定领域的极限优化,在绝大多数情况下,js 都会比“原生语言”,例如 go, rust, c/cpp 等,慢一个数量级。
随着项目规模的膨胀,第一个发出默默抗议的,是传统的前端构建系统(webpack等)和开发时的检查系统(typescript, eslint等)。大约在10万行规模下,开发人员就要“明显地”等待这些工具的运行结果,随后这个等待时间更是会与代码规模一起成线性比例地膨胀,到五十万行左右就足以成为开发阶段的一个显著限制因素,成为架构师必须要面对和解决的难题。
好在社区也有对应的解决方案。
关于构建,我在上个月的文章中已经介绍过了rspack。后来我仔细研究了它们的文档,发现已经有围绕rspack提供的一套方案。例如rsbuild,是以rspack为基础,封装出的一套“开箱即用”的快捷配置,对标的是vite。而在rsbuild之上,又进一步提供了rstest和rslib,分别对应各自的细分场景。
我已经做了一些尝试,效果还不错,在新的、构建配置不太复杂的项目中,rsbuild等甜点工具完全可以作为默认的构建工具来使用。不过对于比较复杂的项目来说,仍然需要手动维护rspack,这是无法避免的。而即使无法采用rsbuild,我觉得也依然值得去读一读它的文档,选择性地参考他们的开发思路和业界风向。
对于语法检查工具,也就是typescript,我在上个月也简单介绍过官方推出的v7版本,也就是go语言的重写版本。上个月我说由于ts从v6开始语法逐渐变得严格、现有项目几乎无法迁移到v6或者v7版本,但是随后遇到一些问题,我在权衡之下,依然只能选择排除万难、强行把现有项目的庞大代码库匹配了新的语法规则。回过头来看,“倒也不难嘛”,有必要的情况下依然是可以做到的。
对于风格检查工具,也就是eslint,我上个月并没有提及它的替代品。目前业界一种相对常见的替代品是biome,其主要思路就是用rust来重写eslint,并顺便内置了代码格式化工具来替代prettier。但很遗憾的是,经过我的初步尝试,我发现它的配置文件居然无法满足我一些很基础的要求,于是最终我选择了与它热度相差无几的另一个替代品——oxc。他俩的实现思路是大致相似的,不过后者配置更灵活,至少能够满足我现阶段的所有要求。
升级了这些工具之后,开发体验确实得到了明显的提升。这也反映了业界的大趋势:逐步采用 go, rust 等高效的现代编程语言来替代低运行性能的 js 等脚本语言。并且我大胆预测,在AI时代、“写代码”这件事本身已经变得更容易的现在,这个趋势会越来越明显,覆盖越来越多的场景,甚至不仅仅局限于开发阶段的工具,还会逐渐侵入一线业务层实现。
Harness Engineering
说好听点叫“最近AI编程领域发展迅猛,已经跨越一个基础临界点”;说难听点也可以算是一种“打脸”——就在不久的几个月前,我还在说,AI编程只能胜任小规模、强确定性的编程任务,而现在,我已经在探索”Vibe Coding”的最佳实践。
Harness Engineering这个概念的最初提出者似乎已经无从考证,它更像是业界在经过几年的AI编程探索之后,自发收敛总结出的一套理论体系。
现在可以比较确定的给出它的明确定义:“围绕 LLM 构建一整套 “约束 + 反馈 + 执行” 的外部系统,使其稳定地产生可用结果”。
我看了看手边,已经落了一层灰的一本书,也是被网友们称为“人族天阶功法”的一本书——《工程控制论》,英文原名《Engineering Cybernetics》。这个词与AI领域的Harness Engineering很相似,但不同。本质区别在于,前者依赖的是一个可预测、可建模的工程模型,也就是status = f(x, y, z)的函数式模型,只要给定了参数x, y, z,理论上应该得到相同或者近似相同的结果(如果不精确那就说明参数不够充分),在这种基础上,“系统通过连续反馈实现稳定控制”。运维界的著名框架k8s其实就是借用了这个术语。
而Harness Engineering底层所依赖的LLM则完全相反,它是一个概率性的“不可控组件”。软件工程师们围绕LLM建立的是一套“约束系统”,我们不管这个系统的内部实现细节,只从外部验证结果,通过黑箱式的调整,使系统达成一个“能完成任务”、“质量凑合可以接受”的目标。也可以说,业界刻意避免使用Cybernetics这个术语以免造成歧义。
最近能够公开查阅到、最权威的对于Harness Engineering的论述文章,是Openai发表在官网的《Harness engineering: leveraging Codex in an agent-first world》一文。
虽然无法否认的是,我在自己的调研过程中,使用了许多商业公司提供的服务或者开源社区提供的代码或者解决方案,难免在不知不觉中受到了引导,但是我依然可以说在相当程度上是靠自己的实践总结出了一套理论思想。当我看到上述这篇文章的时候,有种“英雄所见略同”,或者说“找到了嘴替”的痛快感。我的主要思想与Openai的这篇报告是高度重合的。
主要思想我总结如下:
重新定义工程师的角色。尽量避免人工编码,当AI提供的代码不符合预期的时候,不是手动去修改,而是思考“为什么ai会这样写?它缺少什么信息导致了没有按我的意图执行?”,随后修改环境信息、提供更详尽的指令,帮助AI自己重新生成符合预期的代码。
明确架构设计。将较大的目标分解成较小的构建模块(设计、编码、审查、测试等),引导AI构建这些模块,并利用它们来解锁更复杂的任务。明确模块之间的边界,尽量避免AI对其他模块进行大量的搜索或者猜测。
保证系统的可观测性。让AI自己能够分析它写的代码对应的运行结果,特别是前端,即使费力也要尽量通过 chrome devtools 来给AI接入读取结果的可能性。除此之外还有日志、性能指标等数据,为此可能需要一套完整的支持保障系统以及其对应的 SKILLS。
压缩知识库,为其建立索引。一份“大而全”的AGENTS.md可能不仅没有好处,反而还对系统有害。我们尽可能将知识整理成文档,只提供简短的索引,让AI自己选择自己所需的上下文。
吞吐量改变了合并理念。在一个代码吞吐量远超人类注意力的系统中,不可能再坚持传统的代码审核流程。压缩合并请求的生命周期,测试中发现的问题尽量通过后续合并来解决,而不是阻塞整个项目开发进度。
熵与垃圾回收。AI会倾向于复制项目中已有的模式,即便它参考的那个模块的实现并不合理;随着时间推移,偏差会逐渐累积。与其花费时间人工调整,不如明确指定一些“原则”,让AI自己定期修复和重构、偿还自己拉出来的技术债。人类的审美偏好只需一次捕捉,剩下的交给机器去执行重构。
以上。
但是必须要强调的一点是,上述理论是针对完全 vibe coding 模式维护项目有效,OpenAI自己也承认他们这次的实践是一个“数个月时间、七个人、百万行代码的内部项目”。而如果要运用在商业项目,特别是那些已经在“传统古法编程”模式下维护了很长时间的旧的商业项目中,还是非常具有挑战性的。
其实,上述很多思想,并不来源于AI时代从无到有的原创,而是来源于于几年前“古法手工编程”的管理经验:当走上负责人的岗位,面对多位年轻的同事们提交的代码,当时面临的困难和挑战其实与如今面对AI提交海量代码的场景极其相似。我也多次强调这个理论:作为架构师,要学会抓大放小,定期铲屎。
工程师,无论是传统的建筑、土木、机械工程师,还是我们软件工程师,永恒不变的工作目标一直是:“权衡”。在理论与现实进行权衡。在AI的时代,这一目标依然不变,我们依然需要权衡,只不过天平的方向发生了改变,我们需要把工作的侧重点也相应地调整。以前觉得可望不可及的TDD, DDD等设计理论,rust, c++等具体落地的语言,在如今都值得我们加大投入。
二游的新篇章
今年春节后,二游圈传出噩耗——《尘白禁区》宣布无限期停服维护。
原因也颇为搞笑,是他们居然找到中国邮政搞联名,真是一个敢想一个敢做,两个傻子一拍即合。像尘白这种半软半硬的色情游戏,本来低调一点、靠着麻辣仙人小圈子作为基本盘,完全可以活得很滋润的。但是他们还是选择盲目扩圈,甚至扩到了国字号头上,那些色情制片人做成邮票光是想想我都感觉很尴尬,它得到一个暴死的结局那是一点都不意外。
不过只看这件事的话,似乎也可以理解。因为业界另一大毒瘤《恋与深空》——一款完全面向女性玩家的半软半硬的色情游戏,之前就跟中国邮政联名过,当时也没什么问题。可是性别对换一下,换《尘白禁区》来立马就暴死了,也可以说是意料之外。
在去年我写过的《二游格局之我见》一文中我已经做过阐述。最大的问题还是他们非常不专业,根本搞不懂市场想要什么。我不理解他们怎么会一直抱有一个“机甲游戏的梦想”,其实尘白禁区刚上线的时候就是走的“硬核科幻射击游戏”风格,在将死之际临时180度大转弯走上了媚男路线才得以复活;可就算这样还不吸取教训,还要把大量的资源投入到另一款“更加硬核的机甲射击游戏”《解限机》上,做出了后者这个直接暴死连复活的机会都没有的失败作品,还严重拖累尘白禁区的开发资源(更别说他们本来就挺菜的就算认真恐怕也没什么能力)导致一直没有核心玩法而被大量正常玩家弃坑。至于有些逆天剧情文案、与玩家社区之间的风波事件更不用提了。
简而言之就是它该死。作为充值过四位数金额的玩家,我觉得我有资格说这句话。
春节之后的这一小段时间里,游戏业界也在快速发展,甚至可以说竞争非常激烈。
《蓝色星原》二测,我没有抽到资格,非常遗憾。《异环》定档公测,我已经迫不及待了,从网络流传的三测视频来看,强得离谱。还有插队抢跑的《洛克王国:世界》和《王者荣耀世界》,虽然我个人对这俩题材(以及发行厂商)不感兴趣,也觉得客观上质量很平庸,但是平心而论,其实这些游戏质量已经很高了,我还能骄傲地鄙视他们,不得不说现在的游戏玩家吃的是真好。