36 万 Star 标着开源,license 却禁止你转载,roadmap.sh 到底在卖什么

发布于 2026年07月13日 23:49 #DevOps#Github 解读 原文链接

36 万 Star 标着开源,license 却禁止你转载,roadmap.sh 到底在卖什么 封面图
  • 仓库与网站数据双向同步,GitHub 是展示窗口和 UGC 入口,数据库是真正数据源
  • 路线图以 React Flow 坐标 JSON 存储,内容文件通过随机 ID 映射,维护成本高
  • AI 功能复用同一数据格式和渲染管线,将内容产能放大,形成 UGC 平台
  • 自定义 license 限制内容转载,仅代码开源,内容 All Rights Reserved,引发社区争议
  • 项目采用持续部署模式,无版本发布,核心维护者单一,bus factor 高

大家好,我是若风。

2017 年 3 月,一个叫 Kamran Ahmed 的巴基斯坦工程师在 GitHub 上传了三张 PNG 图片,分别叫 Frontend、Backend、DevOps Roadmap。图片很大,画的是他从自己求职过程中总结出来的技术栈路径,每一格还标了「click to read more」,虽然点了什么也不会发生。

九年过去,这个仓库改名成了 nilbuild/developer-roadmap,涨到 36 万 Star,是整个 GitHub 上星标第 6 多的项目,仅次于 freeCodeCamp、996.ICU 这种现象级仓库。它对应的网站 roadmap.sh 每月有数百万开发者访问,背后有一整支团队和一套完整的 SaaS 商业模式。

但有意思的不是这些数字。

有意思的是,你把这个仓库 clone 下来,跑 pnpm install,会发现它根本不是一个「开源项目」该有的样子。它的 license 禁止你把内容转载到博客,它的代码里藏着一个从远程 API 往本地写文件的脚本,它最近一次打 release tag 是 2023 年 1 月。这不像一个开源项目,更像一个把代码托管在 GitHub 上的商业产品的「前台展示区」。

我花了一个下午把它的源码翻了一遍,想搞清楚一个问题,36 万 Star 的开源项目,到底是怎么变成一门生意的。

一个仓库,两个方向的数据流

先说最反直觉的一个发现。

你以为 developer-roadmap 是 roadmap.sh 的源码,社区贡献者在 GitHub 上改 markdown,然后部署到网站。这是大多数「内容型开源项目」的标准玩法,freeCodeCamp 就是这么干的。

但实际不是。仓库里有两个脚本,一个叫 sync-content-to-repo.ts,一个叫 sync-repo-to-database.ts,看名字就能猜到,这是两个方向相反的同步。

翻开 scripts/sync-content-to-repo.ts,第一段关键代码是这样的。

export async function roadmapTopics(
  roadmapId: string,
  secret: string,
): Promise<OfficialRoadmapTopicContentDocument[]> {
  const path = `https://roadmap.sh/api/v1-list-official-roadmap-topics/${roadmapId}?secret=${secret}`;
  const response = await fetch(path);
  // ...
  return data;
}

注意那个 URL。它不是读本地文件,是从 roadmap.sh/api 拉数据,然后用 secret 鉴权,拿到结果之后写回本地的 markdown 文件。配套的 GitHub Action .github/workflows/sync-content-to-repo.yml 是手动触发的(workflow_dispatch),输入一个 roadmap slug,跑完脚本,检查 git status,如果有变更就自动提交一个 commit。

这意味着什么?意味着 GitHub 上的 markdown 内容,并不一定是「最新」的,真正的 source of truth 是 roadmap.sh 后端的数据库。GitHub 仓库是数据库的一个只读镜像,定期同步下来给社区看。

那社区贡献者提的 PR 算什么?算另一个方向。sync-repo-to-database.ts 负责把 GitHub 上的文件变更推回数据库,这里有个细节,它会用 markdownToHtmlhtmlToMarkdown 做格式转换,还要校验 allowedOfficialRoadmapTopicResourceType。也就是说,PR 不是直接生效,要先进数据库,过审,再走 sync-content-to-repo 回写到仓库。

所以完整的循环是,社区 PR → 合并进 GitHub 仓库 → sync-repo-to-database 推到数据库 → 人工审核 → sync-content-to-repo 把审核后的内容回写仓库。GitHub 在这个流程里扮演的是「展示窗口 + UGC 入口」,不是唯一的数据源。

这是理解整个项目商业模式的一把钥匙。下面会反复用到。

一张路线图,其实是一堆坐标

再来看内容是怎么存的。

roadmap.sh 最核心的资产是那些可视化的路线图,节点之间有连线,可以点击跳转。你可能会想,这不就是个 markdown 列表加渲染吗。

也不是。打开 src/data/roadmaps/ai-agents/ai-agents.json,你会看到这样的结构。

{
  "nodes": [
    {
      "id": "3GO9CZQSSxM1eRSBz9sWR",
      "type": "horizontal",
      "position": { "x": 448.47, "y": 3322.50 },
      "data": { "label": "horizontal node", "style": { "stroke": "#2B78E4", "strokeWidth": 3.75 } }
    }
  ]
}

