读代码之前,先读它的历史

今天 Hacker News 排第一的文章,不是什么宏大叙事,只是一篇很短的工程经验文:在真正打开任何代码文件之前,先跑几条 git 命令,看这个项目过去一年到底哪里最常改、哪里最常出 bug、谁在维护、节奏有没有变慢、团队是不是总在救火。

它看起来很基础,甚至有点像"老工程师口口相传的小抄"。但我觉得它很值得写下来,因为它说中的不是 git 技巧本身,而是一个更普遍的判断:理解代码库,不应该从"代码长什么样"开始,而应该从"这个系统哪里一直在痛"开始。

很多人接手新项目时,会下意识从目录结构、入口文件、框架层次往下读。这当然没错,但它默认了一件事:代码本身已经足够诚实,能直接告诉你系统的真实结构。现实往往不是这样。一个成熟代码库最重要的信息,常常不在静态结构里,而在时间维度里。哪些文件一直被反复改,哪些模块换了几拨人还在补丁叠补丁,哪些地方 commit message 里永远带着 fix、hotfix、revert、rollback,这些才是系统真正的受力点。

换句话说,代码告诉你"它现在长什么样",历史告诉你"它为什么会长成这样"。后者在很多时候更重要。因为工程难题往往不是抽象设计题,而是演化题。一个文件不好动,不一定是因为它今天写得最差,也可能是因为它承接了太多历史债务、团队共识和过去没做完的迁移。你如果只读代码,不读历史,就很容易误把"结果"当成"原因"。

原文提到的几条命令其实都在做同一件事:给代码库做体检。看 churn 高的文件,是在看哪里总被碰;看 bug 相关 commit 聚集在哪,是在看哪里总反复坏;看作者分布,是在看 bus factor 和维护风险;看每月提交量,是在看团队节奏有没有塌;看 revert 和 hotfix,是在看交付是不是一直带着火气。

这些信号单看都不完美。commit message 可能写得很烂,squash merge 也会掩盖真实作者分布,某些高 churn 文件只是功能活跃,不一定就是烂模块。嗯,是这样的,单个指标都不该被神化。但它们组合起来,会比你盲读半天代码更快让你知道应该先看哪里,先怀疑哪里,先避免动哪里。

我觉得这件事在 AI 时代反而更重要了。因为 coding agent 最容易犯的一个错误,就是太快开始"改代码"。它知道怎么生成 patch,知道怎么按表面需求完成局部修改,但它不一定知道一个系统的摩擦力分布。哪个文件一碰就炸,哪个模块虽然丑但其实是业务中枢,哪个目录看似冷清其实是没人敢碰,agent 并不会天然理解。没有这些历史上下文,自动化修改往往会非常高效地把你带进更大的坑里。

所以这篇短文真正有价值的地方,不只是给了五条 git 命令,而是给出了一种顺序:先做项目考古,再做代码阅读;先理解风险分布,再决定阅读路径;先知道哪里疼,再决定手术从哪里下刀。

这背后其实是一种很朴素但很稀缺的工程观。好的工程师,不是读代码最快的人,而是最先识别出"哪里值得读、哪里值得怀疑、哪里不能轻举妄动"的人。很多经验,归根结底都是在训练这种注意力分配能力。

如果把它再往前推一步,我甚至觉得这会成为未来 agent workflow 的标准前置步骤。不是让 agent 一上来就 grep、read、edit,而是先自动生成一份"代码库受力地图":高 churn 文件、缺陷热点、作者集中度、热修频率、变更节奏、核心模块演化史。这样人和 agent 才不是在无差别扫代码,而是在有风险感知的前提下进入项目。

软件工程里有一个常见幻觉:以为代码是客观事实,历史只是附注。其实很多时候正好相反。代码只是当前切片,历史才是解释器。读代码之前,先读它的历史,不是绕远路,而是在避免用最昂贵的方式走错路。