半个月没有写blog了。正好是春节假期。
静下来,想想自己写的代码,真是有些汗颜,更有些惶恐。
加油吧~
偶尔看到拉勾网上的一篇文章。记录下来,鞭策自己:
作者:蛋疼的AXB
来源:程序人生
歇息一直是review的好时机,不处于工作与压力其中。更能看清自己。作者近期写了不少代码。review了不少代码。也做了不少重构,总之是对着烂代码工作了几周。
为了抒发一下这几周里好几次到达崩溃边缘的情绪,他决定写一篇文章谈一谈烂代码的那些事。
写烂代码非常easy
一坨翔但居然能用
刚入程序猿这行的时候常常听到一个观点:你要把精力放在ABCD(需求文档/功能设计/架构设计/理解原理)上,写代码仅仅是把想法翻译成编程语言而已,是一个没什么技术含量的事情。
当时的我在听到这样的观点时会有一种近似于高冷的不屑:你们就是一群傻子,根本不懂代码质量的重要性。这么下去迟早有一天会踩坑。
可是几个月之后。他们似乎也没怎么踩坑。而随着编程技术一直在不断发展。带来了很多其它的我曾经觉得是傻子的人添加到程序猿这个行业中来。
语言越来越高级、封装越来越完好,各种技术都在帮助程序猿提高生产代码的效率,依靠层层封装。程序猿真的不须要了解一丁点技术细节,仅仅要把需求里的内容逐行翻译出来就能够了。
非常多程序猿不知道要怎么组织代码、怎么提升执行效率、底层是基于什么原理。他们写出来的是在我心目中烂成一坨翔一样的代码。
可是那一坨翔一样代码居然能正常工作。
即使我觉得他们写的代码是坨翔,可是从不接触代码的人的视角来看(比方说你的boss),代码编译过了,測试过了。上线执行了一个月都没出问题,你还想要奢求什么?
所以。即使不情愿,也必须承认,时至今日,写代码这件事本身没有那么难了。
烂代码终究是烂代码
下面总结一下
可是偶尔有那么几次,写烂代码的人离职了之后。事情似乎又变得不一样了。
想要改动功能时却发现程序里充斥着各种无法理解的逻辑、改完之后莫名其妙的bug一个接一个,接手这个项目的人開始漫无目的的加班,而且原本一个挺乐观开朗的人渐渐的開始喜欢问候别人祖宗了。
我总结了几类常常被骂娘的烂代码:
1意义不明
能力差的程序猿easy写出意义不明的代码,他们不知道自己到底在做什么.
就像这样:
public void save(){
for(int i=0; i<100; i++){
//防止保存失败,重试100次
document.save();
}
}
对于这类程序猿,我一般建议他们转行。
2不说人话
不说人话是新手最常常出现的问题。直接的表现就是写了一段非常easy的代码,其它人却看不懂。
非常多程序猿喜欢简单的东西:简单的函数名、简单的变量名、代码里翻来覆去仅仅用那么几个单词命名。能缩写就缩写、能省略就省略、能合并就合并。
这类人写出来的代码里充斥着各种g/s/gos/of/mss之类的全世界没人懂的缩写,或者一长串不知道在做什么的连续调用。
还有非常多程序猿喜欢复杂,各种宏定义、位运算之类写的天花乱坠,生怕代码让别人一下子看懂了会显得自己水平不够。
简单的说。他们的代码是写给机器的。不是给人看的。
3不恰当的组织
不恰当的组织是高级一些的烂代码。程序猿在写过一些代码之后,有了主要的代码风格。可是对于规模大一些的project的掌控能力不够。不知道代码应该怎样解耦、分层和组织。
这样的反模式的现象是常常会看到一段代码在project里拷来拷去;某个文件中放了一大坨堆砌起来的代码;一个函数堆了几百上千行;或者一个简单的功能七拐八绕的调了几十个函数。在某个难以发现的猥琐的小角落里默默的调用了某些关键逻辑。
这类代码大多复杂度高,难以改动,常常一改就崩。而还有一方面。创造了这些代码的人倾向于改动代码,畏惧创造代码。他们宁愿让原本复杂的代码一步步变得更复杂,也不愿意又一次组织代码。
当你面对一个几千行的类,问为什么不把某某逻辑提取出来的时候,他们会说:
“可是,那样就多了一个类了呀。”
4假设和缺少抽象
相对于前面的样例,假设这样的反模式出现的场景更频繁。花样很多其它。始作俑者也更难以自己意识到问题。比方:
public String loadString(){
File file = new File("c://config.txt");
}
文件路径变更的时候。会把代码改成这样:
须要载入的内容更丰富的时候。会再变成这样:
之后可能会再变成这样:
这类程序猿往往是项目组里开发效率比較高的人,可是大量的业务开发工作导致他们不会做多余的思考。他们的口头禅是:“我每天要做XX个需求”或者“先做完需求再考虑其它的吧”。
这样的反模式表现出来的后果往往是代码非常难复用,面对deadline的时候,程序猿迫切的想要把需求落实成代码,而这往往也会是个循环:写代码的时候来不及考虑复用。代码难复用导致之后的需求还要继续写大量的代码。
一点点积累起来的大量的代码又带来了组织和风格一致性等问题,最后形成了一个新功能基本靠拷的遗留系统。
5还有吗?
烂代码还有非常多种类型,沿着功能-性能-可读-可測试-可扩展这条路线走下去。还能看到非常多匪夷所思的样例。
那么什么是烂代码?个人觉得。烂代码包括了几个层次:
假设仅仅是一个人维护的代码。满足功能和性能要求倒也足够了。
假设在一个团队里工作,那就必须易于理解和測试,让其它人员有能力改动各自的代码。
同一时候。越是处于系统底层的代码。扩展性也越重要。
所以,当一个团队里的底层代码难以阅读、耦合了上层的逻辑导致难以測试、或者对使用场景做了过多的假设导致难以复用时,尽管完毕了功能。它依旧是坨翔一样的代码。
6够用的代码
而相对的,假设一个project的代码难以阅读。能不能说这个是烂代码?非常难下定义,可能算不上好,可是能说它烂吗?假设这个project自始至终仅仅有一个人维护,那个人也维护的非常好,那它似乎就成了“够用的代码”。
非常多project刚開始可能仅仅是一个人负责的小项目,大家关心的重点仅仅是代码能不能顺利的实现功能、按时完工。
过上一段时间,其它人參与时才发现代码写的有问题。看不懂,不敢动。
需求方又開始催着上线了,怎么办?仅仅好小心翼翼的仅仅改逻辑而不动结构,然后在凝视里写上这么实现非常ugly。以后明白内部逻辑了再重构。
再过上一段时间,有个类似的需求。想要复用里面的逻辑,这时才意识到代码里做了各种特定场景的专用逻辑,复用非常麻烦。为了赶进度仅仅好拷代码然后改一改。问题攻克了,问题也加倍了。
差点儿所有的烂代码都是从“够用的代码”演化来的,代码没变,使用代码的场景发生变了,原本够用的代码不符合新的场景,那么它就成了烂代码。
重构不是万能药
它非常难带来直接收益
程序猿最喜欢跟程序猿说的谎话之中的一个就是:如今进度比較紧,等X个月之后项目进度宽松一些再去做重构。
不是否能认在某些(极其有限的)场景下重构是解决这个问题的手段之中的一个。可是写了不少代码之后发现,重构往往是程序开发过程中最复杂的工作。花一个月写的烂代码。要花更长的时间、更高的风险去重构。
曾经经历过几次忍无可忍的大规模重构,每一次重构之前都是找齐了组里的高手。开了无数次分析会,把组内需求所有暂停之后才敢开工。而重构过程中往往哀嚎遍野,差点儿每天都会出上非常多意料之外的问题,上线时也差点儿必定会出几个问题。
从技术上来说。重构复杂代码时。要做三件事:理解旧代码、分解旧代码、构建新代码。而待重构的旧代码往往难以理解;模块之间过度耦合导致牵一发而动全身,不易控制影响范围;旧代码不易測试导致无法保证新代码的正确性。
这里还有一个核心问题。重构的复杂度跟代码的复杂度不是线性相关的。比方有1000行烂代码,重构要花1个小时,那么5000行烂代码的重构可能要花2、3天。要对一个失去控制的project做重构,往往还不如重写更有效率。
而抛开详细的重构方式,从受益上来说,重构也是一件非常麻烦的事情:它非常难带来直接受益,也非常难量化。这里有个非常有意思的现象,基本关于重构的书籍无一例外的都会有独立的章节介绍“怎样向boss说明重构的必要性”。
重构之后能提升多少效率?能减少多少风险?非常难答上来,烂代码本身就不是一个能够简单的标准化的东西。
举个样例,一个project的代码可读性非常差。那么它会影响多少开发效率?
你能够说:之前改一个模块要3天。重构之后1天就能够了。
可是怎么应对“不就是做个数据库操作吗为什么要3天”这类问题?烂代码“烂”的因素有不确定性、开发效率也因人而异,想要证明这个东西“确实”会添加两天开发时间,往往反而会变成“我看了3天才看懂这个函数是做什么的”或者“我做这么简单的改动要花3天”这样的神经病才会去证明的命题。
而还有一面,很多技术负责人也意识到了代码质量和重构的必要性,“那就重构嘛”,或者“假设看到问题了。那就重构”。
上一个问题攻克了。但实际上关于重构的代价和收益仍然是一笔糊涂账。在没有分配给你很多其它资源、没有明白的目标、没有详细方法的情况下,非常难想象除了有代码洁癖的人还有谁会去执行这样的莫名其妙的任务。
于是往往就会形成这样的局面:
不写代码的人觉得应该重构,重构非常easy,不管新人还是老人都有责任做重构。
写代码老手觉得应该迟早应该重构。重构非常难,如今凑合用,这事别落在我头上。
写代码的新手觉得不出bug就谢天谢地了,我也不知道怎么重构。
写好代码非常难
与写出烂代码不同的是,想写出好代码有非常多前提:
理解要开发的功能需求。
了解程序的执行原理。
做出合理的抽象。
组织复杂的逻辑。
对自己开发效率的正确估算。
持续不断的练习。
写出好代码的方法论非常多,但我觉得写出好代码的核心反而是听起来非常low的“持续不断的练习”。
非常多程序猿在写了几年代码之后并没有什么长进。代码仍然烂的让人不忍直视,原因有两个主要方面:
环境是非常重要的因素之中的一个,在烂代码的熏陶下非常难理解什么是好代码。知道的人大部分也会选择随波逐流。
还有个人性格之类的说不清道不明的主观因素。写出烂代码的程序猿反而都是一些非常好相处的人,他们往往热爱公司团结同事平易近人工作任劳任怨–仅仅是代码非常烂而已。
而工作几年之后的人非常难再说服他们去提高代码质量,你仅仅会重复不断的听到:“那又有什么用呢?”或者“曾经就是这么做的啊?”之类的说法。
那么从源头入手。提高招人时对代码的质量的要求怎么样?
前一阵面试的时候添加了白板编程、近期又添加了上机编程的题目。发现了一个现象:一个人工作了几年、做过非常多项目、带过团队、发了一些文章,不一定能代表他代码写的好;反之,一个人代码写的好。其它方面的能力一般不会太差。
举个样例。近期喜欢用“写一个代码行数统计工具”作为面试的上机编程题目。非常多人看到题目之后第一反映是,这道题太简单了。这不就是写写代码嘛。
从实际效果来看。这道题识别度却还不错。
首先,题目足够简单。即使没有看过《面试宝典》之类书的人也不会吃亏。
而题目的扩展性非常好,即使提前知道题目。配合不同的条件。能够变成不同的题目。比方要求按文件类型统计行数、或者要求提高统计效率、或者统计的同一时候输出某些单词出现的次数,等等。
从考察点来看。首先是主要的树的遍历算法。其次有一定代码量。能够看出程序猿对代码的组织能力、对问题的抽象能力;上机编码能够非常easy的看出应聘者是不是非常久没敲代码了;还包括对于程序易用性和性能的理解。
最重要的是,最后的结果是一个完整的程序,我能够依照日常工作的标准去评价程序猿的能力,而不是从十几行的函数里意淫这个人在日常工作中大概会有什么表现。
但即使这样,也非常难拍着胸脯说,这个人写的代码质量没问题。毕竟面试仅仅是代表他有写出好代码的能力,而不是他将来会写出好代码。
悲观的结语
说了那么多。结论事实上仅仅有两条,作为程序猿:
不要奢望其它人会写出高质量的代码
不要以为自己写出来的是高质量的代码