对几个软件开发传统观点的质疑和反驳

why 下面这些观点都是程序员在教科书上、在编码规范里、在正统的软件工程流程里流传开来的,帮助了许多人在程序员启蒙期间养成了良好的习惯、原则。对许多人(包括曾经的我)来说,似乎是理所当然的。但是随着阅历的增长,视角在变化、看法也在变化,曾经的好恶现在都可能大翻身了。

为代码写足够的注释,让代码易于理解

“ 所有程序员都会写自己看得懂的代码,但只有优秀的程序员才写大家看得懂的代码。” 这话没错,但是——

  1. 什么才是“ 大家看得懂” 的定义? 我有必要让我的 C++代码对于一个月前才明白指针和引用区别的初学者简单易懂么?
  2. 更重要的是,要代码能够“ 看得懂”,主要是靠足够多的注释吗?

我觉得这两点都是扯淡。

关于第 1 点,造就了一些自我感觉过度良好的人 ,习惯性地把前人写的代码批得体无完肤。在他们眼中,这段代码巨烂,那段代码是屎,更有甚者,在评审别人代码的时候,一样说出这样的话来(请 参见这篇文章 里的“ 一坨屎型评审”)。

反对我的人会说,软件公司做产品赚钱,它们希望你的代码让不熟悉项目的新员工快速阅读、上手。 这确实是个矛盾 。说白了,你写的代码要和一个团队的能力匹配。在一个鱼龙混杂的团队,甚至一个糟糕的团队,你写出的代码也许很难和大伙儿产生共鸣,他们希望你写最普通最易懂的代码,没有精巧的设计(我指的是,“ 某一些精巧的设计,恰恰是以降低代码的可维护性为代价的 ”),没有语言高级特性,看着只有顺序、循环、分支判断的基本代码。如果大家都是 JavaEE 的初学者,那么就从 JSP+Servlet 开始吧,这样你们才在一个圈子里,要不然,没有人能真正和你一起讨论设计和代码的问题。

所以许多上进的程序员,会希望在一个牛人的团队里工作。这就像足球运动员一样,因为足球是集体运动, 一个足球运动员能达到的高度,是和他所在的团队息息相关的 。在一个优秀的团队里,大家个性各有千秋,擅长领域不甚相同,但是都学习迅速,能力不差太远,大家阅读代码都能够很快理解和领会,而且讨论问题可以用一些程序员才明白的隐喻(比如有人说“ 我觉得这里应该用一个 builder 来实现”,大家都明白 builder 指的是什么),氛围和效率显而易见。

如果你恰好对当前需要用到的业务和技术特别熟悉,领先团队里其他人一大截怎么办?那你就该在做设计编码的时候先行一步,你是那个最该去做架构设计、写骨架代码的人,完成一个架子以后再来给大家讲解,并和大家讨论,改进现有的设计。也就是说,你要多做一些更重要的事,而不是和大家一起分析、一起讨论,甚至一人负责一个模块,最后的结果就是大家根本和你讨论不到一块儿去。

关于第 2 点, 要代码“ 看得懂”,是设计出来的,而不是注释加出来的 。这和产品质量一样,产品质量是设计出来的,而不是测试测出来的。注释的意义在于对当前代码自解释做不到的地方进行补充。

所以,你的代码要易于理解,首先要保持简洁和清晰,这既包括良好的设计,也包括良好的编码习惯,也就是说,代码是自解释的,其次才通过注释的补充,让代码更易懂。注意,我不是说注释不重要和不必要,而是说,注释应该完成它自己的功用,它远不能代替代码本身自我解释的价值。

举一个简单的例子,你可以这样写代码:

/**
 * 图表模型
 */
class Chart{
    /**
     * 图表长度
     */
    private int length;
    
    /**
     * 获取图表长度
     * @return 图表长度
     */
    public int getLength(){
        return this.length;
    }

    /**
     * 设置图表长度
     * @param length 图表长度
     */
    public void setLength(int length){
        this.length = length;
    }
}

好,那么你告诉我,这段代码和下面这段代码相比,你获取了什么更多的有用信息?

class Chart{
    private int length;

    public int getLength(){
        return this.length;
    }
    public void setLength(int length){
        this.length = length;
    }
}

我想你懂我的意思,两段代码,代码本身意思已经够明确了,再加上这些无聊的注释,只是浪费资源、浪费生命。很多编程语言,利用语法糖,连简单 get、set 方法都可以省了(比如 Objective C),而加这种注释的做法却依然在反软件、反人类而行。也许你和我一样,曾经为了公司某些扯淡的规定,为了规避某些扯淡的代码静态检查工具(比如 CheckStyle 这样的,甚至自己开发这种无聊的检查工具)检查结果中的警告信息,加上了(包括 IDE 自动生成)这些毫无意义的注释,于是领导看了:“ 好,没有警告信息,代码质量好”。 虽然至始都痛恨这样的做法,但我还是做了,至今后悔。最让人痛恨自己的事情就是不得不去做那些自己痛恨的事情。

设计文档要详细,细化到方法定义

持这个观点的大有人在。对于这个观点我并不是完全反对,如果你要说设计文档需要“ 详细到可以指导编码” 我还能同意,但是我确实非常不喜欢详详细细的设计文档。肯定设计文档的价值这没有错,但是 过于详细的设计文档撰写,往往容易造成纸上谈兵的窘境

