开源项目 Roadmap 与 RFC:让重大变化可讨论

0阅读13分钟

重大变化需要讨论入口

维护一个有两三千 star 的开源项目,最让人头疼的不是修 bug,而是「方向之争」。有人在 issue 里说要加 GraphQL 支持,有人提 PR 要把整个状态管理重写,还有人直接发了一篇博客说你们的 API 设计有问题。每条都有道理,但你不能全做。

我见过两种极端:一种是维护者闷头写代码,半年后发一个大版本,社区炸了——因为 breaking change 没人知道、没人讨论、没人准备好迁移。另一种是每件事都发到社区投票,结果一个路由方案的讨论拖了八个月,产出几百条评论,最后什么也没定下来。

Roadmap 和 RFC 就是用来解决这个问题的。它们不是流程官僚,而是给「重大变化」一个可讨论的入口——让方向可见、让方案可比、让决策可追溯。这篇文章讲清楚它们怎么配合使用,以及我在几个项目里踩过的坑。

理论基础:为什么需要结构化的方向管理

RFC(Request for Comments)这个概念来自互联网早期的 IETF 标准流程。它的核心思路很简单:重大决策在实施之前,先写成文档让所有相关方审阅和评论。 Rust 项目把这个模式引入了开源软件治理,后来 React、Vue、Ember、Yarn、npm、ESLint、Gatsby 等项目纷纷采用,形成了今天开源社区广泛使用的 RFC 驱动开发模式。

Fuchsia 项目的 RFC 最佳实践文档把 RFC 定义为「设计文档,用于记录项目决策」。RFC 被接受后成为历史记录,证明所有利益相关者同意了设计方案。这个定义很关键——RFC 的核心价值不是审批流程,而是决策的可追溯性

CNCF 在贡献者增长指南中明确指出,公开 Roadmap 能带来三重收益:建立信任(通过透明化开发计划)、降低重复(贡献者不会提已经在计划中的功能)、匹配技能(贡献者可以发现「哪些工作正好适合自己的专长」)。

但 RFC 流程也不是没有问题。Rust 核心贡献者 Nick Cameron 在《We need to talk about RFCs》一文中指出了几个结构性瓶颈:

  • 写作成本高:写一份完整的 RFC 很费时间,导致提案者倾向于缩小范围,把一个完整特性拆成多个 RFC,反而增加了跟踪难度
  • 评审瓶颈:关键利益相关者往往拖到最终投票阶段才给反馈,前面几周的讨论都在原地打转
  • 缺乏反向压力:没有机制控制涌入的提案数量,也没有明确的流程在 RFC 通过后追踪实施状态
  • 归档困难:由于社区文化倾向于「保持开放」,大量已经过时的 RFC 和讨论线程永远不会被关闭

这些问题不是要否定 RFC,而是说:流程设计必须匹配项目的规模和阶段。 一个十人团队用 Rust 那套重型 RFC 流程会把自己拖死,一个万人社区不用任何结构化流程也会乱套。

案例一:没有 Roadmap 导致的重复劳动

场景

一个中等规模的 UI 组件库,月活贡献者大约 20 人。维护者 A 心里有一个 v3 的规划:要迁移到 CSS-in-JS、要支持 SSR、要重写文档站。但这些想法只存在于 A 的脑子里和几次私下聊天中。

翻车

贡献者 B 花了三周写了一个 Storybook 迁移的 PR。贡献者 C 同时花了两周做了一个 Docusaurus 文档站。贡献者 D 则提了一个完整的主题系统 RFC。三个人都做了有价值的工作,但方向互相冲突——B 的方案假设用 vanilla-extract,C 的方案依赖 Tailwind,D 的方案需要一个还没决定的设计 token 系统。

维护者 A 不得不拒绝其中两个 PR,解释了自己心里的规划。B 和 C 很沮丧:「你要是早说,我就不会浪费时间了。」D 更直接:「你们连 Roadmap 都没有,我怎么知道该往哪个方向贡献?」

修复

