企业级应用与屎一样的代码

最近在部门内组织了一场撕逼大会,几个 golang 组坐在一起聊一聊未来的应用应该怎么写。感觉收获颇丰。

在企业内部做开发的时候,实际上 99% 的程序员都不太在意代码的质量问题。这个比例我没有任何夸大,从上一家公司出来(倒闭)来到滴滴,又转过一次部门。经手了很多系统,个个也都是国内顶级公司(BAT)来的程序员们,但是代码写出来个顶个的垃圾。不管是维护还是阅读,都非常得痛苦。

一年多以来我一直在思考原因,刚开始的时候想法很极端,认为这些程序员就是基础不过关。但是在和很多人沟通的过程中发现,实际上大部分人都还比较聪明,并且务实而理智,分得清优劣,而不像是我刚来的时候想像的那个样子。

那为什么最终的结果却是写出了无数的垃圾代码呢?又经过了一段时间的思考,我大概总结出以下一些原因:

1.项目工期紧,上午提需求下午就要上线

2.需求变化频繁,朝令夕改

3.PM 素质差,没有想清楚问题就提需求,导致没完没了的返工让 RD 失去对项目的耐心和精雕细琢的追求

4.没有重构和优秀代码的概念

5.这个月做的项目下个月就跟我说没用了,其它部门已经做完了

6.懒

没想到列完了以后发现有不少是公司政治的原因哈哈。。不过我们不谈政治,不谈政治。

主要来说一说没有概念的问题~

实际上国内大多数的公司还都是业务驱动型,无论从目前还站在顶端的 BAT,还是后来的京东、美团、滴滴、头条。无论哪个公司,收入的直接来源就是业务,自然从老板的角度来讲,业务是最最重要的部分。至于技术上用什么来实现,作为最上层的老板实际上并不关心,我才不在乎你们这些写代码的是在写 java 还是在写 php,哪怕你在后端写了无数 nodejs,都没有关系,只要帮我赚到钱就好了。

业务驱动的附属品往往就是对工程的质量不重视,说白了什么代码质量工程质量之类的东西根本就不影响老板赚钱。在很多公司创业初期,以电商的场景举例,我一天可能最多也就 1000 单,我招十个客服妹子早晚轮班。每天有技术人员 on call,那遇到问题让他们解决就好了,反正系统是他自己开发的,问题解决起来也不麻烦,业务的规模不大,系统的规模也就不大,技术查起问题来也不会太费劲,即使滚蛋了新来的也能很快接手。

因此从老板层面是不太会有动力去推什么技术执行规范之类的事情的。从和其它公司的同学交流看来,大多数公司都是没有 Code Review 的。除了听说美团在这方面做得好一些,但大概也就只有美团了。百度有自己的静态代码检查工具,至少能让你的代码在风格上满足公司定的规范。

但是代码风格只是代码质量里非常非常微小的一个部分,比如我就可以写出来非常符合 golang 官方推荐代码风格的垃圾:

if driverID > 0 {
  if orderID > 0 {
    if passengerID > 0 {
      if ruleIsValid {
        if dateIsValid {
          if validate(user) {
            // save rubbish to db
          }
        }
       }
     } else {
       // do some strange thing
     }
  }
}

而且这种代码你用任何静态检查工具都不会出 warning (欢迎打脸)。但是你说这段有问题的代码,没有重构的方法吗?

肯定也是有的,在我之前的文章中也早提到过。工程界还给这些重构方法起了一些概念性的称呼,比如 early return,guard clause。

虽然大多数的程序员说起架构大数据分布式算法语言原理头头是道,我们随意拿出一些工程上的 bad case,他们却不知道要怎么进行改进。程序员们的想法也非常现实啊,你说我去理解一个项目的代码是写得好还是不好又不会对我的收入产生任何影响。我出去面试,别人又不会考你什么样的代码是好的代码。我了解这个也没用,凑合凑合得了。

这就是目前码农界的现状。

这样的现状会导致有一些有追求的码农非常痛苦,比如刚入行的程序员,进了公司以后发现公司的项目代码全是屎,但是因为没有经验,却提不出什么改进建议,所以也就向现实妥协了。然后过了很多年以后,写出来的代码还是和在这家公司待的时候一样屎。

作为一个涉世未深的新人怎么跳出这个怪圈呢,我觉得最好的方式就是多混开源社区。可能会有人说我极端,然后给我举出某某某开源项目的代码也是一坨屎的例子。但这并不是我要说的重点。

写代码这件事情某种意义上和你穿衣服差不多,如果在家里都是你自己亲戚熟人,你可能随便一个大裤衩对付对付就得了。但是如果你要出去溜大街,你是一定会小心翼翼地把衣服穿好,并且认真得琢磨怎么样才能不在人前出丑的。

