01|这篇真正要讲的,不是“JavaScript 社区为什么爱造轮子”,而是 npm 为什么会把“拆得更细”推成一种看起来很合理的生态直觉
今天很多人回头吐槽 npm 生态,最常见的一类抱怨就是:
- 一个项目依赖太多
- 一个很小的功能也要装包
- 依赖树深得吓人
node_modules看起来像失控森林
于是很容易顺着得出一个结论:
JavaScript 社区是不是天生就特别喜欢把东西拆碎。
这话当然不是完全没道理。
可如果只停在“社区风气”这一层,还是会把历史写浅。
因为 npm 世界里的小包文化之所以会疯长,
不是因为突然有一批人集体迷恋一行代码一个包。
更深的原因是:
npm 把“把一个小功能独立发出来、让别人顺手装上”这件事做得太便宜了。
而一旦复用成本被压得足够低,
整个生态就会很容易出现一种新直觉:
- 能拆,就拆
- 能复用,就复用
- 能发成独立包,就别留在大项目里
- 能依赖现成模块,就别自己重写
这套直觉一开始看起来并不疯狂。
恰恰相反,
它在当时甚至显得很先进、很现代、很工程化。
所以第二篇真正要问的,不是:
“为什么 npm 生态后来有那么多小包?”
而是:
为什么在 npm 世界里,把东西拆得更细,最早会被很多人自然理解成一种进步。
02|因为 npm 先完成了一件特别关键的事:它把“共享模块”从一件麻烦事,变成了一件默认小动作
上一篇讲的是 npm 为什么能迅速长成中央仓库。
那一篇的重点在入口。
这一篇要往前推一步,去看入口一旦形成之后,会发生什么。
最关键的变化就是:
共享模块的摩擦突然变得非常低。
在很多生态里,你当然也能复用代码。
可复用如果要经过很多步骤,
大家就还是会本能地偏向:
- 自己写一份
- 直接拷进项目
- 在大仓库里顺手放着
因为“单独抽出来再发出去”太麻烦。
npm 把这件事改了。
有了 package.json,有了版本,有了依赖声明,有了 publish,有了 install,
一个原本只服务自己项目的小功能,
突然就很容易被做成:
一个独立可流通的软件单位。
这会带来一个很重要的心理变化。
那就是开发者不再只把“抽公共逻辑”理解成:
项目内部整理代码。
他开始把它理解成:
我可以把这段逻辑正式做成一个包。
这一步特别关键。
因为只要“正式做成一个包”的门槛足够低,
模块拆分就不再只是代码风格问题。
它会慢慢变成:
- 发布习惯
- 复用习惯
- 生态协作习惯
而这正是小包文化会爆炸的前提。
03|为什么“拆得更细”在当时会显得如此有道理?因为它刚好同时满足了工程美学和开放生态美学
这一步要再往里看一层。
因为一个习惯如果后来能长成文化,
通常不是因为它只有坏处。
恰恰相反,
它往往是在早期看起来很像正确答案。
小包文化就是这样。
它最早吸引人的地方其实非常多:
- 单一职责看起来更清楚
- 包做得小,测试和维护似乎更容易
- API 边界更窄,复用看起来更直接
- 不同项目之间可以像搭积木一样组合能力
这套想法其实很顺。
因为它同时踩中了两套很强的美学。
第一套是工程美学:
一个模块最好只做一件事。
第二套是开放生态美学:
既然这件事别人也可能会用,那就把它独立出来共享给所有人。
你会发现,这两套美学在 npm 世界里几乎天然互相强化。
模块越小,
看起来越像“职责纯粹”。
越职责纯粹,
越容易被包装成一个独立包。
越能独立成包,
就越容易被别的项目拿去复用。
所以这时候,小包文化并不只是一种技术偏好。
它看起来更像一种道德上也站得住脚的好习惯:
我不是在无谓拆碎代码,我是在给生态贡献更可复用的积木。
这就是它最早最有感染力的地方。
04|更麻烦的是,npm 还让“依赖别人写的小东西”变得几乎没有心理负担
如果只是“发包容易”,小包文化还不一定会疯长到那个程度。
真正把这件事推快的,是另一面:
装包也太容易了。
这点很重要。
因为一个生态里再多小模块,
只要使用成本高,
它们就很难形成真正的繁殖效应。
可 npm 世界给出的是另一种现实:
- 想用就
install - 依赖会写进
package.json - 版本范围可以声明
- 下一次别人拉项目,直接一起装下来
Node 官方后来对 npm 的介绍甚至会把这种感觉说得很直接:
registry 里有海量包,
几乎什么都有。
这会让开发者逐步形成一种本能:
遇到一个很具体的小问题,先看看有没有现成小包。
而一旦这个本能形成,
生态就会自动往“更细颗粒、更高复用”那边滑。
因为需求会反向塑造供给。
使用者愿意装非常细的小包,
作者就更愿意发非常细的小包。
作者越愿意发,
使用者就越觉得“这类小问题应该先搜包”。
于是一个自我强化的循环就长出来了。
这时候,拆细不再只是作者单方面的偏好。
它变成了供需两端一起推高的文化惯性。
05|所以 npm 最早推广的,不只是复用本身,而是一种特别激进的复用观:连很小的能力也值得独立流通
这是第二篇最关键的一层。
很多生态当然也讲复用。
可 npm 世界的独特之处在于:
它把复用推进得非常细。
不是只有“大库”“大框架”“大工具”值得共享。
而是连这种东西也可以被独立出来:
- 一个字符串处理函数
- 一个对象判断函数
- 一个颜色转换函数
- 一个命令行参数小工具
换句话说,npm 世界慢慢形成的是一种特别激进的复用观:
只要某段逻辑有可能在别处再出现一次,它就值得被做成包。
这套观念如果只从结果看,当然很容易被嘲笑。
可如果放回当时的历史位置,它其实非常顺。
因为 npm 和 Node 一起给了这种观念足够强的现实支撑:
- 包可以轻易发布
- 包可以轻易安装
- 包可以声明依赖关系
- 包可以被更多包继续依赖
一旦这些条件都成立,
“连很小的东西也值得独立流通”就不再只是口号。
它会变成一个真的能跑起来的生态模式。
而这正是后来那些著名微型模块层出不穷的根本原因。
06|可问题也就从这里开始:当拆分越来越容易,生态很容易悄悄把“能拆”误当成“该拆”
这一步,就是第二篇要开始转折的地方。
因为小包文化最早的问题,不在于它完全错误。
问题在于:
它太容易从“这在某些情况下有益”滑向“这几乎总是更先进”。
这就是很多技术文化常见的命运。
一个原本很有价值的原则,
一旦进入大规模扩散阶段,
就容易被过度普适化。
在 npm 世界里,
这种过度普适化很容易表现成:
- 只要能独立,就最好独立
- 只要足够小,就好像更优雅
- 只要能复用,就比留在本地更高级
这时,“单一职责”会悄悄变形。
它不再只是帮助你控制复杂度。
它开始变成一种文化压力:
你好像不把东西拆出去,就显得不够模块化。
而一旦文化压力形成,
技术系统就会被推向更极端的方向。
这也是为什么 npm 生态后来的问题,并不只是“包太多”。
而是:
生态慢慢养成了一种把细粒度拆分本身当作价值信号的习惯。
这就很危险了。
因为“能拆”是一种能力。
“该拆到什么程度”则是另一种判断。
而 npm 世界最早恰恰特别擅长前者,却没有来得及建立后一者的共同节制。
07|Isaac 自己后来的回看其实已经很说明问题:npm 未必真能阻止重复造轮子,它只是让造轮子和复用轮子都变得更高效
这点特别值得记住。
因为它能帮助我们避免把历史讲成简单的是非题。
Isaac Z. Schlueter 后来在采访里讲得很坦白:
他不能问心无愧地说 npm 真正阻止了重复造轮子,
因为 npm 上本来就有很多很多轮子。
他的希望是:
让重新造轮子这件事更容易、更高效,也让你别老是重复做同样已经做过的脏活。
这段话很重要。
因为它说明 npm 从一开始追求的,
未必是“让世界只剩一个最标准的实现”。
它更像是在说:
让模块流通得更快,让尝试不同实现也足够便宜。
这套哲学当然非常开放。
可它的副作用也很明显:
同一类问题会长出很多近似模块。
包的总量会非常夸张。
开发者会越来越习惯依赖别人写的小块逻辑。
而一旦这种习惯形成,
整个生态的依赖密度就会越来越高。
换句话说,
npm 小包文化并不是简单的“大家都不想自己写”。
它更像一种特别开放、特别高流速的生态后果:
写一个小包很便宜,用一个小包也很便宜,于是整个世界自然会越长越碎。
08|为什么小包文化不是歪风,而是 npm 机制最顺手的结果
把前面几层叠一起看,第二篇最重要的判断可以压成一句:
小包文化不是偶然长歪的,它一开始就是 npm 把复用成本压到极低之后最自然的文化结果。
这句话里有几层意思。
第一,小包文化不是先由某个极端案例带起来的。
它是中央仓库、package.json、publish / install 路径和版本机制共同作用的长期结果。
第二,它最早并不是负面文化。
它看起来同时符合工程美学和开放生态美学,所以特别容易被社区主动拥抱。
第三,真正的问题不是“模块化”本身。
而是生态后来慢慢把“复用很便宜”误滑成了“拆得更细总是更好”。
第四,这种文化一旦形成,
深依赖、脆弱性扩散和治理压力其实就已经在后面排队了。
所以理解 npm 的第二步,不该只记住:
“后来出现了很多一行代码的包。”
更该记住的是:
那些极端案例之所以可能出现,是因为整个生态在更早的时候,就已经被训练成把细粒度复用理解成一种几乎天然正确的方向。
09|小包文化是 npm 把复用做便宜之后的自然结果
npm 江湖 的第二篇,最值得记住的,不是“JavaScript 社区喜欢小题大做”这种轻浮判断。
更值得记住的是:
当 npm 把共享、安装、声明依赖和继续组合这几件事做得足够便宜之后,JavaScript 世界就自然长出了一种特别激进的复用文化,而小包文化正是这种文化最顺手、也最危险的表现。
也正因此,
后来大家看到 left-pad 那种案例时,
才会突然意识到一个此前没那么多人认真面对的事实:
原来一个看起来微不足道的小模块,真的可能挂在成千上万项目的关键路径上。
这也就是第三篇要接上的地方。
因为第三篇真正要讲的,不只是一次事故。
而是:
为什么整个行业要等到 left-pad 出事,才第一次被迫承认“小包复用的美学”背后,其实一直埋着一整套此前被低估的脆弱性。
编者注(事实核对):文中关于 npm 如何降低模块共享与安装门槛的描述,主要依据 npm Docs 中关于 package.json、dependencies / devDependencies、npm install 与 npm publish 的说明,其中明确写到发布包最重要的字段是 name 和 version,依赖关系可直接写入 package.json,而 npm install 会按其中的版本范围自动下载并安装依赖。关于 npm 的规模和“几乎什么都有”的使用直觉,主要依据 Node.js 官方文档 An introduction to the npm package manager,其中明确称 npm 是 Node 的 standard package manager,并提到 registry 已拥有数百万包,是 “the biggest single language code repository on Earth”。关于 Isaac Z. Schlueter 对 npm 生态中“重复造轮子”问题的回看,主要依据 Increment 采访中“npm really prevents ever reinventing the wheel”那段回答,他明确承认 npm 上仍有大量重叠模块,但强调希望让重新造轮子与复用已有轮子都变得更高效。正文将这些线索综合概括为“小包文化是 npm 把复用成本压得极低之后最自然的文化结果”,属于对工具摩擦、生态激励和社区审美共同作用的综合判断。
关键人物速览
- Isaac Z. Schlueter:
npm的发起者。理解为什么 npm 生态会把“模块流通”做得如此低摩擦,绕不开他。 - Laurie Voss:npm 早期核心推动者之一。理解 npm 如何把 registry 文化和社区叙事一起推大,绕不开他。
- Sindre Sorhus:小而专注模块文化最有代表性的实践者之一。理解“小包并不天然等于坏设计”这条辩护线在社区里为什么长期成立,绕不开他。
参考与延伸阅读
An introduction to the npm package manager | Node.js Docs
https://nodejs.org/learn/getting-started/an-introduction-to-the-npm-package-managernpm | npm Docs
https://docs.npmjs.com/cli/v8/commands/npmpackage.json | npm Docs
https://docs.npmjs.com/cli/v11/configuring-npm/package-json/Specifying dependencies and devDependencies in a package.json file | npm Docs
https://docs.npmjs.com/specifying-dependencies-and-devdependencies-in-a-package-json-file/Packages and modules | npm Docs
https://docs.npmjs.com/packages-and-modules/Interview with Isaac Z. Schlueter, CEO of npm - Increment
https://increment.com/development/interview-with-isaac-z-schlueter-ceo-of-npm/npm About
https://www.npmjs.com/aboutAbout npm | npm Docs
https://docs.npmjs.com/about-npmWhat are your thoughts on the left-pad npm case?
https://github.com/sindresorhus/ama/issues/367
下篇预告:left-pad 为什么会成为行业惊魂夜,因为它第一次逼整个行业承认,一个看起来几乎微不足道的小包,真的可能挂在整个生态的关键路径上。