这是 React Flow 的数据格式。每个节点有绝对坐标 position.xposition.y,有 widthheight,有连线,有样式。换句话说,路线图不是「文档」,是「画」。

这解释了一件事。为什么 contributor 提交新路线图,官方建议你用他们的在线编辑器 draw.roadmap.sh 来画,而不是直接写 JSON。因为人肉维护一套精确到小数点的坐标系,成本太高。contributing.md 里写得很清楚,「Adding/Removing Nodes and Modifying Node Titles」这一项要求你开 issue,不要直接改 JSON,只有 typo 可以直接改 markdown。

跟坐标 JSON 配套的,是 content/ 目录下的大量 markdown 文件。AI Agents 这一条路线图,content/ 下有 101 个 .md 文件,每个文件对应路线图上的一个节点。文件名是 topic-slug@randomId.md 这种格式,比如 agent-loop@Eih4eybuYB3C2So8K0AT3.md。那个 @ 后面是一串随机 ID,作用是让节点和内容文件之间建立稳定的映射,即便 topic 改名,映射关系也不会断。

文件里的内容很短,大概一两百字,解释这个概念是什么,然后跟一串资源链接。

# Agent Loop

An agent loop is the cycle that lets an AI agent keep working toward a goal.
First, the agent gathers fresh data ...

Visit the following resources to learn more:

