为什么代码中的注释大部分是多余的
一篇关于代码中注释大部分是多余的文章。
这是我最近加入一家新公司,接触到一组代码后得出的结论。这位开发者认为注释代码是前进的方向。我发现的是一套非常冗长且大部分无用的注释,它们充斥着代码,使其极难阅读。还有另一个问题,几乎所有东西都被注释了,除了你真正需要提示的部分!
现在,我可以袖手旁观,说这是一个多么没用的开发者——这些注释是垃圾!不过,这并不公平,除了将所有问题归咎于已离职的开发者完全是徒劳的,而且可能也不真实。你现在可能在阅读代码时遇到困难,但它能存在多年,本身就证明它确实有效。只有当需要进行更改时,你才会突然被推到前台,要求你进行修改。有几年开发经验的每个人都经历过这种情况,即使有优秀的文档,拾起别人的代码也从来都不容易。打个比方,可以帮助我们理解这一点。想象一下,你发现了古埃及的象形文字——你可能知道单个符号的意思,但如果没有上下文和语法规则,很难理解其含义。在我们的例子中,语法就是架构愿景——计划是什么?开发者是如何尝试解决这个问题的?
那么,为什么代码中的注释在很大程度上是无用的呢?好吧,我认为普通开发者在开发过程中会给代码添加注释。在大多数项目中,我们做的绝大多数都是相当标准的,比如套接字、Web界面、数据库连接等等。我们经常做这些事情,所以通常都很容易,我们也感到自信。在每个项目中(除非你非常不幸),总会有一些新的、具有挑战性的东西,会在一定程度上考验你的谷歌搜索/编码技能。现在,当我们处理标准的东西时,我们会自信地频繁地注释代码——我以前做过,我确切地知道我在做什么!这导致我们用很多冗长的文字注释所有非常明显的东西,但当涉及到一些更困难的东西时,我们可能需要尝试几种不同的方案才能正确。一旦单元测试全部通过,就没有多少动力回去记录你的工作了。换句话说,你会得到大量关于应用程序中平凡日常部分的注释,而对于更有趣的领域,注释却很少。换句话说,你会得到很多这样的注释:
//This connects the socket to the appropriate server and port
socket.connect(server, port);
呃,真的感谢你的注释。我永远也想不到!!
那么,这就引出了显而易见的问题——你应该记录什么?这实际上是一个非常困难的问题,已经提出了许多解决方案。现代的敏捷方法论通常会说一些相当平淡的话,比如“尽量少记录,或者只记录你需要的!”或者在极限编程模型中,“文档是一个商业决策,只记录业务要求!”——这些说法听起来很棒,但实际上意义不大。企业通常不知道自己需要什么,并且更不愿意为此付费。
在像瀑布这样的其他方法论中,你经常会遇到“文档=解决方案”类型的经理。这种人会大声疾呼风险登记册和“完整的”文档。文档本身并不能解决任何问题,一旦有人提到风险登记册之类的东西,我的毛都要竖起来了!
我看到记录已知问题或架构债务几乎没有什么意义。你永远不会修复它们,将它们保存在数据库中是相当徒劳的——事实是,当一个系统进入生产环境时,它就代表了你的架构决策——那就是系统,它要么有效,要么无效。
你会注意到我还没有回答这个问题——那么,聪明先生,你应该记录什么呢?嗯,我认为良好的文档来自于一种有纪律的软件开发方法。你应该记录系统应该实现的目标、你如何解决这些问题以及对其支持的关键点。这实际上与软件开发生命周期直接相关。
- 分析和需求收集
- 设计
- 开发
- 系统测试
- 用户验收测试(UAT)
- 部署
你可能采用迭代方法或瀑布方法,但这基本上是在软件开发项目中所做的事情。分析和需求收集非常重要,未来的开发者至少可以看到你原本应该构建什么,即使最终构建的并不完全是那样!
在这里值得一提的是敏捷方法——我非常支持敏捷开发,但敏捷并不意味着在项目方法上不够正式——事实上,许多方法,如 Scrum,都是非常正式的框架。敏捷就是精益——我们不包含不需要的东西,但同样,我们也不会吝啬好东西。列出你希望你做过但可能没时间做的事情,这被称为“管理”,而不是敏捷。好好记录你的系统——这是敏捷、瀑布以及基本上良好的软件开发的重要组成部分。
一旦你有一套需求和适当的分析,开发者的工作就是考虑实现。第一件事就是把你的 IDE 锁在一个黑暗的房间里,带着你的编译器去度一个短暂而应得的城市休假。你现在不应该写代码,你应该以更抽象的方式思考。
设计不仅仅是选择框架——事实上,在设计阶段,说我们将使用某种 ORM 和一个 MVC 框架就足够了。太棒了,现在我们需要考虑的是每个项目都会带来的一点点有趣的东西,所以让我想想——在这个项目中,我们将
- 将一些东西存入数据库
- 从数据库中检索一些东西
- 为管理层绘制一张漂亮的图表
- 根据 PH.D. Dave 刚刚想出的新算法计算宇宙的质量
- 创建一个非常漂亮的进度条
等一下——第四点听起来很有趣。我想我们知道在这个应用程序中应该重点记录什么。
很明显,如果你要完成这个项目,你总得在某个时候写一些代码,但你会注意到我们已经有了分析和需求文档,以及描述我们如何处理这个问题的设计——我希望有一些漂亮的 UML 图。我们已经为未来支持你应用程序的开发者奠定了非常好的基础。
接下来,我们将创建一个与代码紧密相关的文档——没错,就是单元测试!单元测试是一种很好的记录方式,因为它们本质上是代码每个方面的工作示例。新开发者可以逐步运行单元测试,确切地了解你期望它做什么以及你期望的结果是什么。这是很棒的文档,我们不需要花大量精力去写。
我们很快就会进入测试阶段。在测试方面,我们有几种选择:
- 使用专门的测试人员,他们会编写测试脚本(文档)并希望编写一些自动化回归测试。
- 让另一位开发者来测试(最好是讨厌你的人——他们总是能找到更多 bug)或者
- 自己测试(这属于假装测试系统的范畴)。
最后,是 UAT(用户假装测试系统)和部署。好吧,我们现在有一套相当不错的文档,代码中也没有注释(好吧,可能只有几个,但真的不应该到处都是)。我们还需要写些什么?希望很少——一个部署图可能不错,也许还有一些给用户的笔记。
工作完成了,我们有了一套非常有用的文档,对未来的开发者非常有帮助,所以下班去酒吧之前只剩最后一件事了——把它保存在一个偏僻的地方,这样它就永远不会被找到了!