现在的项目开发里,代码注释就像程序员的头发,越来越少。

尤其是国内,这种现象不仅是在小公司小团队中司空见惯,就算在大公司,以及大团队中的开源项目里,也是屡见不鲜。

上图是我在阿里的 Druid 项目源码里截的。DruidDataSource 是 Druid 重度使用的核心类,非常关键,可是哪怕这种关键的核心类,也见不到什么注释。

没有注释对我们读代码带来了很多的不便之处。就像扔给你一个数码产品,上面堆叠着密密麻麻的功能按键,但是却没有给你说明书。

那为什么代码注释消失了呢?

我尝试总结一下原因:

1. 国内程序员的职业环境对加注释不友好

在国内这种环境里,程序员们每天在苦闷的 996 中挣扎,各种大活小活不断地做着,正常写代码都忙得不可开交,加注释更是进一步提升了工作量,没人喜欢自己给自己加工作量的。

咱们想想,在费劲巴拉地写完一大堆代码之后,经过反复自测修改之后,好不容易调通了,脑子已经晕乎乎的了,你此时会有多大心思去写这段注释呢?

又再想想,你可能想着要给代码加注释呢,突然这边产品拉你开会,又或者那边运营告诉你,需求变了,刚写好的代码还得再改改……此时,你还有给代码加注释的念头吗?

另外,注释这事儿,写好了是很费精力的。一般来说,一段好的注释,要能在有限的行数之内说明出:被它注释的代码到底做了什么,是个怎样的概念以及为什么会写这段代码

写注释麻烦不说,关键是注释还不算咱们程序员的工作量。

程序员的工作是把业务用程序实现,工作结果里不看你注释了多少代码,也不看你注释写的好还是坏,只看你的程序是不是写完了,满足了需求没有,会不会上线出什么问题。

至于注释,它滚出了程序员兄弟们的 KPI。有多少公司能像 Google 那样去 Review 代码的?BAT 有一个算一个,都差点意思。

所以,国内程序员们糟糕的环境,是代码注释少的首要原因。

2. 看待注释的方式出现了变化

Java 是一门面向对象的语言,从它出世以来,业界就不断地为 Java 制定了数不清的规范。

在 2008 年,集这些规范于大成的《Clean Code》—— 中文名叫《代码整洁之道》这本书出现了。

在《代码整洁之道》中有个理念就是,注释是为了弥补代码表达能力不足的一种不得已的做法。如果代码能表达清楚,那就没必要写注释。

甚至,这本书的作者认为写注释都需要用 failure 这个词来形容,也就是说,如果你写了注释,那就说明你的代码不够好,你写好代码的努力失败了。

这个理念在业界也被不少大牛们认可了。所以,后面就有越来越多的人认为:代码写的够好,就不用写注释了。

如果大家有空,可以去看看《代码整洁之道》的第四章,里面详细说明了这种如今被业界不少人接受的关于注释的理念。

所以,“好代码不需要注释”这种观点也是造成注释少的一个原因。

3. 注释没有规范,导致质量参差不齐

很多团队里,是没有注释规范的。对怎么注释,在哪里注释没有任何规定,随意程序员们自由发挥。

这就麻烦了,注释一旦写了,它就很关键了。因为

错误的注释,比没写注释还祸害人

注释写的很差,那不仅没起到注释本应该起到帮助读代码的作用,反而还可能影响读代码,甚至还能把人带坑里。

如果没有注释规范,往往经常就会出现有人做的好有人做的差的情况。

比如,有人到处加注释,i = i + 1; //把i加1连这种简单代码都恨不得加注释,这就有点脱裤子放屁了。

还有的人写注释了,但是需求变了,代码改了之后,注释懒得改了。又或者是改代码的人不是原作者,新人改完之后压根就没意识到要改注释。

所以,如果没有规范,很多程序员对注释没有什么正确的概念,没写好注释由此还引来了埋怨……久而久之,就没人爱干加注释这件事了。

