“There are only two hard things in Computer Science: cache invalidation and naming things.”

Phil Karlton

在计算机领域只有两件艰难的事情:缓存失效和对象命名。

这还真不是一个笑话。写代码是比较容易的事情,但是阅读别人的代码,那就因人而异了。

好的工程师写出来的代码可读性很高,比如我上家公司的同事旭总。一般的工程师写出来的代码就像是一坨屎,比如之前某某几位同事。

所以我会经常去格式化他们的代码。如果不幸轮到你继续在屎代码上面开发,那就是屎上堆屎了。心疼你。

当然有时候工期紧张,我自己也会写一些屎代码shit code,但是每次提交的时候都有一种强烈的愧疚感。希望这些代码最多存活一个月就消失,不要被人踩到了。

屎代码是怎么产生的?

要说怎么写屎代码,这个我就很拿手了。下面随便列举一些常见的屎代码产生方式:

  1. 看不懂的命名
  2. 过长的类|函数
  3. 大段重复的代码
  4. 没有注释的Magic number
  5. 100多个参数的函数
  6. 一堆没有注释的if-else嵌套
  7. 业务过度耦合:支付订单和点餐订单能耦合在一起?谁重构谁痛苦
  8. 代码和文档分离:几年前的业务完全不知道是个啥

很不幸,大多数人的项目中,这些常见的屎代码产生方式是随处可见的。

毕竟,几百个人写屎代码,就像几百个人堆积木。堆得歪歪扭扭,摇摇晃晃,乱七八糟,你千万不能抽里面的积木,指不定抽了一块就塌了。只能看见哪里觉得不牢靠不停的往那边填积木。只要不倒就好了。这也是大部分程序员的追求了。

不写屎代码从命名规范开始

如果想解决代码高耦合这坨屎,需要有比较好的顶层解耦设计,划分清楚各自服务的边界。

如果你一直都只是代码搬运工,传说中的cv工程师,那么到这一步还有一些内功需要你去修炼的。

但是好的命名规范确实每个人都可以做到的。不同的语言都有各自的规范,如果所有人都能正确理解那些规范,并且严格遵守,同时强制使用ci校验,就可以保证代码外表上是美观的。

这里就以Go语言的命名规范为例讲一下怎么写出人人都想闻的香代码。

go fmt可以统一不同人的编码规范,却没有办法格式化出一个好的命名。但是在Go社区中其实一直都存在着一些成文的或者不成文的命名规则,比如:

  1. 某个名称在包外是否可见,就取决于其首个字符是否为大写字母
  2. 使用驼峰命名而不是下划线
  3. 单个方法的接口名称应该是InterfaceName = MethodName + er
  4. Getter方法的命名不需要包含Get,比如cat.Owner()方法不需要命名成cat.GetOwner()
  5. 首字母缩写词应该保持原有格式:应该使用userID而不是userId,应该使用userAPI而不是userApi
  6. 变量名需要尽可能的简单但是又能描述清楚

总之,规则是有的。只是很多程序员选择直接忽视。比如前面的五条。

还有一些规则是被过渡滥用了。比如第6条,很多同事的命名沿用以前的老风格,在一个struct中大量使用单字符的的变量或者随心所欲的缩写。这样的代码是完全没有办法阅读的。所谓有追求的程序员,还是得追求一下代码的品味。

悲观的说,即使做到了这些也只是你一个人的代码是优雅的,但是你怎么能保证所有人都有这样的追求呢?作为个人,除了提建议之外,其实是没有太多有效的办法的。

怎么才能根治屎上堆屎?

想根治这个问题,只靠某个程序员一直保持优雅代码是没什么用的。

你写10句优雅的代码,其他10个同事每个人都写10句屎代码。这样算起来,优雅代码的比例最多只有十分之一。

如果一个团队想要彻底解决屎上堆屎这个老大难问题,就需要贯彻执行下面两点方法:

  1. 招高质量的程序员:code sense很重要,每个程序员都需要懂得奥卡姆剃刀原理:若无必要,勿增实体。
  2. 管理层需要有长远的视野而不仅仅是短期目标。

深度悲观的说,这两个方法真正执行起来的时候也是难度重重,基本不可能完成。

高质量无论是不是在互联网行业,都意味着价格昂贵,但又有几家公司能给得雇佣的起这么多昂贵的程序员呢?

而长远的目标在资本的压力之下,也显得一文不值。代码规范提升10%的重要性和“明天上线”这个命令比起来,也是低到尘埃的的。然后日复一日,明天又将是明天,规范性最终消失殆尽,屎山越来越高。

说到底,这些都是钱的问题,也是最无解的问题,最终在某一天,屎山崩溃,一切回到原点。

最后,大胆猜测一下,昨天的Google服务崩溃也是因为屎太多了吧。