再把这个思路套用到我们公司来,你们一个小组三四个人。不管谁开的头,项目代码哪一天复杂起来了,腐化了,不小心没在意。反正就我们几个人,也没别人来批评,那就这么着吧。我要真闲下来不如划划水看看电影,重构啥代码啊。不单写业务系统的代码这样,写公共的组件库也这样。

直到有一天,有其它的项目组说要复用你们写的组件库。然后说你们的东西怎么都这么屎啊,我可以推测一下,你肯定是不开心的。但是别人证据确凿,比如我们平常忙啊,没有时间重构啊,我们不是基础架构啊,代码肯定有缺陷啊,我们线上验证过啊,这点小问题不要紧的啊一类的理由就全来了。

你看看,在外面你听多少公司在吹企业级应用,自研企业级语言 lib,自研企业级 web 框架,自研企业级 rpc 工具。

结果你实际来看看,哎哟,这企业级难道是要变成垃圾的代名词么?

这种事情实际上解决起来也没有那么复杂。虽然这年头外面全都在吹微服务,进而导致了公司的组织架构随系统架构进行了迁移。很多码农可能会被归属到一个非常非常小的组里,但是这不代码组间不能进行交流,部门内不能进行交流,也不代表公司部门间不能进行交流。有时候很多人的想法就是我分了组,我的就是我的,你的就是你的,我们自个儿干自个儿的,井水不犯河水。很多时候技术上的交流和辩论就能解决不少认知上的短浅。说白了心态要开放,要接受得了批评。

再回到前面说的,如果你们的公司真的组组间都是墙,那你还是可以通过混开源社区,学习那 70% 的优秀项目来提高自己的节操和审美。如果你刚入行,那业界也有人写好了帮助你建立基本审美的书:

https://item.jd.com/12749344459.html

https://item.jd.com/11728740.html

重构这本书基于 java,有一定的局限性,但很多观点还是可以参考的。思考这些“没有正确答案”的问题往往是很痛苦的,因为大多数人不在乎这些事情。

建立了基础的审美之后,再来谈一谈 test。

对于程序员来说,常用的 test 大概就两种,一种是单元测试,说白了就是函数测试;另一种是偏业务的测试,可能就是接口测试、http 测试、或者 e2e 测试。

刚入行的第一年我也是没怎么写过测试的,最近一年为自己的开源库和其它人的开源库写过测试之后,我确实真切地理解了测试的好处。

1.保证代码的正确性

2.别人可以直接从你的测试知道你的库提供了什么样的功能

3.测试完善的情况下可以帮助你进行快速重构

前两点总是会有人能提出一些狡辩的理由,比如我们的东西是线上验证过的,你说 test 靠谱还是我的系统线上验证过更靠谱啊,我都跑了一年了你说是吧。你说功能?我连 example 都给你写好了还要啥自行车?

“线上跑了一年”这种事情是没有办法证明你提供的 lib 是靠谱的,说白了现在大多数的人在写代码上实行的是实用型编程而不是防御性编程,你现在没问题只是证明它还没出问题而已。另一方面,每个技术团队的业务覆盖范围都是有限的,可能有的组的业务根本连 redis 都用不上,即使用上了也只是做做简单的 set/get,但是你说我们写了一个 redis 的库,里面包含了很多实用的命令。里面有很多指令你自己都没用过,你怎么证明你的函数库是靠谱的?

这个逻辑很简单啊!

没有简单的事情。

第三点就更反驳不了了。单元测试会帮助你覆盖到你对这个库所有的使用情况和边界情况,如果未来你觉得之前这个库有问题想要重构,那么能够给你的重构提供的唯一保障就是之前留下的 test 内容。每次重构,重跑 test,可以在 99% 的情况下保证你的重构是正确的。如果你 test 不全,那 test 没覆盖到的函数你敢改么?

没有 test 的代码如果稍微复杂一点,经过一段时间后一定会成为技术包袱或者技术遗产,后人避之唯恐不及,而不会动心思去改进他。如果有机会肯定会使用别人测试更全面的库,至少心里踏实。

从这次争辩的结论上来讲,至少还是有不少共识达成的,毕竟大家也都是技术人员,不会在一些很明显的问题上胡搅蛮缠。共识部分如果之后有时间的话也写一篇文章吧~

最后补一刀,还有一些程序员觉得业务代码就是水平低质量差,但我现在可以明确地说,很多公司的基础部门的代码一样质量很差。不信你用开源社区的静态扫描工具去扫一扫~

Xargin

Xargin

If you don't keep moving, you'll quickly fall behind
Beijing