到底应该怎么写注释呢?

谈了那么多不写注释的原因,这里也想说明一下我对注释的观点。

我个人并不怎么赞同《代码整洁之道》对注释的观点,我自己读有好注释的代码,直接就省了五成以上的力气。有好注释的代码读起来,就像常年脑血栓,一朝被皮搋子打通了一样,那叫一个顺畅。

比如,看看 Netty 的注释:

/*** A nexus to a network socket or a component which is capable of I/O* operations such as read, write, connect, and bind.* <p>* A channel provides a user:* <ul>* <li>the current state of the channel (e.g. is it open? is it connected?),</li>* <li>the {@linkplain ChannelConfig configuration parameters} of the channel (e.g. receive buffer size),</li>* <li>the I/O operations that the channel supports (e.g. read, write, connect, and bind), and</li>* <li>the {@link ChannelPipeline} which handles all I/O events and requests*     associated with the channel.</li>* </ul>** <h3>All I/O operations are asynchronous.</h3>* <p>* All I/O operations in Netty are asynchronous.  It means any I/O calls will* return immediately with no guarantee that the requested I/O operation has* been completed at the end of the call.  Instead, you will be returned with* a {@link ChannelFuture} instance which will notify you when the requested I/O* operation has succeeded, failed, or canceled.** <h3>Channels are hierarchical</h3>* <p>* A {@link Channel} can have a {@linkplain #parent() parent} depending on* how it was created.  For instance, a {@link SocketChannel}, that was accepted* by {@link ServerSocketChannel}, will return the {@link ServerSocketChannel}* as its parent on {@link #parent()}.* <p>* The semantics of the hierarchical structure depends on the transport* implementation where the {@link Channel} belongs to.  For example, you could* write a new {@link Channel} implementation that creates the sub-channels that* share one socket connection, as <a href="http://beepcore.org/">BEEP</a> and* <a href="http://en.wikipedia.org/wiki/Secure_Shell">SSH</a> do.** <h3>Downcast to access transport-specific operations</h3>* <p>* Some transports exposes additional operations that is specific to the* transport.  Down-cast the {@link Channel} to sub-type to invoke such* operations.  For example, with the old I/O datagram transport, multicast* join / leave operations are provided by {@link DatagramChannel}.** <h3>Release resources</h3>* <p>* It is important to call {@link #close()} or {@link #close(ChannelPromise)} to release all* resources once you are done with the {@link Channel}. This ensures all resources are* released in a proper way, i.e. filehandles.*/
public interface Channel extends AttributeMap, Comparable<Channel> {

Channel 是 Netty 里非常核心的一个接口,你直接看注释,一下子就能理解了 Netty 为啥搞出个 Channel 类来,Channel 类你可以怎么玩儿,这些 Netty 在注释给你说得清清楚楚、明明白白。

所以,我觉得注释一定是要的,只是需要有个标准,也要有个度。

从实践上看,我们团队有这么几个必须加注释的标准:

1. 复杂的业务逻辑

业务逻辑关联太多的东西又或者步骤非常多,更或者两者兼有,那么就很少有人会去耐心仔细的去一行一行的把整个代码全部读通理顺。

这时候,必须在业务逻辑实现的相关类中,把类在业务逻辑实现中是个什么成分,为什么这么设计类,以及对应的业务逻辑都要讲清楚。并且重构代码后,注释也必须跟着重构。

2. 晦涩的算法

算法也要加上注释的,尤其那些深奥的算法。大家不可能都是算法专家,能一下子就通过代码理解到算法实现的真谛。所以,这里也要加上注释,一般是说明这是用了个什么算法,这套算法的出处或者附上相关文章的引用地址。

3. 非常规的写法

非常规的写法往往是有特殊情况,不得已为之的。比如,为了得到更好的性能;又比如,为了修复一个 bug,却不想对代码进行大改动。

总之,非常规的写法就是反模式、反套路的,有时候甚至会违反程序员的直觉。像这些做法,必须在注释中写明这样实现的原因。

4. 可能有坑却暂时没太好解决办法

有些时候,需求出的够难够复杂,时间上催的又很急,你根本没办法马上想到特别好的办法去实现。只能临时想个简单粗暴的方案,先凑活着。甚至还会在某些地方,把一些变量的值写死先去把本期的需求实现了。

像这种就很可能就会给后面挖坑了。这时候,注释必须加上为什么要这么解决的原因,还必须加上 //TODO 这类的,表示后面需要进行进一步的修改。

5. 关于项目核心的接口、类和字段

做项目的时候,需求中的很多核心概念很可能会被映射到对应的接口或者实体类上,如果在这些核心接口和实体类加上清楚的注释,写明对应的业务概念,那么,后面再维护项目的时候,真的是事半功倍。

比如,我们在一套批量调度系统里,可能有多种任务的概念,有需要限定执行时间的任务,也有不需要限定执行时间的任务,那么实现上,就可能有个 LimitedTimeTask 类对应限定时间的任务,还有个 UnLimitedTimeTask 类去对应不需要限定执行时间的任务。那这两个类就必须加上注解,写清楚对应的业务概念。

如果特定概念是复合的,是由多个小概念构成,却必须用一个接口或者一个类来表示,那很可能实现上,就还得用字段去映射这些小概念,那么这些字段也得加上注释说明起对应的概念。

总之,注释我个人理解必须要有,但是不可能太泛滥,必须有节制、有规范的加。

最后,咱们说白了,我对注释的态度就是,和写代码一样,要有规范。

在这里和管理者说一句,如果你希望大家写好注释,不能就靠一句“必须写注释”这么高高在上的话去要求大家。没有规范,你就不能完全怪程序员不加注释了。

最最后

对项目、领导的吐槽,可以写到注释里!

为什么越来越多的程序员不写注释?注释应该怎么写?相关推荐

