在 arm64 的 Mac 上运行古董前端项目,憋出一肚子关于技术债的想法

2025年5月19日Elecmonkey

技术债不完全是技术问题。它关于 CI/CD 现代化,关于组织治理,关于每个技术人的开源文化与社区参与。 ——我自己说的。

运行 x86_64 的 Node.js

众所周知,npm 上面的包不一定都是用 JavaScript 写的,这些包被以二进制形式发布。但是,苹果第一台 Apple Silicon 芯片的 Mac 2020年底才发布。五年前的项目很多还在跑,但是五年前的包很多没有为 ARM 架构的 macOS 提供预编译的二进制文件。

好在,苹果为我们提供了一个叫做 Rosetta 2 的兼容层,允许以模拟方式运行 x86_64(Intel 架构)的程序。

arch 命令用于指定以哪种架构运行程序,启动一个 x64 的终端:

bash
1arch -x86_64 zsh 2 3# 可以通过以下命令确认当前终端架构: 4arch 5# 应该输出:i386

在 x86_64 终端中,需要安装 x86_64 版本的 Node.js。使用 nvm 管理 Node.js 版本,可以这样操作:

bash
1# 先卸载当前的 ARM 版本 2nvm uninstall v18 3 4# 安装 x86_64 版本 5nvm install v18 6 7# 使用该版本 8nvm use v18
bash
1# 确认 Node.js 架构 2node -p "process.arch" 3# 应该输出:x64

在 x86_64 环境下安装项目依赖:

bash
1npm install abc@x.y.z

最后,如果经常需要在不同架构的终端之间切换,可以考虑在 .zshrc.bashrc 中添加别名:

bash
1alias x64='arch -x86_64 zsh'

这样,只需要输入 x64 就可以快速切换到 x86_64 终端了。

技术债无法避免吗

雪球如何滚起来

技术债这个词已经被说烂了,这是个很形象的比喻——债务这个东西,短期还钱影响自己手里现金流,天天拖着不还,拖出天量的利息。

从理想的情况来说,每当所依赖的技术栈有更新的时候,就应该及时跟进去更新一下依赖。小版本的更新大概率不会有问题,有 BREAK CHANGE 也不会太多,官方一般会给出迁移替代说明。此时就算是不熟悉项目的维护者也能根据 BREAK CHANGE 导致的报错定位问题,完成更新。

但是如果项目依赖长期不更新,等到五个十个五十个有 BREAK CHANGE 的版本过去了,这就没法更新了。于是只能推倒重构,或者干脆躺平摆烂,能跑一天是一天,有一天彻底跑不了了,那真就只能相信后人的智慧

那维护的同学怎么不更新呢?

这个问题的答案很简单,「升级依赖」不是一个看得见的任务点,它不直接产生 KPI 也不直接产生汇报,不会被列入计划之中。不动它,与我无关。升级了,且不说如果有 BREAK CHANGE 需要做小迁移是给自己找事儿,就算没有,也是给自己制造风险,万一出问题那不是我的锅。程序员个体去「主动」升级或重构,风险自负但收益不归自己,这违反了人性的激励机制。

不要出事触发治理

说实话,技术债确实难以避免。

对于一个组织,可以是团队、可以是企业,最好要有一个懂技术、又有权力(决策或财务)的负责人存在。TA 能知道软件被做出来上线只是软件生命周期的开始,软件需要持续演进、维护。当已经积累了大量技术债的时候,TA 能争取到时间、资源,推动大规模的重构,在成本可控时解决问题。

但是这好像还是要求有一个个人,自己扛下风险去推动演进,不管是哪一层级的人。如果没有人能用个人的意志力去推动这个吃力不讨好的事情,那就只能靠出事触发治理

于是,地板下的白蚁越来越多:日常看不见,不出事没人管;出事就一大片塌方。塌方了自有救援队来救援,尽管我们都知道,提前重建危楼的成本代价不可能比得上塌方后救援的成本。

解药之 「现代 CI/CD」

技术债很多时候不是因为写得不好,而是因为代码写完之后就没人持续关注它了。从这个意义上讲,技术债不是技术问题,而是管理能力、组织治理能力。

如果项目发起者能够有意识的利用现代 CI/CD 系统,在日常开发中持续地发现问题、推动更新,那技术债的积累速度就会大大减缓,甚至在早期就被抹平。比如说,自动依赖升级工具(如 RenovateDependabot),自动检测依赖更新、自动创建 PR,开发者 Review + Merge。比如,单元测试要有覆盖率,lint、类型检查、构建、弃用警告不能当作看不见,没有什么特别的问题应该引入一些硬性要求,尽可能的消灭「违反最佳实践但能跑」带来的警告。

现代 CI/CD 自动持续部署,Git 提交或者 Git tag 发布版本。这带来另一大好处,即便升级出问题,也能迅速定位并恢复,降低"更新失败"的风险成本,从而鼓励更频繁地小步快跑式技术迭代。

解药之 「社区参与」

相当多写项目的人,抱着「我学习xx语言」「我学习xx框架」的心态,这些东西对他们来说是纯粹的知识,干活儿的工具,对于后面的「文化」根本不感兴趣。

那这没错,它们当然是知识,可以成为学校或者培训班的课程。但是它们本身只是一个个的开源项目,是 GitHub 上的一个 repo,是有社区讨论、维护、演进路线的开源项目。

开源社区不是稳定 API 提供商,是一个活的生态。一个模块是否继续维护、是否推荐使用、是否被标注 deprecated、有哪些替代方案——这些路线在社区的思想碰撞中产生,这些信息往往出现在论坛中、写在 README、Issue、Discussion、Release Note 里,不是报错信息。

对于自己常用的库和工具,做到参与社区才有可能真正得心应手。也许并没有提 Issue、提 PR 的精力或者想法,但至少要关注社区动态,知道背后是一群什么样的人做了这样一个名叫 Node.js 的工具,知道 React 社区对于"前端的服务器"角色的思考,知道 Vue.js 社区的核心维护者对于未来构建工具链的想象与期望,我们或许就能知道这个开源项目自己的「性格」。这不是刷B站上漫山遍野的 Vue 网课、背 API 能得到的。

结尾

架构、测试、文档、CI/CD、依赖更新、社区文化……这些"非功能性"的工作,并不直接与业务功能相关,被我们排除在 roadmap 之外。但我们大概都能隐隐的想明白,这些"非功能性"的工作,恰恰是支撑一套系统长期稳定运行、可持续演进的根本。