半夜刷 GitHub 热榜这件事我坚持了大概三年,这期榜单里两个名字挨着出现——Valhalla 的静态工程审阅,和 pdf-inspector 的源码证据驱动评测——第一眼看过去气质差得离谱:一个是几十万行级别 C++ 堆出来的地图路由引擎,一个是几百行 Rust 写的 PDF 分类小工具。但把它们放进同一期其实很有道理,因为它们考的是同一件事:在不跑业务、甚至不编译的前提下,光靠读仓库,你能把一个项目看透到什么程度。
这就是所谓静态工程审阅和源码证据驱动评测的核心。前者更像"体检",看的是目录分层、构建系统、依赖清单、CI 门禁、代码规约这些工程骨架;后者更像"法庭举证",每一个结论都必须能指回具体的文件、具体的行,README 里写的性能数字在没被源码印证之前,一律当成待验证的声明。这两套方法合起来,基本就是我现在判断一个开源项目值不值得引入、敢不敢上生产的主要依据。
这篇文章写给三类人:一类是刚学会 clone 仓库、想搞明白"该从哪里看起"的新手;一类是要在团队里做技术选型、需要一份能拿得出手的评估结论的工程师;还有一类是单纯喜欢围观大项目怎么组织代码的老手。我会把 Valhalla 和 pdf-inspector 当成两个样本,从零讲一遍我自己的审阅路径、判定口径和踩过的坑,看完你拿这套流程去审任何一个仓库都能用。
1. 两个项目先对号入座
1.1 Valhalla:C++ 地图路由引擎,名字还容易撞车
Valhalla 在开源地图圈子里算是个熟脸,它是一套用 C++ 写的路径规划与地图匹配引擎,吃的是 OpenStreetMap 那套原始数据,吐出来的是路径、等时圈、距离矩阵、地图匹配这些结果。它能做的事很多:算最快路线、算最短路线、算多模式出行(步行、自行车、摩托车、卡车、公交),还能按时间做动态绕行、按地形做坡度惩罚,对外通过 HTTP 接口暴露服务。做物流调度、出行平台、GIS 分析的人,基本都会碰到它。
需要先提醒一句:Valhalla 这个名字在 GitHub 上不止一个项目在用,"英灵殿"这个意象被太多人喜欢了。你在搜索框里敲进去,可能会撞到游戏相关的资源、别的小工具、甚至某个前端组件库。所以审阅的第一步永远不是看代码,而是先把 owner/repo 这对坐标钉死,确认你打开的是那个 C++ 路由引擎,而不是同名兄弟。这一步花三十秒,能省掉后面两小时的迷惑。
1.2 pdf-inspector:判断 PDF 里到底有没有"字"
pdf-inspector 解决的问题听起来很小,但实际非常脏:给你一个 PDF,你先得知道它到底是"文本型"还是"扫描型"。文本型 PDF 里每个字都有坐标、有编码,抽出来就能用;扫描型 PDF 本质是一堆图片,直接抽文本只会得到空白或者乱码,必须先走 OCR。很多文档处理流水线跑得又慢又贵,根子就在这个判断没做好——该 OCR 的没 OCR,不该 OCR 的白烧了一大笔算力。
pdf-inspector 就是干这个的:快速判断 PDF 属于哪一类,顺便把页数、标题、作者这些元信息也带出来,再往上还能做文本抽取。它用 Rust 写,主打的就是轻、快、依赖少,适合塞进服务端做预处理的第一道关卡。做 RAG 知识库、合同解析、票据识别的团队,几乎都会在流水线最前面放一个类似的东西。
1.3 为什么这俩值得放在一期里看
一个是大而全的重型引擎,一个是小而精的单点工具,把它们放一起的价值在于对照。审大工程,你要关注的是模块边界有没有烂掉、依赖有没有失控、构建开关是不是一堆耦合;审小工具,你要关注的是接口设计够不够窄、有没有把复杂度诚实地暴露出来、宣称的性能能不能对上代码里的实现路径。两套视角切换着练,你对"工程"这两个字的判断力会涨得很快。
另外一个现实原因是,这俩的代码体量差了三个数量级,正好可以用来演示不同体量下的审阅节奏:小项目我可能四十分钟就能给出结论,大项目光是把目录摸完就要一个下午。节奏不同,但清单是同一张。
2. 静态工程审阅到底在审什么
2.1 我固定的七处切入点
审阅不是漫无目的地翻文件,我每次都按同一张清单走,顺序基本不变,因为顺序本身就是在控制时间成本——先用最低成本的信息决定要不要继续投入。下面这张表是我这些年沉淀下来的固定动作,你可以直接拿去当模板用。
| 顺序 | 切入点 | 主要看什么 | 判断价值 |
|---|---|---|---|
| 1 | 仓库元信息 | 许可证、最近提交、issue 响应、star 曲线 | 判断项目是否还在维护,五分钟出结论 |
| 2 | 顶层目录 | 分层是否表达业务含义,还是全塞 src | 判断作者有没有架构意识 |
| 3 | 构建脚本 | CMake/Makefile/Cargo.toml/package.json | 判断依赖管理与可复现性 |
| 4 | 依赖清单 | 直接依赖数量、版本锁定方式、有无 vendored | 判断供应链风险与构建难度 |
| 5 | CI 配置 | 测什么、门禁卡在哪一步 | 判断团队纪律,比测试数量更真实 |
| 6 | 代码规约 | 格式化配置、lint 规则、命名一致性 | 判断长期可维护性 |
| 7 | 测试与基准 | 测试覆盖的是核心路径还是边角 | 判断哪些承诺是被验证过的 |
我特别想强调第五和第六项。很多人审仓库只看代码和 README,忽略了 CI 和规约文件,但这俩恰恰是最难伪装的。一个项目可以在 README 里写任何漂亮话,但.github/workflows里到底跑了哪些检查、格式门禁是不是卡在合并前、有没有跑 sanitizer,这些是装不出来的。
2.2 语言占比和代码统计怎么读才不被带偏
GitHub 首页那条语言占比彩条,是新手最容易误读的东西。它按字节数统计,不按逻辑复杂度,一份自动生成的 protobuf 定义文件或者一份巨大的 JSON schema,能轻松把一个仓库染成另一种颜色。所以我的做法是:彩条只用来形成第一印象,真正的判断靠目录结构和文件大小分布。
具体怎么操作?把仓库拉到本地后,我用cloc或者tokei跑一遍,然后重点看三件事:第一,核心逻辑目录下的代码量占比是多少,如果业务代码只占三成、剩下七成是生成代码和测试夹具,那这个项目的"阅读成本"和你看到的总行数完全不是一回事;第二,单文件最大是多少行,超过两千行的源文件我会单独标记,那通常是历史包袱的聚集地;第三,注释率和函数平均长度,这两个指标能侧面反映出团队有没有在做持续重构。
举个我自己的经验值:C++ 项目里,如果头文件的总行数接近源文件的两倍,那大概率模板和声明写得很重,编译时间会很感人;Rust 项目里,如果unsafe出现次数在除去 FFI 绑定层之后仍然超过两位数,那就得逐处确认安全性论证是否写了注释。这些都不是硬指标,但它们能帮你在开工前就知道该往哪里使劲。
2.3 构建系统是工程成熟度最诚实的证据
构建脚本之所以排在我的清单第三位,是因为它几乎无法说谎。一个项目的架构理念会直接体现在构建选项的划分上:选项是不是正交的、能不能单独关掉某一层、关掉之后依赖是不是也跟着消失,这些细节决定了你把它集成进自己系统时会不会被拖死。
我判断构建质量有个很土的办法:假装自己只想要它最核心的那一个功能,看看能不能只用两三个开关就编出来。如果为了用一个小功能,被迫把整套服务端、数据库客户端、图形库全拉进来,那这个项目的模块化就只是纸面上的。反过来,如果每个可选能力都有对应的开关,而且关掉后编译能过、测试还能跑,那说明作者是真的在设计接口,而不是在设计口号。
3. Valhalla 静态审阅实录
3.1 目录分层:它把"数据"和"算法"切得很干净
打开 Valhalla 的顶层目录,你会看到一堆听起来像北欧神话的名字,别被吓到,这其实是这个项目最值得学的地方——它用命名把职责划得非常清楚。我的理解是这样的:有一层专门负责把原始地图数据加工成内部格式,有一层负责定义图的数据结构和读写,有一层负责请求解析和参数校验,有一层负责各种出行方式的代价模型,还有一层专门负责路径搜索算法本身,最后还有一层负责把算出来的路径翻译成人能看懂的文字指引。
这个分法的好处是改动的影响面可以被预估。你要加一种新的出行方式,改动基本落在代价模型那一层;你要换一种搜索算法,改动落在算法层,不用碰数据加工。这就是"高内聚低耦合"的具体样子,不是口号。审阅的时候我建议你随手画一张依赖方向图——谁调用谁,箭头只能单向流动还是出现了互相引用。如果出现两个大模块互相依赖,那就是架构开始腐化的第一个信号。
3.2 依赖清单与构建开关:一块"胖"工程的取舍
Valhalla 属于典型的"胖"工程,依赖里你会看到序列化库、几何计算库、HTTP 相关库、坐标系转换库、压缩库这一串。这不是作者贪心,而是它解决的问题本身就横跨数据处理、数值计算和网络服务三个领域,绕不开。审阅重点不在于"依赖多不多",而在于"依赖管得好不好"。
我看两个点。第一是版本策略:依赖是写死了精确版本,还是用一个宽松范围?精确版本可复现性强但升级麻烦,宽松范围升级省事但容易在别人机器上炸。老牌 C++ 项目常见的做法是配一份依赖安装脚本加一份容器镜像定义,把环境固化下来,同时用包配置机制在构建时发现依赖。第二是开关设计:能不能关掉服务端只留数据加工、能不能关掉测试和基准以缩短编译时间、能不能关掉图形格式支持以减少依赖。这些开关如果存在且正交,你把它嵌进自己的构建系统就会轻松很多。
提示:审阅依赖时,把每个直接依赖问三个问题——它的许可证是什么、它最近的提交是什么时候、它有没有被标记过安全公告。这三问能筛掉大部分隐患。
3.3 CI 与代码规约:格式门禁比测试更能说明团队纪律
Valhalla 这种体量的 C++ 项目,如果没有强制格式化,代码风格早就散成一锅粥了。所以我在仓库里找的第一批文件就是格式化配置和对应的脚本,以及 CI 里有没有一步专门跑格式检查。有格式化脚本只是及格,把格式检查放进合并门禁才是真章。道理很简单:脚本谁都能写,但只有卡在合并前,才会真正被执行。
测试方面我会区分"跑得起来的测试"和"跑得快的测试"。老项目常见的情况是,测试套件非常大,但本地跑一遍要几十分钟,结果就是没人跑,CI 成了唯一的执行者。这时候我会去看它有没有把测试分层:快速的单元测试放在每次提交都跑的流水线里,耗时的集成测试和端到端测试放在定时任务里。分层的存在,说明团队在认真对待"反馈速度"这个工程指标。
另外一件容易被忽略的事是 sanitizer。地址检查和未定义行为检查这两类工具跑一遍的成本不低,但如果一个 C++ 项目在 CI 里长期挂着它们,说明团队对内存类缺陷是有敬畏心的。这类项目你引入时踩雷的概率会明显降低。
3.4 审阅结论与风险清单
按上面这套走完,我对 Valhalla 的整体判断是"工程纪律在线、上手门槛偏高"。它的模块划分和构建开关设计都值得借鉴,但代价是编译时间长、依赖链条深、跑通一次完整的数据加工流程需要准备足够的数据和磁盘。如果你想引入它,我建议的路径是先跑容器镜像验证功能,再决定要不要自己从源码编。
| 观察项 | 表现 | 对我的影响 | 应对建议 |
|---|---|---|---|
| 模块命名 | 按职责分层,命名自解释 | 阅读成本低,改动影响面可预估 | 先画依赖图再动手改 |
| 依赖数量 | 偏多,横跨多个领域 | 首次编译时间长 | 用官方容器镜像起步 |
| 构建开关 | 粒度较细,能力可裁剪 | 集成灵活度高 | 只开自己需要的能力 |
| 测试分层 | 相对完整 | 回归信心较足 | 本地先跑快速层 |
| 数据准备 | 需要额外下载原始地图数据 | 无法开箱即用 | 提前规划磁盘和流程 |
3.5 影响范围:谁会被它的设计选择波及
一个地图引擎的设计选择,往下游传导得非常远。它把数据加工和在线服务拆开,意味着部署形态天然是"离线预处理 + 在线查询"两段式,这会直接影响你的运维结构:你得有一个定时任务负责把新地图数据加工成内部格式,还得保证在线服务在做数据热切换时不断请求。这不是简单的重启进程,而是要在数据目录级别做版本管理。
它把出行方式的代价模型做成可插拔的一层,好处是扩展方便,代价是配置项极多,参数之间还互相影响。这一层是新人最容易配错的地方——改了一个权重,另一类出行方式的结果也跟着变。所以如果你的团队要用它,建议把代价模型相关的配置单独做一份内部文档并配回归用例,不要指望业务同学自己摸索。
再往外一层,因为它是通过 HTTP 暴露能力的,你的网关、限流、超时策略都要跟着它的请求模型走。它的某些接口单次请求耗时并不低,超时阈值设得太紧会频繁失败,设得太松又会拖垮上游。这些细节在 README 里通常不会写,得靠审阅时从接口定义和算法结构里推出来。
4. pdf-inspector 源码证据驱动评测
4.1 评测前置原则:README 不算证据
源码证据驱动评测的第一条规矩,是把 README 降级成"待验证的声明"。作者写"极快""极轻""高准确率",这些词本身没有错,但它们不是证据,只是假设。我评测一个库时会在纸上先列一份待验证清单,然后逐条去代码里找对应实现,找到就打勾,找不到就打问号。
这份清单我一般这么列:宣称的判定能力,对应到代码里是哪几个函数;宣称的性能优势,是靠算法路径短还是靠并行,还是单纯靠样本挑选;宣称的内存占用低,是不是因为做了零拷贝或者延迟解析;宣称的依赖少,去看依赖清单里有没有重量级解析库;宣称的错误处理到位,去看返回类型设计成异常还是结果对象。每一条都要能被一个文件路径支撑,这份评测才有说服力。
4.2 从入口函数反推数据流
拿到一个陌生 Rust 库,我习惯先看导出接口,因为导出接口决定了它的能力边界和调用姿势。以我本地拉到的版本为例,入口大概长这样,具体函数名可能随版本微调,但形状是类似的:
use pdf_inspector::{detect_pdf, PdfType}; let bytes = std::fs::read("sample.pdf")?; let report = detect_pdf(&bytes)?; match report.pdf_type { PdfType::TextBased => { // 直接走文本抽取路径 } PdfType::Scanned => { // 交给下游 OCR 环节 } _ => { // 混合型或空文档,单独分流 } }从这段接口能读出几个设计取舍。第一,它接收的是字节切片而不是文件路径,说明库本身不负责 IO,把文件管理权留给调用方,这是很干净的边界。第二,它返回的是一个描述对象而不是布尔值,说明作者意识到"是不是扫描件"这个问题的答案不是非黑即白,存在混合型文档这种情况。第三,判定和抽取被我拆成了两步,先判定后抽取,这意味着调用方可以先做便宜的操作,再决定要不要付出更贵的抽取成本。
接着我顺着导出函数往里追,看它内部怎么组织:通常会有个解析层负责按照 PDF 格式读出对象树,有个统计层负责遍历页面收集字符与字体信息,有个判定层负责按阈值给出分类,最后有个元信息层负责拎出页数、标题这些字段。这四层如果分得清楚,说明作者在写的时候就想过复用;如果全糊在一个函数里,那这个库大概率只适合当脚本用,不适合当依赖。
4.3 判定逻辑:文本型、扫描型、混合型是怎么分开的
这是这个库最有意思的部分,也是最需要"证据"的部分。判断一个 PDF 有没有文本,直觉答案是"看能不能抽出字",但工程上不能这么干——真正抽一遍文本本身就是那个贵的操作,你想省的就是它。所以合理的实现路径是:只读文档结构,统计每页里字符对象、字体对象、内容流的分布,用统计量做判定,而不是真的把文字解出来。
我关注的具体指标包括:页面里有没有字体资源、内容流里文本绘制指令的密度是多少、平均每页的字符数落在什么区间、有没有出现整页只有一张大图的模式。这些统计做完,阈值一卡,分类就出来了。这里最值得审的是阈值是怎么定的——是拍脑袋写死的常数,还是从一批标注样本里调出来的,代码里如果能看到明确的常量加注释说明来源,可信度就高很多。
注意:分类阈值天生是场景相关的。合同、发票、学术论文、扫描古籍,这四类文档的统计分布完全不一样。你在自己业务里用之前,一定要拿自己的样本重新跑一遍分布,别直接信默认值。
4.4 依赖与 unsafe:内存安全的证据链
Rust 项目谈安全,不能只看"它用 Rust 写的"。我会做两件事:一是看依赖树,二是数unsafe。依赖树用工具跑一遍,重点找有没有那种为了一个小功能拖进来的大依赖;unsafe则逐个确认,看每一处是不是都配了说明为什么要这么写。
一个处理 PDF 的库,理论上确实可能需要unsafe,比如做内存映射、做零拷贝的缓冲区切片、或者和 C 写的压缩库对接。这些都是合理理由,但合理理由必须写在注释里。如果代码里出现裸的unsafe块,周边没有任何解释,那对我来说就是一个明确的减分项——不是因为它一定会出错,而是因为作者没有为将来的维护者留下判断依据。这一点在大项目里同样成立,只是大项目通常有更多"历史原因",容忍度会稍微高一些。
另外我会看依赖许可证。做文档处理的服务通常要过法务,依赖里如果混进一个许可证类型比较特殊的库,可能会给整个产品带来合规问题。这类问题越早发现越省钱。
4.5 评测口径设计与同类方案对比
评测一个库不能只跑作者给的样例,那等于用出题人的卷子考出题人。我的做法是自建三组样本:第一组是干净的电子文档,第二组是纯扫描件,第三组是那种"扫描件后面又贴了一页电子页"的混合文档,第三组才是真正拉开差距的地方,很多实现会在这一类上翻车。样本量不用很大,每组三五十个,但必须是我自己业务里真实出现过的形态。
| 评测维度 | 验证方式 | 常见陷阱 |
|---|---|---|
| 判定准确率 | 三组自建样本,人工标注 | 只用干净样本会虚高 |
| 单页耗时 | 同一份文件重复跑,看分位数 | 只看平均值会被长尾掩盖 |
| 内存峰值 | 大文件下观测峰值占用 | 小文件测不出问题 |
| 错误处理 | 喂损坏文件、空文件、加密文件 | 崩溃比报错更常见 |
| 依赖体积 | 看最终产物大小与依赖数量 | 开发依赖和生产依赖要分开算 |
拿它和"直接上 OCR"的粗暴方案比,它的价值在于把成本前置判断了:能在预处理阶段用很便宜的代价把大部分文本型文档筛出来,让 OCR 只处理真正需要的那部分。这个收益在文档量大、扫描件比例低的时候非常明显;反过来,如果业务里九成都是扫描件,那这个判定层的收益就有限了,该优化的是 OCR 本身。
5. 源码拉取实操:访问不稳时怎么拿到完整仓库
5.1 先学会"少拉一点":浅克隆与稀疏检出
审阅大仓库时最常见的浪费是把整部历史全拉下来。历史提交、大体积二进制资源、已经不用的分支,这些东西对静态审阅几乎没有价值。我标准的开场是浅克隆加过滤,只拿最新一次提交和必要的文件内容:
# 只拉最新一次提交,跳过全部历史 git clone --depth 1 https://github.com/<owner>/<repo>.git # 需要历史但不想下载大文件内容时,用部分克隆 git clone --filter=blob:none https://github.com/<owner>/<repo>.git # 只要某几个目录,做稀疏检出 git sparse-checkout init --cone git sparse-checkout set src include cmake这三个命令我几乎每次都用。深度的取舍很清楚:做架构审阅,历史价值有限,--depth 1足够;做代码演化分析,比如想看某个模块是怎么一步步长成今天这样的,那就得保留历史,但可以用部分克隆把大文件内容按需拉取,首次克隆能快好几倍。
5.2 镜像站与国内托管平台的同步玩法
访问不畅的时候,我通常有三条路走,按可靠性排序。第一条是用国内代码托管平台提供的仓库导入功能:把上游仓库地址填进去,它会帮你做一次完整同步,之后你在本地从这个副本克隆,速度通常可以接受。这条路的代价是同步有延迟,审阅时要在结论里注明"基于某日期的副本"。第二条是直接下载归档文件,不经过 Git 协议,很多情况下反而更顺畅。第三条是团队自建缓存,把常用仓库在内部机器上做一份裸仓库,内部所有人都从它克隆,这是长期最优解,一次性投入,之后就再也不用跟网络较劲了。
需要提醒的是,用任何第三方副本都要核对一致性。克隆完第一件事是比对提交哈希,确认你拿到的确实是对应上游版本,而不是某个被改过的分支。这一步在多来源环境下尤其重要。
5.3 Release 归档、git bundle 与校验
如果我只是想看某个发布版本,压根不需要克隆仓库。发布页上的源码归档包就是最省事的入口,下载解压即读。要做离线传递的时候,git bundle是个被严重低估的工具,它能把一个仓库的全部历史打包成一个文件,拷到没有网络的环境里再解出来:
# 打包全部历史 git bundle create repo.bundle --all # 在目标机器上做校验 git bundle verify repo.bundle校验这一步别省。文件在传输过程中损坏是常有的事,verify会在解包前就告诉你包是否完整、依赖的引用是否齐全。另外下载归档包时记得核对摘要值,发布页如果有提供校验信息,就一定要用上。
5.4 几个我常用的 GitHub 检索语法
搜代码这件事,九成的人只会往搜索框里敲关键词,结果被一堆不相关的结果淹没。其实 GitHub 的检索语法非常好用,尤其是做静态审阅的时候,我几乎全靠它来定位关键实现。下面这几个是我用得最频繁的:
language:Rust path:src unsafe # 找某语言某目录下的不安全代码 repo:<owner>/<repo> filename:Cargo.toml # 在指定仓库里找特定文件 language:C++ in:file "clang-tidy" # 全文搜索特定内容 stars:>1000 pushed:>2024-01-01 topic:pdf # 找活跃的 PDF 相关项目配合仓库页面的文件树快捷键,定位效率还能再提一截:在仓库首页按t可以直接进文件搜索,输入文件名模糊匹配;看某个文件的历史修改,用 blame 视图比翻提交记录快得多。这些操作看起来是小技巧,但在审阅大仓库时,累积起来能省下相当可观的时间。
6. 常见问题与排查速查
6.1 审阅过程中的疑问
审阅最常卡住的不是技术,而是"我该怎么判断"。比如看到一段写得很难看的代码,要判断它是历史遗留还是有意为之。我的办法是回溯提交历史,看这段代码最后一次改动是什么时候、当时改了哪些文件。如果它孤零零地待在某个角落、多年没人碰,那基本就是遗留;如果它周边一直在被维护却始终保持这个形态,那多半有原因,值得多读两遍再下结论。
另一个常见疑问是"要不要看生成代码"。我的答案是看,但只看生成规则,不看生成结果。生成出来的文件动辄几万行,读它们没有意义;有价值的是生成脚本和模板,它们能告诉你这个项目的数据契约长什么样。
6.2 拉取与构建阶段的坑
| 现象 | 大概率原因 | 处理方式 |
|---|---|---|
| 克隆中途反复中断 | 单次传输体积过大 | 浅克隆、部分克隆、稀疏检出 |
| 构建报缺少依赖 | 依赖来自系统包管理器 | 用官方容器镜像或依赖脚本 |
| 编译极慢 | 模板重、调试符号多、未开并行 | 开编译缓存、调并行参数、减调试信息 |
| 测试跑不完 | 集成测试未分层 | 只跑快速层,慢的放后台 |
| 数据准备失败 | 原始数据只下了部分切片 | 先小范围跑通再放大 |
这张表里的每一条我基本都亲自撞过。最想强调"数据准备失败"那一行:大项目的离线数据加工通常分好几个阶段,每个阶段都有自己的输入输出目录,任何一个环节的文件没下全会导致后面莫名其妙地失败。正确的姿势是先拿一小块区域的数据把整条链路跑通,确认每一环的产物都在,再放大到全量。
6.3 我的避坑清单
最后说几条我反复吃亏之后总结出来的习惯。第一,审阅开始前先写一份"我这次要回答的三个问题",比如"它能不能嵌进我的构建""它的核心路径有没有被测试覆盖""它的依赖许可证有没有风险",三个问题足够聚焦,不会让你在仓库里迷路。第二,任何结论都要留下证据路径,文件加行号,哪怕只是给自己看,一周后你还能想起来为什么这么判断。第三,别在没跑通最小用例之前就给项目下结论,静态审阅能看出架构和纪律,但看不出实际运行时的脾气。
还有一条偏经验:对新项目宽容一点,对老项目严格一点。新项目缺测试、缺文档是常态,只要核心设计干净就值得关注;老项目如果还在用十年前的组织方式并且没有重构迹象,那引入成本会远超你的预期。这个尺度不好量化,但多审几十个仓库之后,你会有感觉。
我个人在实际操作中的体会是,静态工程审阅和源码证据驱动评测这两件事,最大的价值不在于"给项目打分",而在于逼你把"我觉得"换成"我在哪个文件里看到"。前几次做的时候会很慢、很别扭,总想跳过步骤直接下结论,但坚持几轮之后你会发现,自己看代码的眼光变尖了,被 README 漂亮的措辞误导的次数也肉眼可见地下降了。下次再看到热榜上那些眼熟的名字,你可以先别急着 star,花四十分钟按这套清单走一遍,收获大概率比收藏夹里多躺一个链接要大得多。