
通用代码技术
2023/02/05阅读:32主题:雁栖湖
为什么你应该减少代码注释?
说一个违反直觉的建议:
在大多数时候,你不应该在你的代码中写注释。
没错,如今现代编程语言提供了许许多多的富有表现力和抽象力的语言工具和特性。
使用这些特性,并对代码进行可读性重构,远比增加大量的代码注释要好得多。
下面看几个例子。
Part1去除注释,增加表现力
1简单例子
int status == 5:
message.markSend()
查看这段代码,并不能直接看出来 5
是什么含义,我们可以添加一条注释来说明 5
代表什么:
# A status of 5 signals message sent
int status == 5:
message.markSend()
但是更好的做法是,我们可以创建一个具有字面含义的常量来代替这个数字 5
. 如下:
MESSAGE_SENT = 5
int status == MESSAGE_SENT:
message.markSend()
在更复杂的例子中,应该思考其是否可以简化或者重构代码本身,使其变得更易理解。
2复杂例子
# 如果当前用户是这段信息的作者,
# 并且这段信息是五分钟前发表的,你可以更新这段信息。
# 或者当前用户是 超级管理员,那么你也可以更新这段信息,
# 并且在这段信息未发表之前你也可以修改。
if (message.user.id == current_user.id and (
message.deliverd_time() is None or (datetime.now() - message.delivered_time()).seconds < 300
)) or current_user.type == User.Administrator:
message.update_text(text);
这段代码我们添加了足够多的注释来解释它,但是我们可以通过使用变量来命名表达式的各个部分以此来简化代码。
FIVE_MINUTES = 5 * 60
user_is_author = message.user.id == current_user.id
user_is_admin = current_user.type == User.Administrator
is_recent = message.deliverd_time() is None or (datetime.now() - message.delivered_time()).seconds < FIVE_MINUTES
if (user_is_author and is_recent) or user_is_admin:
message.update_text(text);
此时我们可以发现,if
条件读起来就像在读上面的注释一样清晰。所以我们可以去掉上述注释。
当条件足够复杂的时候,你还可以考虑将整个条件单独创建为一个函数:
def can_edit_message(current_user, message):
FIVE_MINUTES = 5 * 60
user_is_author = message.user.id == current_user.id
user_is_admin = current_user.type == User.Administrator
is_recent = message.deliverd_time() is None or (datetime.now() - message.delivered_time()).seconds < FIVE_MINUTES
return (user_is_author and is_recent) or user_is_admin
if can_edit_message(current_user, message):
message.update_text(text);
Oh My God! 一切都是如此的清晰。

Part2类型也可以减少注释
现代语言都具备了丰富的类型系统,增强了语言自身表现能力,从而减少了注释。
// Get the delivered timestamp;
//
// A value of -1 is returned if the
// message has not been delivered.
int64_t delivered_timestamp();
上述代码表示,如果 delivered_timestamp()
出错,那么它将返回 -1
指代错误状态.
这在 Linux 编程中随处可见,因为 linux 内核主要使用了 C 语言进行编写,而 C 语言只有简单的类型系统。
但是在各种现代语言中,增加了一种类似 optional
的类型,它表示可能的含义,即可能是正常的值,可能会错误值。例如在 C++ 17 中新增了 std::optional
std::optional<int64_t> delivered_timestamp();
这样在面对多返回值的函数时,就不需要再单独处理另一种错误情况,std::optional<T>
中就有一个 bool 值来指代函数是否错误。
而且正常情况下 std::optional<T>
类型的变量仍可以当作 T
类型来使用。这样就减少了更多不必要的注释。
Part3注释的弊端
注释并不是越多越好。
注释也会像代码一样出错
当人们对代码进行修改的时候,他们通常不会更新相对应的注释。我们有各种工具来检查代码的错误,例如测试,编译器检查,但是我们没有任何系统来检查注释的错误。
或许有一天 AI 的发展会出现这样的工具,来检查代码是否与注释相匹配,但是那时候程序员这个职业也许就不复存在了。但是不可否认的一点是:我们并不相信注释。
注释可以说谎,但代码不会。
Part4代码文档
代码文档表述了系统的高级架构和公共 API,代码文档不同于注释。
注释表述了代码工作原理的内部结构,也就是代码是如何工作的;
而代码文档表述了如何使用代码。
世界需要更多高质量的代码文档,但不需要更多的代码注释。
Better Code = Better Documentation
Part5应该写注释的几种情况
3性能因素
如果代码因为性能原因做出了难以理解的行为,那么它需要一个注释来说明这么做的原因。

4使用了数学公式或算法
如果代码使用了特定的数学原理或者特别的算法,你应该在注释中说明其来源。
例如在 Cpython 的源码[2] 中:

Part6结语
与代码注释相比,代码是表达意图的最好方式。
Code > Comments
所以总的来说,如果你需要使用人类语言来解释你的代码,看看你是否可以让你的代码更加人性化,更加易读。
参考资料
std::optional
Cpython 的源码: https://github.com/python/cpython/blob/main/Lib/datetime.py#L1178
作者介绍

通用代码技术
公众号: 通用代码技术