- [@article@What is an Agent Loop?](https://huggingface.co/...)
- [@article@Let's Build your Own Agentic Loop](https://www.reddit.com/...)

注意那个 @article@ 前缀。这是 roadmap.sh 自创的链接类型标记,用来区分文章、视频、官方文档。sync-repo-to-database.ts 在回写时会校验这些类型,不符合的白名单会被拒掉。contributing.md 里还有一条硬规则,「No GeeksforGeeks links」,以及「Maximum of 8 links per topic」。这些规则不在代码里强制,但在 PR 审核时执行,背后是内容质量的护城河。

AI 层是怎么贴上去的

把基础架构看明白之后,再看 roadmap.sh 这两年疯狂加的 AI 功能,就很容易理解了。

package.json 里有个依赖叫 @ai-sdk/react,版本是 2.0.0-beta.34,这是 Vercel 的 AI SDK。对应代码在 src/components/AIChat/ 一整个目录,有 AIChat.tsxChatHistory.tsxUploadResumeModal.tsxPersonalizedResponseForm.tsx,还有 src/components/AIQuiz/src/components/AIGuide/

src/api/ai-roadmap.ts 里定义了「AI 生成的路线图」的接口。

export type GetAIRoadmapBySlugResponse = {
  id: string;
  term: string;
  title: string;
  data: string;
  isAuthenticatedUser: boolean;
};

这个接口返回的 data 字段就是一张完整的路线图 JSON,跟前面看到的 ai-agents.json 同构。用户输入一个关键词,比如「我想学 Rust 后端」,后端调用 LLM 生成一张个性化的路线图,存进数据库,给一个 slug,用户可以分享和访问。

这个功能的精妙之处在哪?在于它复用了前面那套基础设施。AI 生成的路线图,格式跟官方路线图完全一样,同一套 React Flow 渲染器,同一套点击跳转逻辑,同一套 content 文件结构。等于说他们把「人工编辑路线图」这件事抽象成了一种数据格式,然后让 AI 也能生产这种格式的数据,瞬间把内容产能放大了几个数量级。

AICourseDocument 接口里还有 viewCount 字段,说明这些 AI 生成的课程是有浏览统计的,热门的会被推荐。这是典型的 UGC 平台玩法,只不过生产者从人换成了 AI。

拼起来看

把前面三个发现拼到一起,整个系统的架构大概长这样。

系统架构图
系统架构图

最上面是社区接入层,GitHub 仓库在这里扮演镜像和 UGC 入口的角色。中间高亮的双向同步层是核心,左边把数据库内容拉下来写进仓库,右边把社区 PR 推回数据库,中间隔着一道人工审核。下面是真正的内容中枢,私有数据库才是 source of truth。最底层是 AI 和交付,AI 引擎生成的路线图复用同一套数据格式,同一套 React Flow 渲染管线。

这套架构的关键不在于技术多复杂,而在于它把「开源协作」和「商业控制」分到了两个方向,用同步层做隔离。往下,内容完全受控;往上,协作完全开放。

license 这一刀

说到这里,要聊一个不太好聊但必须聊的事。

roadmap.sh 官网首页写着,「Open Source,6th most starred project on GitHub」。但你去翻仓库根目录的 license 文件,第一段是这样的。

Everything including text and images in this project are protected by the copyright laws. You are allowed to use this material for personal use but are not allowed to use it for any other purpose including publishing the images, the project files or the content in the images in any form … you must get prior consent from the understated.

翻译一下,所有内容受版权保护,只允许个人使用,不允许以任何形式发布图片或内容,包括但不限于博客、文章、newsletter,除非获得作者事先同意。

这不是任何一个 OSI 认可的开源协议。它甚至不是 Creative Commons 的任何一种。它是一份自定义的、限制性极强的版权声明。

这件事社区早就注意到了。Issue #3332 标题就叫「What about choosing a recognized open source license for the project」,有人很客气地指出,官网自称开源,但 license 一看根本不符合 OSI 对开源的定义。Kamran 的回复大意是,我们考虑过 MIT、CC BY 这些,但内容被搬运太严重了,最后选择了更严格的保护。Issue 关闭了,license 没改。

你可以说这是合理的商业决策。一个团队花了九年时间维护的内容资产,确实不应该被随便搬走。但「开源」这个词在这里的用法是含糊的。严格说,这个项目开源的只是「代码」,内容(路线图、文字、图片)是「All Rights Reserved」。而真正有价值的、让人愿意来访问的,恰恰是内容。

这不是 roadmap.sh 独有的现象。freeCodeCamp 的课程内容用的是 CC BY-SA,可以自由转载,但它的考试认证体系是收费的。Strapi 这种 headless CMS,核心代码 MIT,但企业版功能闭源。区别在于,这些项目对「哪些是开源的、哪些是商业的」分得很清楚,而 roadmap.sh 把整个仓库统一打上「开源」标签,对普通用户来说,容易产生误解。

所以回到标题那个问题,36 万 Star 的开源项目在卖什么。我的理解是,在卖「基于这套内容资产的 AI 个性化服务、认证、流量广告」。开源标签是流量入口,不是产品本身。

一个人的项目

最后说一个工程层面的观察。

看 contributors 列表,Kamran Ahmed(现在的 GitHub ID 是 nilbuild,他自己改了用户名,所以仓库跟着从 kamranahmedse/developer-roadmap 变成了 nilbuild/developer-roadmap)一个人贡献了 3133 次 commit。第二名 arikchakma 是 242 次。中间差了 13 倍。

第三名 dansholds 217 次,后面是一串两位数、个位数的贡献者,大多是修 typo、加链接的社区贡献者。真正在做架构决策、写核心代码的,基本就是 Kamran 一个人加几个核心维护者。

这是典型的「bus factor = 1」项目。如果哪天 Kamran 不做了,这个项目能不能继续运转,是个问号。从代码层面,Astro + React 的技术栈不复杂,社区能接手。但从内容运营层面,9 年积累的路线图知识体系、跟赞助商的关系、跟 AI 模型供应商的对接,这些是个人资产,很难移交。

另外还有一个时间维度的信号。仓库的 Releases 页面,最后一次打 tag 是 2023 年 1 月 5 日,叫「4.0 Roadmap 4.0」。到今天两年半了,没有新的 release。但 commits 一直没停,今天(2026 年 7 月 13 日)就合并了好几个 PR。这说明项目早就放弃了传统的版本发布模式,走的是持续部署,代码合并即上线。deployment.yml 这个 workflow 干的就是自动部署到生产环境。

对一个个人主导的内容型 SaaS 来说,这是合理的,发版号没有意义,因为「产品」是网站本身,不是可分发的软件包。

它给我们的启发

把 developer-roadmap 拆完,我最大的感触不是它的技术多厉害。技术栈是标准的 Astro + React + Tailwind,没有黑科技。

真正值得学的,是它把 GitHub 这个平台用到了极致的一种模式。我把它叫做「GitHub-as-Billboard」,把仓库当广告位。

具体来说有这么几层。

第一层,用开源仓库做 SEO 和品牌。36 万 Star 带来的权重和曝光,任何付费渠道都买不到。用户搜「frontend roadmap」,GitHub 仓库和 roadmap.sh 都在第一页。

第二层,用 GitHub 的协作机制做 UGC 入口。社区提 PR 加内容,过审后进数据库,再回写仓库。贡献者觉得自己在参与开源,实际上在做免费的内容众包。

第三层,用数据库做 source of truth,仓库只是镜像。这样既能享受开源协作的流量红利,又能保留对内容的完全控制权,随时可以拒绝某个 PR,不用担心 fork 出去就失控。

第四层,用 AI 把内容产能指数级放大。人工路线图维持质量标杆,AI 路线图覆盖长尾需求,同一套数据格式,同一套渲染管线。

这套模式的可迁移性很强。任何想做「内容型 SaaS」的团队都可以借鉴。前提是,你得对自己用「开源」这个词有多诚实,有个清晰的判断。Kamran 选择了一个含糊的用法,换来的是增长,代价是社区的信任成本。这个 trade-off 值不值得,每个项目答案不同。

但有一点是确定的,下一个 36 万 Star 的「开源项目」,大概率也是一个 SaaS 的广告位。看仓库之前,先看它真正的产品在哪。

评论互动

© 2026 王若风的技术博客 · Powered by Astro