永远在责备中提升代码理解能力Always Be Blaming
文章提出通过‘责备’(blaming)机制来增强对代码的理解力,建议开发者主动质疑和追溯代码行为背后的逻辑。作者强调将代码视为可问责的系统,有助于发现隐藏假设和设计缺陷。这种方法能提升调试效率和架构认知深度。
提升代码理解能力的几点建议。
我曾写过关于阅读代码重要性的文章:《警惕 bug》。我默认的阅读方式是“预测式”:我不会逐行实际阅读代码,而是先尝试理解它要解决的问题,然后设想自己会如何编写解决方案,再对比脑海中设想的代码与编辑器中看到的代码之间的差异(diff)。如果 diff 非空,则说明要么是我的理解有误,要么是代码本身有改进空间。
这是一种二维阅读方式——理解某一时刻被冻结的代码快照。通常这足以发现那些“感觉不对劲”的异常现象,值得进一步调查。
理想的代码是无记忆的——它能精确解决当前问题。而大多数真实世界的代码则是马尔可夫式的——在时间 T 时的代码形态不仅取决于问题陈述,还依赖于时间 T-1 时的代码形态。三维阅读的关键在于追溯代码随时间的演变过程:我们从何而来?我们是什么?我们将去向何方?
接下来的一步是理解“为什么”。当时我们写这段代码时是怎么想的?这里需要用到“心理理论”(theory of mind)的概念。我个人是在人生很晚的时候才学到这个术语,因此今天我要为幸运的 10,000 人做个简短介绍。心理理论是指能够想象自己置身于他人内心世界的能力——不只是“站在他们的角度”(“我当然会采取不同的行动”),而是真正理解“他们为什么会那样做”(“我不会那样做,但我能理解他们为何如此”)。这是人类可以学会的技能。实验设置通常是:把一个孩子和一个房间里的玩具放在一起,房间里另一端坐着一个娃娃,然后问孩子:“娃娃看到了什么?”年幼的孩子会从自己的视角描述房间,而年长的孩子则会开始推测娃娃的视角是不同的。
所以,阅读代码的目标就是理解原作者当时的思考方式和动机。
废话结束,说点实用建议。第一,每一行代码都应该有文档,这非常有用。
第二,确保你能轻松追溯任意一段代码的演变过程。这比看起来难得多!仅仅使用 git blame 是不够的——要注意“容易解决的问题”和“真正需要解决的问题”之间的差距。
git blame 回答的是空间性问题——“这一行是如何出现在这个文件中的”,因为这类信息有一个相对直观的 UI 呈现方式——为每行代码标注提交哈希值。但这并不是你平时最常问的问题!你并不关心整个文件!你只关注中间一小段代码,想要的是它的时间线历史。
尽管我不喜欢在浏览器中工作,但 GitHub 网页界面的 blame 功能可能比你在本地默认获得的功能更好。它从快捷键 y 开始,可以将符号引用解析为 URL 中包含提交哈希值的引用:
https://github.com/tigerbeetle/tigerbeetle/blob/main/src/vsr/replica.zig直接输出,每段格式为 [N] 后跟中文:
https://github.com/tigerbeetle/tigerbeetle/blob/c54f613a2eb2a127a0ba212704e3fa988c42e5cb/src/vsr/replica.zig这个提交哈希至关重要,因为它锚定了整个仓库——如果你从网页界面打开另一个文件,它将显示为该提交时的版本。这使得你不必仅仅局限于关注当前的差异部分,而是能够全面了解该时间点的完整上下文。
因此,我通常的网页工作流程如下:
再次强调,我的目标不是为某个文件的 diff 添加注释,而是获取一个“虚拟检出”状态,即那个有趣提交时的样子。
这种网页方式是我职业生涯中长期使用的做法,但现在我终于找到了一种方法可以在本地复现它。核心思想是让 blame 变成“就地”操作——不再只是给代码行打标签,而是直接切换到历史提交。为此我设计了一套复杂的快捷键组合:
, b l 会对当前光标所在行进行 blame。它会记录下 $line,运行 git blame -L $line,$line 找出引入该行代码的 $commit,然后执行 git switch --detach $commit 将其检出。我专门设置了一个工作树用于代码考古,所以不用担心破坏现有工作。虽然也尝试过保持“逻辑”上的光标位置,但效果并不理想。有没有什么 git 命令能直接告诉我:“对于 $sha-A,$file:$line:column 在 $sha-B 中的等价位置是什么?”
, b p 则是对父提交进行 blame,也就是切换到当前 HEAD 的父提交,这与 GitHub 上的“blame before this change”功能类似(尽管实现机制略有不同,因为它假设前一条命令是 , b l)
, b u 会撤销最近一次 blame 操作,回到之前的状态。我非常喜欢这个功能——在网页端我可以 cmd-点击创建分支探索路径。理论上这也可以在本地复现,但我更倾向于直接在磁盘上破坏性地修改单一工作树。选择就地 blame 的最大原因是 LSP、./zig/zig build test、rg 等工具都能正常工作,这一点对我来说比维护多分支路径更重要,而且撤销操作也可以接受作为折中方案。
最后,, b w 会将当前提交和行号的 GitHub 链接复制到剪贴板,方便粘贴到浏览器中使用。现代版本控制系统面临的一个巨大问题是,以代码评审评论形式存在的极其关键的信息并未包含在 git 仓库中,而是被锁在别人的专有数据库里。我在一个周末内未能解决这个问题,只能勉强适应现状——在浏览器中打开提交记录时,也会同时带你进入对应的 PR 及其讨论区。
实现这个 blame 工作流程需要一些自定义代码。欢迎随意使用,但请注意它有些粗糙,尤其是在维护当前光标位置方面。制作一个生产就绪的版本听起来像是一个有趣的项目 ;-)
需要完整排版与评论请前往来源站点阅读。