项目在 GitHub 仓库根目录创建了 ROADMAP.md,并配合 GitHub Projects 看板管理状态:

# Roadmap
 
## 当前重点(v3.0,目标 2026 Q3)
 
- [x] 设计 token 系统(RFC #42,已合并)
- [ ] CSS-in-JS 迁移(RFC #45,讨论中)
- [ ] SSR 支持(RFC #47,草案阶段)
- [ ] 文档站重建(依赖 CSS-in-JS 完成)
 
## 暂时不做
 
以下是社区多次提到但当前不在计划中的方向,附上原因:
 
- **GraphQL API**:组件库不涉及数据层,建议通过 adapter 模式集成
- **Vue 版本**:团队没有 Vue 经验,欢迎社区 fork 或独立维护
- **IE 11 支持**:已在 v2.5 正式放弃,不再接受相关 PR

关键改动不只是写了一个文件,而是明确了「不做什么」。这比「做什么」更能减少无效贡献。

对比:好 Roadmap 与坏 Roadmap

维度坏做法好做法
可见性放在 Notion 私人页面或维护者脑子里放在仓库根目录 ROADMAP.md,README 直接链接
粒度「提升性能」「改善体验」具体到 RFC 编号、状态、依赖关系
更新频率写了就忘,半年不更新每月至少更新一次状态,标注日期
否定项不写,靠维护者在 issue 里临时解释单独列出「暂不做」及原因
时间承诺给出具体日期(经常跳票)给出季度范围或里程碑依赖

案例二:RFC 太重拖垮了决策速度

场景

一个内部工具库(约 500 star),团队 5 人。有人提出要把 CLI 的参数解析从手写换成 commander.js。这个改动影响面不大——改一下入口文件、调整几个测试就行。但项目有 RFC 流程,于是贡献者写了一份 12 页的 RFC,包含背景、动机、替代方案分析、迁移计划、风险评估……

翻车

RFC 发出后,团队都忙着手头的工作,没人有空读 12 页文档。两周后有人评论了一句「为什么不用 yargs?」,引发了关于 commander vs yargs vs minimist 的对比讨论。又过了三周,讨论偏离到了「我们是不是应该统一所有依赖的版本策略」。两个月过去了,RFC 还是 Open 状态,而 CLI 的参数解析问题(某些边界情况会崩溃)一直没有修。

这就是 Nick Cameron 说的问题:流程太重,导致小改动也要走大流程,结果什么都做不了。

修复

项目引入了变更分级制度——不是所有改动都需要 RFC:

## 变更分级规则
 
### Level 0:直接 PR(无需 RFC)
- Bug 修复(不改变公开 API)
- 文档更新
- 依赖升级(patch/minor 版本)
- 内部重构(不改变行为)
 
### Level 1:轻量 RFC(1-2 页,3 天评审期)
- 新增公开 API(函数、组件、配置项)
- 小范围 breaking change(影响 < 5% 用户)
- 新增 CLI 命令或子命令
 
### Level 2:完整 RFC(完整模板,7 天评审期)
- 重大 breaking change
- 架构变更(模块系统、插件机制、构建流程)
- 弃用核心 API
- 新 major 版本的整体规划

回到 commander.js 的例子,这属于 Level 1——新增一个依赖、改变内部实现,但公开 API 不变。写一份 1-2 页的 RFC 说清楚为什么换、怎么迁移、有没有风险,三天内团队审阅完毕,一周后合并。

对比:RFC 分级制度

变更级别典型场景文档要求评审周期决策方式
Level 0Bug 修复、文档、依赖升级PR 描述即可随时,1-2 人审阅维护者直接合并
Level 1新增 API、小 breaking change1-2 页 RFC,含动机和迁移方案3 天核心团队多数同意
Level 2架构变更、重大 breaking change完整 RFC 模板7-14 天核心团队全票 + 社区公示

对比:好 RFC 与坏 RFC 的写法

坏做法:RFC 写成了论文

# RFC-0023: CLI 参数解析库迁移
 
## 1. 摘要
本文档提出将 CLI 参数解析从手写实现迁移至 commander.js……
 
## 2. 历史背景
自项目创立以来,CLI 一直使用手写的参数解析……(3 页历史回顾)
 
## 3. 现有实现分析
当前 parseArgs 函数共 342 行,分为 7 个子函数……(逐行分析)
 
## 4. 候选方案对比
### 4.1 commander.js
优点:……缺点:……(每个方案 2 页)
### 4.2 yargs
### 4.3 minimist
### 4.4 自研 v2
 
## 5. 推荐方案
……
 
## 6. 迁移计划
……
 
## 7. 风险评估
……
 
## 8. 时间线
……
 
## 9. 未解决问题
……

这份 RFC 的问题不是信息不够,而是信息太多。评审者需要花 30 分钟读完才能投票,大部分人会选择「稍后再看」——然后永远不看。

好做法:RFC 聚焦决策

# RFC-0023: CLI 参数解析迁移到 commander.js
 
## 问题
当前手写的参数解析在遇到 `--flag=value` 和短选项组合(`-abc`)时会崩溃。
已有 3 个相关 issue(#112, #128, #145)。
 
## 方案
用 commander.js 替换 `src/cli/parse-args.ts`,保持公开 API 不变。
 
## 为什么选 commander
- 体积小(~45KB,yargs 是 ~320KB)
- API 简单,学习成本低
- 活跃维护,周下载量 4000 万+
 
## 迁移影响
- 用户无感知(CLI 接口不变)
- 测试需要更新 3 个 snapshot
- 预计工作量:1-2 天
 
## 不做什么
不引入子命令系统,不改变现有参数格式。

两份 RFC 讨论的是同一件事。第二份只用了不到 30 行,但包含了决策需要的所有信息:问题是什么、方案是什么、为什么选它、影响是什么、不做什么。评审者 5 分钟就能读完并给出有意义的反馈。

案例三:RFC 通过了但没有追踪实施

场景

一个数据处理框架,社区 200+ 贡献者。RFC #15 提出了「插件式管道架构」,经过两个月讨论后合并。所有人都觉得问题解决了。

翻车

六个月后,有人问:「RFC #15 什么时候实施?」维护者回复:「RFC 通过了不代表有人去做。」又过了三个月,两个不同的团队各自实现了插件系统——API 完全不同。RFC 成了一纸空文。

这是 Nick Cameron 指出的另一个结构性问题:RFC 通过后缺乏「显式的更新流程」。RFC 被接受只代表「方向同意了」,不代表「有人实施了」。

修复

项目在 RFC 模板中增加了「实施负责人」和「状态追踪」两个必填字段,并将 RFC 状态和实施状态分开管理:

## RFC 模板新增字段
 
### 实施计划
- 负责人:@contributor-name(必须在 RFC 通过前确认)
- 预计开始时间:2026 Q3
- 预计完成里程碑:
  - M1:核心接口设计(RFC 通过后 2 周)
  - M2:最小可用实现(M1 后 4 周)
  - M3:文档和迁移指南(M2 后 2 周)
 
### 状态追踪
RFC 状态:✅ 已通过(2026-06-15)
实施状态:🔧 进行中(M1 完成,M2 开发中)
实施 PR:#328

同时在 ROADMAP.md 中用表格追踪所有已通过 RFC 的实施进度:

## 已通过 RFC 实施追踪
 
| RFC | 标题 | 通过日期 | 负责人 | 实施状态 | 备注 |
|-----|------|---------|--------|---------|------|
| #12 | 异步中间件 | 2026-03 | @alice | ✅ 已完成 | v2.4 发布 |
| #15 | 插件管道 | 2026-06 | @bob | 🔧 进行中 | M2 开发中 |
| #18 | WASM 支持 | 2026-05 | - | ⏳ 未开始 | 等待负责人 |

关键区别在于:RFC 状态(方向是否同意)和实施状态(是否做完)是两件事。 把这两个状态混在一起,就会出现「RFC 通过了但没人做」的真空地带。

从提案到落地的完整流程

下面的流程图展示了一个重大变更从提出到落地的完整路径。Level 0 变更跳过 RFC 环节,直接走 PR 审阅。

流程图画布 · 115%
Mermaid 流程图加载中...

决策沉淀:RFC 通过或拒绝都要记录原因

RFC 通过后合并进仓库,这是大多数项目都能做到的。但 RFC 被拒绝或撤回后,很多项目不记录原因——要么直接关闭 PR,要么在评论里留下一句「暂不采纳」。

这会导致一个问题:半年后有人提出同样的方案,重新讨论一遍,再被拒绝一遍。讨论历史散落在关闭的 PR 里,新人根本找不到。

好做法是为每个关闭的 RFC 添加一段「决策记录」:

## 决策记录
 
### RFC-019: 支持 Web Components 输出
 
**状态:❌ 拒绝(2026-05-20)**
 
**原因:**
1. 当前渲染管线(基于虚拟 DOM diff)与 Web Components 的 Shadow DOM 模型不兼容,
   强行适配需要重写渲染层,预计工作量超过 3 个月
2. 社区调查显示(RFC 评论 #34-#89),仅有 12% 用户需要此功能,
   而 React 输出适配器有 67% 使用率
3. 团队当前优先级是性能优化(RFC #17),资源冲突
 
**替代方案:**
- 建议通过 `@framework/adapter-webcomponent` 社区包实现
- 项目提供 adapter 接口文档,欢迎社区贡献
 
**未来条件:**
如果社区 adapter 包达到 500+ 周下载量,或出现商业用户需求,
可以在 v4 规划中重新评估。

这段记录的价值在于:未来有人再提同样的想法时,不需要重新辩论,直接看决策记录和条件。如果条件满足了,重新启动;如果条件没变,引用这条记录就行。

不同规模项目的策略选择

项目规模贡献者数Roadmap 建议RFC 建议工具选择
小型(< 500 star)1-5 人GitHub Issues milestone 即可不需要正式 RFC,用 Issue 模板 + 标签分级GitHub Issues + Projects
中型(500-5000 star)5-30 人ROADMAP.md + GitHub ProjectsLevel 1/2 需要 RFC,存放在 rfcs/ 目录GitHub Issues + Projects + /rfcs 目录
大型(5000+ star)30+ 人独立 Roadmap 网站 + 季度更新完整 RFC 流程,独立 RFC 仓库独立 RFC 仓库 + 社区论坛 + Discord/Slack 频道
超大型(50000+ star)100+ 人Roadmap + 子项目各自路线图分级 RFC + 子团队自治 + 跨团队协调机制多仓库 + RFC Bot + 定期社区会议

常见反模式与对策

反模式症状根因对策
流程过重小改动也要写 RFC,决策周期以月计没有分级制度,所有变更一刀切引入 Level 0/1/2 分级,Level 0 直接 PR
流程过轻重大变更直接在 PR 里讨论,评论上千条没有 RFC 意识,或者觉得「写文档浪费时间」在 CONTRIBUTING.md 里明确哪些变更必须先写 RFC
RFC 变辩论场讨论偏离主题,变成个人偏好之争缺少主持人和讨论规范维护者在 RFC 开头定义「讨论范围」和「决策标准」,及时锁定无关评论
僵尸 RFCRFC 通过后无人实施,状态永远是「已通过」RFC 状态和实施状态混为一谈分开追踪,RFC 通过后必须指定负责人和时间线
幽灵 RoadmapRoadmap 写了就忘,半年不更新没人负责维护每月固定时间回顾,标注更新日期,过期条目主动标注
黑箱决策RFC 讨论了但最后维护者私下决定,RFC 形同虚设流程缺乏约束力在 CONTRIBUTING.md 中写明:Level 2 变更必须通过 RFC 才能合并

Roadmap 与 RFC 协作检查清单

规划阶段

  1. Roadmap 是否在仓库根目录可见? 不在 README 里放链接的话,很多人找不到。
  2. Roadmap 是否包含「不做什么」? 明确否定比沉默更能减少无效贡献。
  3. 变更分级规则是否写在 CONTRIBUTING.md 里? 贡献者应该在提 PR 之前就知道要不要先写 RFC。
  4. RFC 模板是否存放在仓库里? 放在 rfcs/TEMPLATE.md.github/rfc-template.md,让贡献者 fork 后直接填写。

RFC 撰写阶段

  1. RFC 是否包含「问题」「方案」「影响」「不做什么」四个核心部分? 缺任何一个都会让讨论失焦。
  2. RFC 是否控制了篇幅? Level 1 RFC 不超过 2 页,Level 2 不超过 8 页。超过说明范围太大,需要拆分。
  3. 是否指定了实施负责人? RFC 通过前至少要有一个人表态愿意做,否则 RFC 通过后很容易变成僵尸。
  4. 评审周期是否明确? 在 RFC 开头写明「评审截止:YYYY-MM-DD」,避免无限期讨论。

决策与追踪阶段

  1. RFC 无论通过还是拒绝,是否都记录了决策原因? 关闭 PR 时附一段「决策记录」,未来的贡献者会感谢你。
  2. 实施状态是否在 ROADMAP 中单独追踪? RFC 状态 ≠ 实施状态,两者要分开管理。
  3. Roadmap 是否每月至少更新一次? 过期条目要标注原因,而不是悄悄删掉。
  4. 是否有定期回顾机制? 每月或每季度回顾未完成的 RFC 和 Roadmap 条目,决定继续、调整还是关闭。

工具选择:不是越复杂越好

需求小团队推荐大社区推荐说明
Roadmap 托管ROADMAP.md 在仓库里独立网站(如 roadmap.project.org小团队不需要额外基础设施
RFC 存储仓库内 rfcs/ 目录独立 RFC 仓库(如 project/rfcs独立仓库可以有自己的 issue 追踪和 CI
状态追踪GitHub Projects 看板GitHub Projects + 自定义标签体系看板视图比列表更直观
讨论平台GitHub PR 评论GitHub + Discord/论坛专属频道长讨论在 Discord 里更高效,但结论必须回到 RFC
自动化GitHub Actions 自动检查 RFC 格式RFC Bot(自动分配审阅者、提醒截止日期)自动化能显著降低维护者的管理负担

很多项目一开始就上了独立 RFC 仓库、Discord 频道、自动化 Bot,结果维护者花在管理工具上的时间比写代码还多。我的建议是:从仓库内 rfcs/ 目录和 GitHub Projects 开始,等项目规模真的需要时再升级。 Gatsby 的 RFC 流程就是先从仓库内目录起步,等贡献量上来后才考虑更复杂的工具。

落地建议:从零开始搭建流程

如果你的项目现在既没有 Roadmap 也没有 RFC,不要试图一步到位搭建完整体系。按这个顺序来:

第一步:写一个 ROADMAP.md 不需要多完美,列出当前季度要做的 3-5 件事,以及 2-3 件明确不做的事。放在仓库根目录,README 里加一行链接。

第二步:在 CONTRIBUTING.md 里加一段变更分级规则。 告诉贡献者:小改动直接提 PR,大改动先开 issue 讨论。不需要马上搞 RFC 模板,先用 issue 模板就能起步。

第三步:当 issue 讨论变得混乱时,引入 RFC。 典型信号是:一个 issue 超过 50 条评论还没结论,或者同一个话题被反复提出。这时候写一个简单的 RFC 模板,存放在 rfcs/TEMPLATE.md

第四步:当 RFC 积累到 10 个以上时,考虑独立仓库。 独立仓库有自己的好处:可以单独配置 CI 检查格式、可以有独立的 issue 追踪、可以吸引只看 RFC 不看主仓库的参与者。

第五步:每月回顾。 花 30 分钟看一遍 ROADMAP 和 RFC 列表,更新状态,关闭过期条目。这个习惯比任何工具都重要。

参考资料

评论 0

0 / 1000