  1. 为什么越来越多的程序员开始学机器学习的原因

    越来越多的程序员开始学习机器学习了,看了本文,也许解释了为什么? 一.前言 程序员容易掉头发,而且,头发的多少似乎跟能力成反比: 1.PHP PHP之父,Rasmus Lerdorf 2.Java J ...

  2. 转载:韩卫平--程序员们,你愿意维护别人写的“烂”代码么

    韩卫平--程序员们,你愿意维护别人写的"烂"代码么? http://blog.csdn.net/akirya/archive/2009/03/11/3982139.aspx 程序员 ...

  3. 为什么越来越多的程序员开始学机器学习?揭秘一个重要原因

    越来越多的程序员开始学习机器学习了,看了本文,也许解释了为什么? 一.前言 程序员容易掉头发,而且,头发的多少似乎跟能力成反比: 1.PHP PHP之父,Rasmus Lerdorf 2.Java J ...

  4. 10分钟学计算机,电脑运行越来越慢?程序员大牛10分钟教你学会电脑瘦身

    原标题:电脑运行越来越慢?程序员大牛10分钟教你学会电脑瘦身 你的电脑是不是越来越慢?这里让程序员大佬用10分钟时间教你学会给电脑软件瘦身,1分钟了解计算机硬件升级.分分钟让你成为别人眼中的计算机大牛 ...

  5. 2019年大厂面试题合集:Java架构师技术栈为什么竞争越来越激烈?程序员必看!

    2019年大厂面试题合集:Java架构师技术栈为什么竞争越来越激烈?程序员必看! 就今年大环境来看,跳槽成功的难度比往年高很多,一个明显的感受:今年的Java技术栈面试,无论一面还是二面,都特别考验J ...

  6. 程序员坐牢了,继续被安排写代码。。

    今天给大家分享一篇有意思的爽文,但也是根据多年之前一个真实报道改编而来的.本文字数较多,建议先收藏,上下班路上.带薪上厕所.浑水摸鱼时再慢慢看~ 本故事纯属虚构 请大家不要随意模仿,后果自负! -  ...

