
> 本文由 Intern-S1 等 AI 生成,机智流编辑部校对
别急着打开代码
接手一个新项目,大多数人的第一反应是 git clone 然后开始翻代码。但有个做了多年代码审计的工程师 Ally Piechowski,她的习惯刚好相反:打开任何文件之前,先在终端里跑几条 git 命令。提交历史比代码本身更能反映项目的健康状况——谁在写、哪里老出问题、团队是在稳步推进还是到处灭火。代码告诉你系统长什么样,提交历史告诉你它是怎么一步步长成这样的,后者的诊断价值往往更高。
五条命令
哪些文件改得最多
git log --format=format: --name-only --since="1 year ago" \
| sort | uniq -c | sort -nr | head -20
过去一年改动次数最多的 20 个文件。排在最前面的那个,大概率就是团队里人人都会提醒你"别碰"的文件。
频繁改动本身不一定是坏事,活跃开发嘛。但如果一个文件改动频率很高,却始终没人做结构性重构——全是小补丁——那就有问题了。治标不治本。这种文件有个很形象的叫法:Dead Zone。团队不会去修它,而是绕着它写代码,就像树绕着栅栏柱子长。
微软 2005 年做过一项研究,发现用改动频率预测缺陷,比用代码复杂度指标更准。你可以把这条命令的输出和下面 bug 聚集点那条命令交叉对比——改得多又老出 bug 的文件,就是最大的风险点。
谁写了这些代码
git shortlog -sn --no-merges
这条命令列出所有贡献者,按 commit 数量排名。如果一个人贡献了 60% 以上的 commit,这就是你的 bus factor,要是这个人半年前已经走了那就更危险了。加个时间窗口再跑一次:
git shortlog -sn --no-merges --since="6 months ago"
如果历史总排名第一的人不在最近半年的列表里,要立刻标记出来。另一个值得看的是列表尾巴:项目有 30 个贡献者,最近一年活跃的只有 3 个——造系统的人和维护系统的人已经不是同一批人了,大量的架构决策和隐性知识很可能随着人员流动一起走了。不过有个坑要注意:如果团队用 squash-merge,这个输出显示的是谁点了合并按钮,不是谁写了代码,下结论之前先搞清楚合并策略。
Bug 在哪里聚集
git log -i -E --grep="fix|bug|broken" --name-only --format='' \
| sort | uniq -c | sort -nr | head -20
思路和 churn 分析那条一样,只不过筛选条件换成了带 bug 相关关键词的 commit。把这个列表和前面的改动频率列表放在一起看,两边都上榜的文件风险最高:一直在出问题、一直在打补丁,但从来没真正修好过。准确度取决于团队写 commit message 的习惯,都写"update stuff"的话你什么也捞不到——不过话说回来,粗略的 bug 分布图也比没有强。
项目在加速还是在减速
git log --format='%ad' --date=format:'%Y-%m' | sort | uniq -c
这条命令按月统计 commit 数量,覆盖项目的完整历史。关键是看曲线的形状:节奏平稳是好信号;某个月突然掉了一半,大概率有人离职了;连续下滑半年到一年,说明团队在失去势头;隔几个月来一次尖峰再沉寂,说明在攒批次发布而不是持续交付。她有一次把 commit 速度曲线展示给一位 CTO,对方看到某个月的断崖式下跌,说"那个月我们走了第二个 senior"——他之前从来没把产出下降和人员变动关联起来过。本质上这不是代码数据,是团队数据。
团队多久灭一次火
git log --oneline --since="1 year ago" \
| grep -iE 'revert|hotfix|emergency|rollback'
看 revert 和 hotfix 出现的频率。一年出现几次很正常,但两周就来一次的话,说明团队对自己的发布流程没有信心——测试靠不住、缺 staging 环境、或者回滚比部署还费劲。一条都搜不到也别高兴太早,要么团队确实稳,要么是 commit message 里根本没人写这些词。
数字背后的东西
五条命令跑完用不了几分钟。它们不能告诉你全部真相,但能指出该先看哪些代码、带着什么问题去看。有方向地读代码和漫无目的翻文件,效率完全不一样。
她在另一篇文章里提出了一个概念叫 Codebase Drag——代码库对团队产生的拖拽力,让每件事都比它本该花的时间更长。这种东西不会出现在任何仪表盘或 sprint 报告里,但它实实在在地拖慢了所有人。
她总结了五个典型症状,和前面那五条 git 命令刚好能对上:
一个真实案例很能说明问题:某个团队花了一整周才给后台管理面板加上 CSV 导出,两个工程师、需求明确、实际编码量大概一天,多出来的时间全耗在了"把现有代码理解到能安全修改的程度"上。写代码从来不是最贵的部分,读代码才是。
先跟人聊,再看代码
她的审计方法论里有个反直觉的原则:在 git clone 之前,先跟人聊。
问开发者三个问题:
你最怕改哪块代码? 上次周五部署是什么时候? 最近三个月,有没有测试没拦住、直接在线上炸了的 bug?
问技术负责人:
有没有哪个功能卡了一年以上一直没做? 最近一次严重超出预估的需求是什么?
这些对话能挖出来的信息是跑工具看不到的。比如问"上次周五部署",对方一笑,就什么都清楚了——部署在这个团队是高危操作,大家活在随时可能搞崩线上的阴影里。
聊完了才看代码。而且不是从业务逻辑看起,而是先翻三个文件(以 Rails 项目为例,其他技术栈也有对应的东西):
依赖清单(Gemfile / package.json / requirements.txt):数一数有多少依赖,找重复职责——两套登录系统、两个文件上传库——标记那些你解释不了为什么存在的依赖。 数据库 schema:找列数超过 30 的"上帝表"、外键上缺索引的、有表但没有对应 model 的僵尸表。 路由文件:RESTful 路由和自定义路由的比例。自定义路由多到 500 条,那不是风格差异,是架构出了问题。
半小时翻完这三个文件,不用跑任何工具,就能有一个大致的判断。比如她遇到过一个很典型的案例:某客户的 transactions 表有 122 列。stripe_charge_id、wire_transfer_reference、ach_routing_number、paypal_transaction_id——公司对接过的每个支付渠道,都在这张表里占了一组 nullable 列。一笔银行转账不需要 stripe_charge_id,一笔 Stripe 扣款也用不到 ach_routing_number。大部分行的大部分列都是空的。
更要命的是这张表用了 integer 主键。日交易量不小,signed integer 的上限大约 21 亿。她问 CTO 什么时候会撞到这个上限,对方压根不知道有这回事。
从发现问题到解决问题
数据有了,下一步怎么用?建议是别想着一口气全修,给每个症状打个分(0-2 分),从分最高的那个开始。
部署恐惧最严重?先搞 CI 提速、自动回滚、把部署拆小。 估时偏差最大?先把波及范围最广的模块解耦。 新人上手太慢?花一天修好启动脚本和环境文档,以后每招一个人都省一周。
给自己两周时间,选一个信号、跑一个聚焦的 sprint、盯一个具体指标。部署频率最好衡量,估时准确度或新人首次 PR 时间也行。不搞大重写,就做一次点状投入,让团队感受到改善。
最难的部分不在技术层面,而在说服管理层掏出时间预算。因为不修的代价是隐形的。但你把话换一种说法,效果完全不同:"我们有技术债"听着像程序员在发牢骚;"因为这三个模块的耦合,每个需求平均多花一倍时间"——这是管理层听得懂的业务成本。
git 历史最值钱的地方就在这里:它把"感觉代码不太行"变成了可量化的、拿得出手的证据。
几点额外的想法
用了一段时间这套方法,补充几个自己的体会。
commit message 的质量直接决定了你能从 git 历史里挖出多少东西。团队连 commit message 都懒得好好写的话,你跑这些命令只会得到噪声。反过来想,这本身也是一种信号——commit message 都敷衍的团队,代码审查大概也是走过场。这套分析在 monorepo 里特别好使,代码量大到不可能通读的时候,churn 分析能迅速告诉你哪几个子系统正在"着火"。
还有一个不太常规的用法是面试。大多数公司不会让候选人看源码,但你完全可以问那些诊断性的问题,比如"你们上次周五部署是什么时候"——听着人畜无害,信息量极大。
这几条命令稍微改改参数还能做趋势对比,比如 churn 分析分别用 --since="3 months ago" 和 --since="1 year ago" 跑两遍,短期热点和长期热点一对比,马上能看出问题在恶化还是在收敛。工具就在你的终端里,花五分钟跑一下,在打开第一个文件之前。
-- 完 --
机智流推荐阅读:
1.
2.
3.
4.
cc | 大模型技术交流群 hf | HuggingFace 高赞论文分享群 lc|LangChain 技术交流群 code | AI Coding 交流群 具身 | 具身智能交流群 硬件 | AI 硬件交流群 推理 | AI 推理框架交流群 智能体 | Agent 技术交流群