正念编码:目的与意图
摘要
一篇鼓励开发者以清晰意图编写代码的文章,认为命名困难往往揭示了抽象设计的问题,并指出理解代码的“为什么”与“是什么”同样重要。
<p><a href="https://lobste.rs/s/864m3v/mindful_coding_purpose_intention">评论</a></p>
查看缓存全文
缓存时间: 2026/08/12 14:22
# 目的与意图 — var0.xyz
来源:https://var0.xyz/posts/mindful-coding-purpose-and-intention.html
## 正念编程:目的与意图
2026\-08\-09“正念编程”听起来有点挑衅意味,甚至可能有点标题党,我知道。我并不是说你在打字时应该专注于呼吸,或者敏锐地感知手指的每一个动作。
我的意思更简单:留意代码的意图。
编程语言已经给了我们描述动作的词汇。我们可以遍历列表、调用函数、赋值、抛出异常。但有趣的问题往往不是代码在做什么,而是为什么。
这种区别很容易被忽略,因为机制就摆在我们眼前。一个 `for` 循环告诉我我们在迭代。但它不会告诉我为什么需要访问这些项目,或者这次迭代在更大的操作中扮演什么角色。好的代码填补了缺失的上下文——不是通过逐行解释,而是通过让代码的目的显而易见。
## 名字不仅仅是标签
我们传达意图的最简单方式之一就是命名。
值 `5` 几乎不能告诉我任何信息。一个名为 `MAX_RETRIES` 的常量告诉我这个数字有特定作用:它代表一个操作应该被尝试的次数限制。名字赋予了该值存在的理由。
这就是命名如此困难的原因之一。我们经常开玩笑说,计算机科学中最难的问题有 2 个:命名、缓存失效和差一错误。这个玩笑之所以成立,是因为命名确实很难,有时我们还没有充分弄清楚一段代码是用来做什么的,以至于无法给它起一个好名字。
最近,我和一位同事讨论把一些代码提取成一个函数。我们本可以这样做。提取会让周围的代码更短。但随后我们绞尽脑汁也想不出新函数的名字。
这让我停下来问了一个不同的问题:*我们一开始为什么要提取这些代码?*
问题不在于我们没有找到正确的名字,而在于根本没有一个有意义的可命名概念。我们只是在代码中画了一条任意的边界。我们并没有分离关注点、封装一段连贯的逻辑,或创建可复用的东西。我们只是把几行代码移到别处。
命名的困难暴露了设计中的问题。
## 当名字暴露了抽象
这时,命名就变得比挑选可读的标识符更有趣了。名字可以告诉你抽象本身是错的。
想象一个函数,它的名字实际上必须类似于 `validateAndStore`。也许在某些上下文中这完全合理。但如果这个函数既验证输入、又存储输入,还决定如何返回 HTTP 错误,那么我们有更强的信号表明多个不同的关注点被强行挤在了一起。
问题不在于 `validateAndStore` 是一个坏名字。问题在于这个名字准确描述了一个做得太多的抽象。
好的抽象给了我们值得命名的东西,因为它们对应着有意义的概念。当我们不得不为一个任意的代码切片编造名字时,这种挣扎可能是症状而不是疾病。
所以当你卡在一个名字上时,有时答案不是寻找一个更好的词。退一步问:**我实际想要命名的概念是什么?** 如果根本不存在这样的概念,也许那里就不应该有边界。
## 代码告诉你是什么。历史告诉你为什么。
同样的区别也出现在代码之外。
考虑一条提交信息。常见的写法是“添加验证模块”或“引入重试逻辑”。这些不一定错误,但往往不是很有用。提交已经包含了代码。如果我想知道*什么*变了,我可以查看 diff。
更有价值的问题是:*我们为什么做这个改变?*
也许我们发现了一个 bug。也许用户遇到了某种特定的失败。也许旧的实现在正常情况下没问题,但在我们未曾预料到的条件下崩溃了。也许我们尝试了另一种方法,发现它行不通。
这些信息不一定存在于代码中。它是代码历史的一部分。
因此,有用的提交信息不只是叙述 diff。它保留了导致该 diff 的推理过程。多年后,当有人问起这段看似奇怪的代码为什么存在时,提交历史可以给出答案。
## 注释不应重复代码
注释也有同样的问题。
像这样的注释:
``
x = 5 # 将 5 赋值给 x
``
什么也没有添加。代码已经告诉我们这一点。
一条有用的注释解释的是代码本身不容易表达的内容:为什么那个特定的值是必要的,为什么一个看似多余的操作必须保留,为什么一个显而易见的优化不应尝试。
例如,如果一段代码因为之前遇到的一个 bug 而看起来很奇怪,注释可以保留这个上下文:
``
# 不要把这个优化掉。这看起来多余,但移除它会
# 重新引入 issue #1234 中描述的竞态条件。
``
确切的措辞并不重要。重要的是注释告诉读者一些他们无法仅通过阅读代码就了解到的信息。答案不一定是解释整个系统的冗长注释。引用相关的 issue、讨论或文档片段可能更有用。
## 意图就是信息
这就是我所说的正念编程。它不是关于写更多注释、更长名字或更复杂的抽象。事实上,有时你能做的最有意识的事情是*不*添加另一个抽象、注释或层次。
它关乎对你代码所传达信息的自觉。代码本身通常擅长告诉我们发生了什么;而逻辑周围的“支撑结构”——名字、边界、注释和历史——则能告诉我们为什么发生。
这个“为什么”往往是未来读者真正需要的。它解释了一个值为什么存在,一个函数为什么是一个连贯的概念,一个抽象为什么有特定的边界,一段看起来奇怪的代码为什么必须保留,或者一个改变为什么一开始要做。
当这些东西是有意识的,代码就变得更容易理解,而无需更多解释。当它们是任意的,我们常常能感觉到:名字不贴切,抽象不太讲得通,注释不得不解释那些本应通过设计显而易见的东西。
所以下次你写代码时,如果发现自己在想该叫它什么,问自己一个更根本的问题:
**为什么我难以找到名字?**
答案可能会揭示这个事物本不该存在,这就是为什么它没有名字。
---
我把这个论点做成了视频版本,如果你更想看的话:正念编程:目的与意图 (https://www.youtube.com/watch?v=RnhQ4CUqzNY)。
感谢阅读。
相似文章
论代码的意图
作者认为,阅读和编写代码就像讲故事,意图通过显式和隐式的方式传达。他们讨论了在阅读代码时理解作者意图的重要性,并使用了 C++ auto、Python 类型注解和公司编码策略等示例。
软件关乎人,而非代码(2020)
一篇论述软件成功更多取决于理解人及其需求,而非编写完美代码的文章,并以被遗弃的、未解决实际问题的代码库为例。
迈向可理解的软件
本文批判了当前的编程实践和对大语言模型的依赖,反而主张通过更好的抽象、文档和软件栈来使代码更易于理解和维护。
编码即思考:为何我仍坚持手写代码
作者认为手写代码对于培养批判性思维和解决问题的能力至关重要,并警告不要过度依赖AI编程工具。
Martin Fowler:技术债、认知债与意图债
Martin Fowler 反思 AI 对代码质量的影响,指出人类的“懒惰”反而促成清晰抽象,而 LLM 则可能用不必要的复杂性把系统拖胖。