  7. Java程序员的求职面试简历应该怎么写?Java常用框架有哪些?

    [Java程序员]的求职面试简历应该怎么写?首先要做到信息的完整,比如基本信息.求职意向.工作经历/项目经验.个人技能这几大简历版块一定得有.然后简历内容要做到简单明了,详略得当.即要求大家简单概括自 ...

  8. 史上最良心程序员,在代码注释里,告诉这家公司有多坑

    程序员压力大,需要一个地方发泄,可又不能因此断了思路,于是代码注释成了绝佳的地方. 去年虾米音乐APP被爆出,代码注释中含有歧视侮辱性的词汇,将活动赠送的vip,标注为穷xvip.事件一曝光,就受到广 ...

  9. AI 技术越来越平民化,程序员不想被淘汰该怎么办?

    不久前,B站上独立游戏开发者大谷上传的一则"修复100年前老北京影像"视频带火了一项技术--AI修复.通过AI技术手段,100年前的老旧黑白影像变得更加清晰,还原了彩色世界,让人一 ...

  10. 临近年关,为何越来越多的程序员不愿回家,带你盘点那些程序员最怕的几件事

    接近年关,该回家的就回家了,虽然现在科技发达了,程序员已经不再是一个陌生的行业,但在很多地方的农村,对程序员还是有很多的误解,很多程序员过年回趟家就会被折腾的再也不想回去,每次回来都感觉无力吐槽,而最 ...

最新文章

  1. Normalization 的发展历程
  2. R语言lm函数拟合多项式回归模型、删除数据中的异常样本outlier、之后诊断模型( diagnostics)、使用plot函数打印回归模型的QQ图、残差拟合图、标度-位置图、残差与杠杆关系图
  3. 百万级数据库优化方案
  4. 【机器学习算法专题(蓄力计划)】十、机器学习中必备的高等数学和线性代数基础
  5. LQ训练营(C++)学习笔记_动态规划入门
  6. 前端学习(3286):Aop
  7. Flutter高内聚组件怎么做?阿里闲鱼打造开源高效方案!
  8. base href= php,如何正确定义项目下的base href(页面中所有相对链接的基准URL)...
  9. android获取工程中所有类名,android 获取手机的所有程序和widget的包名和启动类名...
  10. javascript数据结构——栈
  11. CAM350 V14.6 检查gerber文件
  12. 电脑小米手机,小米手机怎么连接电脑?
  13. 物联网卡的6个应用案例全面解析
  14. (已解决)windows2020卸载office2013(安装程序包语言不受系统支持)
  15. 微信二维码无法下载APK解决方案
  16. iPhone12、iPhone12 Pro、iPhone12 Max、iPhone12 Pro Max 详细参数配置
  17. 8in1模拟器v2模拟飞行_重新想象飞行模拟器:过去和现在
  18. 如何注册我的世界服务器账号密码,我的世界电脑服务器怎么注册登录密码
  19. 大数据 杨栋_大数据专家杨栋谈如何助高考考生事半功倍
  20. 一次调频二次调频matlab仿真,一种改进型VSG二次调频控制器及控制方法与流程

热门文章

  1. What‘s next for AlphaFold and the AI protein-folding revolution / 什么是AlphaFold和AI蛋白质折叠革命的下一步?
  2. Web安全之常见面试题总结
  3. java 临时文件目录_在Java中使用临时文件/文件夹
  4. Spring实例(DI注入)——女生追男生
  5. pg PostGIS教程:几何图形(geometry)
  6. net core 微服务 快速开发框架 Viper 初体验
  7. httprunner 3.x学习10 - parameters 参数化
  8. 【华为机试真题 JAVA】统计射击比赛成绩-100
  9. linux服务器告警信息:Free inodes is less than xx% on /volume 排查
  10. Pocket PC访问PC上的Webservice