我相信为代码添加注释很重要。最重要的是,我相信注释被误解了。 我前几天在推特上发文 说:“我听到关于是否应该写注释的相互矛盾的意见。但我从初级开发人员那里得到了感谢,因为我写了这些注释,所以我将继续这样做。” 我收到的回复多种多样,但最吸引我的是,对于每个同意注释必要性的人来说,他们都有不同的理由相信这一点。
注释比我们给予它的认可更加微妙。注释没有命名法(不是说应该有),但把所有注释归为一类是一种过度简化。这个漫画中的例子是针对这个回应推文的,这是真的。

我认为许多关于注释的误解都源于此。罗伯特·C·马丁的书 代码整洁之道 谈到了这一点:注释不应该成为必需,因为代码应该是自文档化的。如果你觉得注释是必要的,你应该重写代码使其更易读。我同意也反对这一点。在编写注释的过程中,你经常会发现可以写得更好的东西,但这并不是非此即彼。我仍然可以重写代码使其更具自文档性,也可以编写注释,原因如下。
代码可以描述如何,但它不能解释为什么。
这不是一个新概念,但我注意到它是我遇到的有用注释中常见的主题。能够传达代码无法传达或无法简洁地传达的东西。
综上所述,编写注释没有唯一正确的方式或原因。为了更好地学习,让我们深入研究一些有益的注释**类型**,它们可能都有不同的目的,然后是我们要避免的模式。
好的注释
为什么
许多好的注释示例可以归入此类别。代码解释了你希望计算机采取什么行动。你会听到人们谈论声明式代码,因为它精确地描述了逻辑,但没有像食谱那样描述所有步骤。它让计算机来做繁重的工作。我们也可以将注释写得更具声明性。
/*
We had to write this function because the browser
interprets that everything is a box
*/
这并没有描述它下面的代码将做什么。它没有描述它将采取的操作。但是,如果你发现了一种更优雅的重写此函数的方法,你可以放心地这样做,因为你的代码很可能是用不同方法解决同一问题的方法。
因此,所需的维护更少(我们将在后面更详细地讨论这一点)。如果你发现了一种更好的编写代码的方法,你可能不需要重写注释。你还可以快速了解是否可以重写代码的另一个部分以使此函数不再必要,而无需花费很长时间来解析所有步骤以构成整体。
澄清普通人无法理解的东西
当你看到一行很长的正则表达式时,你能立即理解它在做什么吗?如果你能做到,你属于少数人,即使你现在能做到,你可能明年也做不到。浏览器黑客呢?你有没有在代码中见过这样的代码?
.selector { [;property: value;]; }
还有
var isFF = /a/[-1]=='a';
第一个针对 Chrome ≤ 28、Safari ≤ 7、Opera ≥ 14,第二个针对 Firefox 2-3 版本。我曾经写过需要类似代码的代码。为了避免另一个维护人员或未来的我假设我那天在去上班前服用了一些鼠尾草,向人们说明这些代码的用途非常重要。尤其是在为不再需要支持该浏览器或浏览器错误已修复并可以将其删除的时候做好准备。
对你来说清晰易读的东西并不一定对其他人来说也是如此
谁聪明?我们是!谁写代码整洁?我们!我们不需要注释,看看它有多清晰。这种思维方式的问题是,我们每个人在不同的领域都有更深的知识。在团队规模小、成员技能和专业知识更像一个圆形而不是韦恩图的团队中,这比团队规模大、经常更换团队成员或拥有初级开发人员或实习生的团队来说问题更小。但我可能仍然会为这些新人或未来的自己留出空间。在团队规模较大、拥有初级工程师或来自各种背景的工程师的团队中,人们可能不会直接告诉你他们需要你添加注释,但许多人也会在你这样做时表达感谢。
像书的章节一样的注释
如果这篇文章本身是一大块而不是分成几节,并用空白和较小的标题分开,那么浏览起来会更加困难。也许我所说的一切并不都适用于你。注释部分或片段可以让用户跳到与他们最相关的部分。但是,你说。我们现在有了函数式编程、导入和模块。
没错!我们将事物分解成更小的部分,使其更易于管理,感谢上帝。但是,即使在代码的较小部分中,你也会不可避免地遇到必须稍微长一点的部分。能够快速了解相关内容或标记一个略有不同的区域可以提高生产力。
编写代码时保持逻辑清晰的指南
这很有趣!这些不是你保留的注释,因此也可能出现在“不良模式”部分。很多时候,当我处理一个包含很多活动部分的大型项目时,将事物分解成我将要采取的操作非常有用。这可能看起来像
// get the request from the server and give an error if it failed
// do x thing with that request
// format the data like so
然后我可以轻松地一次专注于一件事。但是,如果这些注释像现在这样保留在代码中,以后阅读起来就会很麻烦。在编写代码时它们非常有用,但一旦完成,它们就可能仅仅是对代码所做事情的重复,迫使读者以两种不同的方式阅读相同的内容。不过,这并不能降低它们编写时的价值。
我的理想世界建议是,在编写时使用这些注释,然后在之后重新审阅它们。当你删除它们时,你可以问自己:“这是否以最优雅和易读的方式实现了?”“是否有其他注释可以代替它,解释为什么这很有必要?”“我认为对未来的自己或来自另一个母亲的人来说,最有用的是什么?”
可以重构
你有没有遇到过非常紧迫的产品发布期限?也许你实现了一个你自己不认同的功能,或者他们告诉你它只是“临时的”,“只是一个 A/B 测试,所以无关紧要”。*恐怖音乐响起*……然后它就一直存在……永远……
虽然这可能令人尴尬,但编写这样的注释
// this isn't my best work, we had to get it in by the deadline
相当有用。作为一个维护人员,当我遇到这样的注释时,我会节省大量时间来试图弄清楚这个人到底怎么了,并想象如何破坏他们的早上通勤。我会立即停止试图弄清楚这段代码中哪些部分应该保留,而是专注于哪些部分可以重构。我唯一要提醒的是,不要让这种类型的编码成为你的备用选择(我们将在后面详细讨论这一点)。
注释作为教学工具
你是否是一个 PHP 公司,刚接手了一个完全使用 Ruby 的客户?也许是标准的 Ruby 代码,但你的团队有点吃力。你是否正在为某人写教程?这些只是一些写出“如何”可能会有帮助的有限例子。这个人实际上是在现场学习,可能无法仅仅从代码中推断出代码的作用,因为他们以前从未见过。请注释掉这些代码。学习本来就很令人谦卑,不要让他们不得不大声问你他们可以更容易地自己学习的东西。
我从 Stack Overflow 上复制了这部分代码
你是否刚刚从 Stack Overflow 上复制粘贴了一整块代码并修改它以满足你的需求?这不是一个好习惯,但我们都经历过。我以前做过的一件事,它救了我,就是把找到它的帖子的链接放进去。但是!然后我们就没有功劳了!你可能会说。我会说,你把重点放在了错误的地方。
不可避免地,人们有不同的编码风格,解决方案的作者以一种与你不同的方式解决了问题,如果你更了解这个领域,你会以另一种方式解决问题。为什么这很重要?因为以后你可能会更聪明。你可能会在这个领域升级,然后你会花更少的时间挠头,想知道为什么你这样写,或者从别人的方法中学习。此外,你还可以随时回顾那篇文章,看看是否有新的回复能更清楚地说明这个问题。甚至以后可能会出现另一个更好的答案。
糟糕的注释
编写注释有时会受到批评,这是因为确实存在糟糕的注释。让我们谈谈编写注释时要避免的一些事情。
它们只是说明代码的功能
John Papa 开了一个准确的玩笑,说这个
// if foo equals bar ...
If (foo === bar) {
} // end if
很痛苦。为什么?因为你实际上是用两种不同的方式阅读了所有内容两次。它没有提供更多信息,事实上,它让你必须以两种不同的格式处理事物,这是一种精神上的开销,而不是有帮助的。我们都写过这样的注释。也许是因为我们自己没有完全理解它,或者我们太担心以后阅读它。无论出于什么原因,退一步,尝试从阅读代码的角度而不是从你作为作者的角度来看待代码和注释,总是有好处的,如果你能做到的话。
它没有得到维护
糟糕的文档可能比没有文档更糟糕。没有什么比遇到一大块代码更令人沮丧了,注释的内容与下面的内容完全不同。它比浪费时间更糟,它会误导人。
解决这个问题的一种方法是确保你更新的任何代码,你也要维护注释。当然,只有更少和更有意义的注释会使这种维护工作不那么繁重。但注释和维护注释都是工程师的工作。**注释在你的代码中,你应该负责它,即使这意味着删除它。**
如果你的注释一开始质量很高,并且表达的是原因而不是方法,你可能会发现这个问题会自行解决。例如,如果我写
// we need to FLIP this animation to be more performant in every browser
然后重构这段代码,从使用 `getBoundingClientRect()` 改为使用 `getBBox()`,注释仍然适用。函数存在的目的是相同的,但如何实现的细节已经改变了。
你可以使用一个更好的名字
我确实见过有人写代码(或者我自己也这样做过),其中变量或函数名称只有一个字母,然后注释说明是什么。这是浪费。我们都讨厌打字,但如果你重复使用一个变量或函数名,我不想扫描整个文档,看看你解释了这个名字本身可以做什么。我知道,命名很难。但有些注释代替了可以很容易地写得更精确的东西。
注释是写不出更好的代码的借口
对于很多人来说,这就是问题的关键所在。如果你写的代码是杂乱无章的,并且依赖注释来澄清,这意味着注释阻碍了你的编程。这是一种本末倒置的情况。不幸的是,即使是作者也不容易判断哪种情况是哪种情况。
我们以无数种方式欺骗自己。我们可能会花时间写注释,而这些时间本来可以用来从一开始就使代码更简洁。我们也可能会告诉自己,我们不需要注释代码,因为我们的代码写得很好,即使其他人可能不赞同。
两个方向上都有懒惰的支柱。尽力而为。尝试不要依赖一种正确的方式,而是写代码,然后阅读它。尝试想象你自己既是作者又是维护者,或者这段代码对你来说可能是什么样的。你需要什么信息才能尽可能高效地工作?
人们往往会站在“是否应该写注释”的一边或另一边,但我认为这种讨论不够细致。希望为如何写有意义的注释打开一个更深入的讨论,可以弥合差距。
即使如此,它也可能有很多东西要解析。哈哈,明白了吗?无论如何,我将留给你一些(更好)的幽默。前段时间,Stack Overflow 上有一篇关于人们写过或见过最好的注释的文章。你绝对可以在里面浪费一些时间。 非常有趣.
完全同意。注释不仅仅告诉你 _什么_,它还可以告诉你 _为什么_,而这在很多时候 _非常_ 重要。我无法告诉你,有多少次因为我看到了一些在我看来很愚蠢的代码,但实际上是必要的,以满足我甚至不知道可能存在的某些模糊的边缘情况而打破了东西。那里一个 _为什么_ 注释会很棒。
我喜欢看到代码指定函数或 html 元素何时结束 - 就像 //end {function name} 或者
它使导航嵌套的 html 元素或函数变得容易得多...... 如果你必须导航一个没有良好注释的基于表格的电子邮件,上帝保佑你。
我理解为什么这似乎有帮助。然而,根据我个人的经验,添加一个 //end 会让人们在信任注释是否正确时产生一种错误的安全感。如果 //end 放在错误的位置,它会导致以后出现问题。
代码中有缩进是有原因的。
不错的文章,但我不同意 `} // end if` 不好。如果你有多个嵌套的大型结构,不用向上滚动 1-2 个屏幕就能知道你在这里关闭了什么,这确实有助于你在代码中找到方向。
如果你有如此大的结构,以至于你不能在一个屏幕上看到它们,那么也许是时候重构了。
这是一篇很棒的文章,它提到了我认为很重要的事情。我要说的是,我个人在使用 Amazon MWS API 的一个类的开头写了一些发泄式的评论,我认为这些评论和对我来说有治疗作用一样有用。
感谢您撰写此文!我完全同意注释对于解释代码本身无法解释的部分是必要的。它只是为以后的你和其他人节省了时间。
首先,我**不是**一名程序员(尽管我在 CSS 和 XSLT 上做了不少工作)。很棒的文章(我喜欢里面的漫画)。我只想说,对我来说,最有可能阅读我评论的人是我自己六个月后的我。我无法告诉你,我有多常看着自己的代码却完全不知道它在做什么。我写注释写得还算不错,但是对于 CSS 来说,大部分代码都是非常分层的。所有东西都在其他东西下面,等等。我已经到了把层次结构和解释性注释复制到一个两列的 MS Word 文档中方便参考的地步了。
在我工作的地方,我们基本上被禁止写注释,因为我们遵循你提到的那本书。我已经看到了**很多次**,注释本可以节省**很多时间**。
我经常听到这句话:“我知道我们这么做是有原因的,但我就是记不起来原因了”。
好文章!:) 我在 Twitter 上发帖的时候首先提到了这个漫画(而且没有多少解释 - 对不起!),我应该详细说明它对我有什么作用。我并不特别同意或反对这个观点,但我写代码的时候确实会考虑到它。最重要的是,它是一个俏皮的提醒,让我在写代码的时候为任何可能后续使用的人着想,无论是未来一两个月的我,还是接手我放弃项目的其他人。我写一个注释,然后问自己:“我是否把桥梁叫做桥梁?我有没有做任何事情来传达桥梁的来源或它可能要去的地方?” 这就是所有,但我在 Twitter 上解释起来有点太长了。至少要等到我有了 280 个字。
无论何时想要写注释,都应该改写你的代码。
如果你自己做不到,那就找人帮忙,学习并成长为一名开发者。
如果你担心你的代码不够清晰,可以找人做同行评审,讨论一下,如果觉得有帮助就改写代码。
如果你想发泄情绪或在代码里表达个人意见……那就永远不要这么做。别再被动攻击了。
链接到 SO 也可以提供上下文,如果你对 C/P 代码做了更改。
是的,我遇到过我自己写的代码和第三方库,它们链接到 SO 文章或回复,解释了为什么注释后会出现令人费解的代码块。
感谢你的文章!我同意你写的大部分内容。
有没有可能写一篇关于在团队合作中如何写出有用的提交信息的后续文章?
关于过去几年一直争论不休的话题,这篇文章写得很好。
Sarah,写得很好,很棒的文章。