有人说设计文档太过粗略了做不好设计,事实上, 文档只是呈现设计的其中一种形式而已 ,做得好设计的人,可以一边编码一边思考,可能辅助草稿纸上写写划划,就可以完成优秀的软件;不会做设计的人,设计文档写多少页都没用。这让我想起了今天和同事关于 TDD 的讨论,会做设计的人,不让他用 TDD 也能写好代码;不会做设计的人,TDD 又有何用?所以让 TDD 成为设计的主要工具,那就是扯淡。

再一个,在设计文档中,不可能做完设计,不知你是否有这样的体会,设计文档思考得再缜密细致,等落到代码上的时候,还会和开始的思考有许多不同,至少有很多细小的不同。这是因为设计思考本身就是贯穿整个设计编码过程的,一人只做设计、另一人只写代码这样的理想模式是不可能达成的。

我了解一些对日外包公司,程序员拿到手的设计文档就是细化到方法定义了的,如果你有能力有志气,在中国最好就不要做外包,尤其是对日外包, 这样的公司拒绝你的一切思考,就是在摧残人才

另外一个原因,是针对一些阐明“ 设计文档可以传承业务和技术知识” 观点的人,详细的设计文档并不能够传承什么业务和技术,原因很简单,详细的文档初始撰写成本高,维护的成本更高。我不相信程序员在修改了代码逻辑以后,会去经常保持设计文档的同步性。这不合理,只有代码才是保持最新的,其它一切都会过时。而相对简要或粗略的文档,稳定性就要强得多。

让项目组各个角色去评审代码设计

下面我要驳斥的这个观点来源于我的一些经历,也许并不能算是主流观点。

对于设计文档的评审,如果是设计原理、实现原理,正到了程序员才关心的层面上,如果不写代码的测试跑来一起讨论,这就成了浪费时间、制造矛盾的做法。我经历过这样的事情,觉得很幽默。专职测试人员的定位各有千秋,许多人经验丰富、无可替代,但是至少,我接触的测试人员中,他们几乎是不阅读代码的,也就是说,对于代码设计(注意,是代码设计,不是产品设计)的讨论,他们不该参与进来。

另外, 不要说“ 我几年前也是写代码的”,毛主席都讲了,“ 不了解情况,就没有发言权”,如果你对当前的代码实现不了解,就不要来碍手碍脚地评审代码层面的设计了。

我也经历过这样的场景,每一个产品都要组织一些有经验的程序员,去给别的产品的代码挑刺儿。我的看法是,这很难挑出特别有价值的毛病来,原因也是一样的,你对别的产品业务不了解,那么要花大量精力去阅读代码,更要去熟悉业务,否则只能从代码层面上抠抠细节。

所以,谁来把关实现层面的设计和代码的质量最卓有成效呢?正是熟悉项目的程序员们,尤其是项目组骨干,或者一起参与设计、编码和测试的架构师(其实架构师还是一名优秀的程序员,我从来不认为“ 只懂业务的架构师” 有什么资格去做软件架构)。

为代码设置量化的限制指标

统计指标是有价值的,但是如果设置这些量化指标给程序员套限,则是违背客观规律的行为。这一观点我有必要举例说明一下:

  • 测试代码覆盖率不得低于 95%(比如工具 EMMA);
  • 方法圈复杂度不能超过 15(你也许知道圈复杂度的检查工具 SourceMonitor);
  • 单个类的行数不能超过 500,单个方法的行数不能超过 200;
  • 任意两个类之间重复代码行数不能超过 10 行(你可能知道重复代码检测工具 Simian);
  • ……

这些硬生生限制,都是反软件、反人类的。你可以说圈复杂度高的方法也许过于复杂,你可以说重复比率高的代码往往可以优化,但是这些都只是一个辅助的指标。 这些工具都是用来帮助程序员改善他们的设计和代码质量的,如今它们却被用来做反程序员的事。

对于测试代码覆盖率的要求,而且有许许多多公司拿来作为代码质量衡量的重要指标,我认为更是骇人听闻。我写代码也做单元测试,但是会有选择地写 UT 用例,不会去追求测试覆盖率,而且测试再全面也不可能保证结果的绝对正确,好钢要用在刀刃上,时间的投入要换取划算的回报,而不是不计代价地补充测试用例。而且,在这里我要说的是,保证软件质量的方式有很多,测试验证的方式也有很多。即便覆盖率达到 100%,也不能说明质量高到哪儿去,追求覆盖率始终太过功利。另外,有许多代码本身就没有多大被 UT 测试的价值,这也是不容忽视的。

优秀的程序员,应该难以容忍自己产出糟糕的代码,也许对代码有一点洁癖,对代码之美有不懈的追求。对这样的软件的使用动机,也应该来源于程序员,而相关数据的采集,最终一定要为程序员服务。

今天只是把上面这些观点做了个整理,在和别人谈起这些的时候,其实我觉得我只是说了实话而已,我的观点一点都不偏激。我知道很可能你会有不同看法,这太好不过了,但是 善意地提醒你,请一定仔细思考一下,不要被公司的精神和文化洗了脑 ,我们都是程序员,我们最清楚,或许也都经历过那些针对程序员、软件开发荒唐可笑、乃至不可思议的做法。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

28,303 次阅读

10 thoughts on “对几个软件开发传统观点的质疑和反驳

  1. 我觉得规定是死的,人必须是活的,有些规定,如:
    单个类的行数不能超过 500,单个方法的行数不能超过 200;
    是可以提醒程序员拆分方法和类,降低维护难度,但是如果把这些规定作为一种束缚,那就大可不必了。

发表评论

电子邮件地址不会被公开。

back to top