开源项目作死的种种方式
摘要
文章列举了开源项目消亡的多种方式,包括维护者弃坑、企业忽视、资金断崖和官僚僵局,揭示了开源可持续性中的系统性问题。
暂无内容
查看缓存全文
缓存时间: 2026/05/19 22:05
# 开源项目离世的N种愚蠢方式
来源:https://nesbitt.io/2026/05/19/dumb-ways-for-an-open-source-project-to-die.html
《伯尼的周末》(https://nesbitt.io/2026/05/08/weekend-at-bernies.html)已经表明,大量被依赖最多的开源包已经死亡,而一个项目走到这一步的方式五花八门。
## 维护者离开了
**幽灵维护者。** 最简单也最常见的情况:最后的人类提交在几年前,issue 积压无人回复,仓库没有存档,所以不会被任何标记它的过滤器发现。通常维护者只是转向了其他事情,这个项目对他们来说不值得正式交接或关闭,不过这种沉默掩盖了所有情况,甚至包括维护者已经去世——这既无法在注册表也无法在仓库中表示。从外部看,它和一次长假没什么区别,直到足够多的未回复 issue 堆积起来,让沉默变得明确。npm 工具在《伯尼》的死亡清单(https://nesbitt.io/2026/05/08/weekend-at-bernies.html)中占据榜首,大多属于此类。
**企业孤儿。** 某家公司组建团队构建并开源了它,然后一次业务转向或裁员浪潮带走了团队,没人更新 README。GitHub 组织仍然挂着公司 logo,最后拥有管理员权限的人也离开了,因此公司里经常没人知道这个项目是他们的。Google 的各种墓地是著名的案例,但每家达到一定规模的公司都有几个这样的项目,而那些基础设施性质而非产品性质的项目往往连一个弃用通知都得不到。
**论文孤儿。** 由一名研究生为硕士项目或博士章节构建,之后他们毕业并离开了。托管它的实验室名义上拥有仓库,但那里没有人有上下文继续维护它,学术界也没有理由让他们尝试:维护别人的软件赚不到引用,在评审中与发表新东西相比一文不值。研究软件中充斥着这种案例,往往代码已经无法构建了,相关的论文还在被引用多年。
**资金悬崖。** 项目依靠一笔赠款或固定期限的赞助运行,通常来自基金会或某个公共软件基金,结果资金按时用完了。维护者回去做能付房租的工作了,而一个已经成长到需要全职投入的项目,现在只能靠夜晚和周末,对于那种规模来说几乎等于零。基金会的 logo 通常会在资金停止后很久还留在 README 里,让人容易误以为它还是一个健康的赞助项目。
**被挖走。** 维护者被某家公司雇佣,要么是雇佣合同,要么是新的工作负担导致项目停止。偶尔这是竞争对手在清除麻烦,但更常见的情况完全不是恶意的:苹果就是典型的雇主,根本不允许大多数员工参与外部开源,所以维护者入职意味着他们的项目默认就安静了。在入职前交接是显而易见的解决办法,但几乎没人及时做到。
**继承僵局。** 原维护者联系不上,有人愿意接手,但注册表上的发布权限绑在别人无法访问的账号上,GitHub 仓库没有其他管理员,而注册表的废弃包处理流程要么需要原维护者同意,要么需要一场没人有明确立场启动的漫长争议。PEP 541(https://peps.python.org/pep-0541/)流程和 npm 的争议政策都是为这种情况设计的,但两者所需时间通常都比分叉并重命名更长。
## 维护者还在
**倦怠高原。** 用你想到的任何指标衡量,它都还是活跃的。拼写错误修复和依赖版本更新被合并,偶尔还有 issue 上的“谢谢,会看这个”,但任何需要实际设计决策或调试会话的事情,都会无限期地悬而未决,因为这些事情需要维护者很久以来对这个项目已经不存在的精力。往往正好有足够的回应,以至于建议分叉的人会被指向最近的动态,但又不足以真正发布,这种情况可以保持数年,而不至于彻底死亡到让人觉得应该接手。
**善意僵尸。** 贡献图一片绿色,但每次提交都是机器人。Dependabot 版本更新、自动合并规则、可能是由更新触发的自动发布,以及现在还有定时运行的编码代理,可以在没有任何人阅读的情况下无限期地维持运行。所有基于活跃度的健康评分都会认为它没问题,这基本就是基于活跃度的健康评分的全部问题(https://nesbitt.io/2026/05/09/the-mismeasure-of-open-source.html)。
**监护权争夺。** 两个或多个共同维护者闹翻了,每人都有足够的权限阻止对方,但又不足以单独推进,项目就在他们之间冻结了。可能会解决为一次分叉,或以一方退出告终,但很多就这样放着,issue 追踪器里用户问发生了什么,得到两个互相矛盾的回答。
**部落知识消失。** 代码能跑,测试通过,但理解为什么的人已经离开了,剩下的人没有信心触碰任何承重部分。项目实际上变成只读:边缘的小补丁还行,任何结构性的改动都太冒险了。尤其在数值计算和解析代码中常见,最困难的部分是多年前一个人根据一篇论文实现的算法,而且从未写下来。
**有毒的门禁。** 维护者就在那里,而且充满敌意。新贡献者经历一次令人难受的审查后就不回来了,巴士因子始终为 1,因为没人能忍受分享这个仓库。它在所有统计提交和关闭 issue 的指标上看起来都很健康,当这一个人最终停止时,就会变成没有继任者的幽灵维护者案例,因为每个可能接手的人多年前就被赶走了。
## 破坏与控制
**被控制的维护者。** 提交或发布权限最终落入了敌对的人手中。xz 是复杂版本,为期两年的社会工程攻击,针对一个超负荷的独行维护者,以添加一个共同维护者,然后发布了一个带后门的版本。2018 年的 event-stream 是简单版本,原作者把包交给了一个主动请缨的志愿者,然后志愿者在某个下游依赖中添加了一个钱包窃取器。在这两种情况下,项目在被控制期间看起来比之前更健康,因为新维护者才是真正在干活的人。
**抗议软件。** 合法的维护者故意破坏自己的包。colors 和 faker 在 2022 年被作者破坏,node-ipc 同年针对俄罗斯和白俄罗斯 IP 范围投送了恶意负载,left-pad 在 2016 年因与 npm 的争议被完全取消发布。动机各不相同,但对下游的影响是一样的:注册表中的代码不再是你以为你正在运行的东西,通常没有警告。
## 发布管道坏了
**维护但不发布。** 开发在进行,修复进入了 git,但没人能发布版本。拥有发布权限的唯一账号消失了、丢失了双重验证设备、或属于一个已经不复存在的公司。下游被卡在最后一个发布的版本上,而他们需要的修复就在仓库里的一个提交中,却无法从注册表安装——这正是《伯尼》原文花最多篇幅讨论的情况。
**不可发布的主分支。** 默认分支已经偏离上一个标签太远,发布它会给所有人带来破坏性变更,没人愿意承担这个责任,所以没人打标签。新贡献者向主分支提交补丁,而用户运行着几年前的版本,差距越来越大,直到发布一个版本本身成为一个项目,但永远没有人手去做。
**构建考古学。** 发布的制品能正常工作,但没人能复现它们。构建依赖的 CI 服务已消失,或者基础镜像已被删除,或者某个维护者曾经拥有的笔记本电脑上的某个工具版本已不再拥有。要制作一个新版本,首先需要重建构建环境,而关于其中包含什么的知识已经随设置它的人一起离开了。
**影子维护。** 真正的开发发生在公司的私有单体仓库中,公开仓库定期接收一次压扁的代码转储,提交信息大概是“同步”。针对公开仓库的 issue 和 PR 无人问津,因为那里不是任何人工作的地方。开源项目已经变成了一个封闭项目的发布渠道,从外部看它和幽灵维护者没有区别,除了在同步落地的那几天。
**搁浅的大版本。** 项目是 v4,正在积极维护,但大多数生态系统还在 v1,因为 v2 是一次没人迁移过去的重写,v1 已经多年没有维护者关注了。“这个项目”是否死亡完全取决于你问的是哪个大版本,而安装量最大的版本通常不是那个正在被关注的版本。
**注册表孤儿。** 包可以从注册表解析,但它的元数据中的源码仓库 URL 返回 404:被删除、被设为私有、没有更新注册表就搬走了、或者它所依赖的托管服务关闭了。没有地方可以提交 issue 或分叉,也无法验证 tarball 是否与源码控制中的任何内容匹配。大约 1.7% 的 npm 包和 4% 的 Packagist 包指向一个不存在的仓库(https://nesbitt.io/2026/05/11/proxy.html),其中相当一部分仍在被安装。
## 不可抗力
**制裁搁浅。** 维护者有能力也有意愿,但无法推送,因为注册表已封锁他们的管辖区域,或者他们的账号因出口管制被冻结。过去几年里,有少量 npm 和 GitHub 账号因此被暂停,在下游看来,这和幽灵维护者完全一样,只不过维护者经常在另一个平台上大声解释情况。
**下架受害者。** 在 DMCA 索赔或商标争议后从注册表或托管商处被移除。youtube-dl 在 2020 年被下架后又回来了;很多较小的项目没有回来,而且索赔是否有效与包是否仍可解析无关。
## 世界变了
**平台搁浅。** 绑定在一个生命周期结束的运行时上:仅支持 Python 2、需要已经从 CI 镜像中移除的 Node 版本、依赖一个已被删除的编译器扩展。将其移植到新平台需要比任何人愿意做的更多的工作,所以它留在原地,而它需要的平台逐渐从任何一个你想运行它的地方消失。
**传递性死亡。** 项目本身很好,维护者也在并且愿意,但它的依赖树中两三层深处的东西,已经通过这个列表中的某条路径死亡了,并且无法在不重写的情况下替换。项目继承了死亡,而它自己的仓库中没有任何变化——这就是递归情况:这里的每一条路径也是一条杀死依赖于你的东西的方式。
**API 突然撤销。** 项目包装了某外部东西,而它的所有者收回了它。在服务层,这是某个 API 的客户端库,该 API 被关闭或重新定价到无法触及的地步,Twitter 的 2023 年变更和随后的 Reddit 的变更一次性杀死了整整一代这样的库。在平台层,这是浏览器放弃某个接口,或操作系统锁定某个功能——这涵盖了所有建立在 NPAPI、Flash 或 Chrome 应用之上的东西。无论哪种情况,维护者都无法从自己这边做任何事情。
**被取代。** 项目的功能不再需要了,要么是因为它实现的规范已被替代,要么是因为语言现在原生支持相同功能。`Object.assign`之后的`object-assign`、ES2015 之后的各种 lodash 单函数包、各种 promise 和`fetch` polyfill,以及在协议层面,无数针对已经没人再产生的格式的库。维护者合理退出,而几十万个 lockfile 继续安装它,因为删除一个仍然能解析的依赖不是任何人的优先事项。
## 项目分裂了
**分叉的 limbo。** 一次分歧或一位维护者离开导致了项目在两个或更多分叉之间分裂,没有哪个明显胜出。下游冻结在分裂前的最后一个版本,而不是押注可能会输的分叉,所以原项目保持其安装量,而所有开发工作都在别的名字下进行。io.js 和 Node 最终合并回到了,libav 最终并回了 FFmpeg,很多较小的分裂从未解决。
**许可证突然撤销的后果。** 项目重新授权为不是开源的东西,而旧许可证下的社区分叉存在,但采用率没有集中到它上面。Terraform/OpenTofu 和 Redis/Valkey 都在这条路上的某处,Elasticsearch 则在这条路上更远一点。大多数 lockfile 仍然指向原项目最后开源的版本,现在它是一个无人维护的固定点。
**开放核心空心化。** 有趣的开发转移到了商业版本,开源仓库作为免费层保留。它仍然有发布,主要是版本更新和那些不区分付费产品的东西,人们最初采用的项目实际上已经变成了一个不同的、更小的项目,从未改名。
---
这篇博客文章借名的墨尔本地铁安全宣传(https://en.wikipedia.org/wiki/Dumb_Ways_to_Die)以“在火车附近注意安全”结尾,这比我能给出的任何建议都更可操作。无论上述哪一种情况适用,包仍然可以解析,你的 lockfile 会继续戴着墨镜带它参加派对,只要没人仔细检查。
相似文章
开源坚守:在公司时间保持OSS活力
本文讨论了通过鼓励员工在上班时间为开源项目做贡献来维持开源软件的重要性,强调了公司支持开源项目的必要性。
维护者的困境
一篇探讨开源维护者所面临挑战的博客文章,包括拉取请求积压、AI工具对代码审查的影响,以及在质量与倦怠之间取得平衡的困境。
开源中的倦怠:一个我们可以共同解决的结构性问题
对开源软件开发者倦怠现象的详细分析,基于一位心理学家的访谈和研究,识别结构性原因及潜在解决方案。
AI正在摧毁开源,而它甚至还不够优秀
本文讨论了AI生成的代码和代理AI如何以低质量的拉取请求和错误报告淹没开源维护者,导致像curl这样的项目取消漏洞赏金,并导致维护者受到骚扰。
GitHub 正在沉沦
文章认为,自被微软收购以来,GitHub 的可靠性与文化已大幅衰退,以正常运行时间问题和内容泛滥("slop")为由,指出开发者正转向其他替代方案。