Fork me on GitHub

如何避免写出烂代码?

刚刚成为程序员的时候,根本不管代码是怎么写出来的,只要功能可以实现就好,这就导致了,之后我回顾自己写的代码的时候,经常会楞逼。因为是自己写的代码,所以不会骂的太狠,现在看我最初写的有些代码,就是一坨屎。
因为有看到过自己和别人的很烂的代码,所以我就暗自发誓,以后一定要避免写出烂代码。当看到别人的或者api的代码之后会多留意一下它们是怎么写的。总的就是一句话————整洁,可读性好。

什么是烂代码

什么样的代码算是烂代码?我也遇到过很多的代码了,接下来做一个总结归纳:

意义不明

有些程序员会写出意义不明的代码,可能是因为它们对编程的理解还不够深入,写出的代码完全不知道干什么的,单纯降低程序效率。
就像这样:

1
2
3
4
5
6
public void save(){
for(int i = 0;i < 100;i++){
//防止保存失败,重试100次
document.save();
}
}

这种程序没有任何意义,还降低了效率。反而让人看出这个程序员的真实水平太差。

没有适当的注释

有些方法代码,通篇没有一句注释,偏偏里面的代码是方法调用方法,把人绕的很晕,而且函数名、变量名的命名都是没人看得懂的缩写。而且有些程序员还喜欢各种复杂的宏定义、位运算之类的,生怕代码让别人一下子看懂了显得自己水平不够。
对于这个,我只想说两点:
1.尽量将代码写得简洁,通俗易懂
2.必要的地方尽量加上注释,不仅仅是为了方便别人,也为了自己以后看自己的代码的时候省去不必要的麻烦。

不恰当的组织

这是比较深层的烂代码。因为看的不仅仅是代码本身了。程序员在写过一些代码之后,有了基本的代码风格,但是对于规模大一些的工程的掌控能力不够,不知道代码应该如何解耦、分层和组织。
在这种情况下,经常会看到一段代码在工程里拷来拷去;某个文件里放了一大坨堆砌起来的代码;一个函数堆了几百上千行;或者一个简单的功能七拐八绕的调了几十个函数,在某个难以发现的猥琐的小角落里默默的调用了某些关键逻辑。

这类代码大多复杂度高,难以修改,经常一改就崩;而另一方面,创造了这些代码的人倾向于修改代码,畏惧创造代码,他们宁愿让原本复杂的代码一步步变得更复杂,也不愿意重新组织代码。当你面对一个几千行的类,问为什么不把某某逻辑提取出来的时候,他们会说:

“但是,那样就多了一个类了呀。”

而对于这类代码的维护和更新,都是以拷为主,修改结合的方式来做的。并且想要修改功能时却发现程序里充斥着各种无法理解的逻辑、改完之后莫名其妙的bug一个接一个。而且这类代码我经常能够见到。所以我觉得,对于这一种烂代码的避免的功底要求还是很高的,需要长期的学习和操作才能避免。

代码很难复用

写出这种代码的程序员一般都是项目组里开发效率比较高的人,但是大量的业务开发工作导致他们不会做多余的思考,他们的口头禅是:“我每天要做XX个需求”或者“先做完需求再考虑其他的吧”。

这种反模式表现出来的后果往往是代码很难复用,面对deadline的时候,程序员迫切的想要把需求落实成代码,而这往往也会是个循环:写代码的时候来不及考虑复用,代码难复用导致之后的需求还要继续写大量的代码。

一点点积累起来的大量的代码又带来了组织和风格一致性等问题,最后形成了一个新功能基本靠拷的遗留系统。而且因为当时是针对特定需求写的代码,一旦需求变了就需要重新来过。这就导致了,这样的程序员一直都处于忙碌状态,但是本身缺得不到很大的提升。因为他们的精力都放在了如何去做好需求上,而不是该如何去写好代码上面了。

结论

说到这里,究竟什么是烂代码?个人认为,烂代码包含了几个层次:

如果只是一个人维护的代码,满足功能和性能要求倒也足够了。

如果在一个团队里工作,那就必须易于理解和测试,让其它人员有能力修改各自的代码。

同时,越是处于系统底层的代码,扩展性也越重要。

所以,当一个团队里的底层代码难以阅读、耦合了上层的逻辑导致难以测试、或者对使用场景做了过多的假设导致难以复用时,虽然完成了功能,它依然是坨翔一样的代码。

写好代码很难

与写出烂代码不同的是,想写出好代码有很多前提:

理解要开发的功能需求。

了解程序的运行原理。

做出合理的抽象。

组织复杂的逻辑。

对自己开发效率的正确估算。

持续不断的练习。

写出好代码的方法论很多,但我认为写出好代码的核心反而是听起来非常low的“持续不断的练习”。

很多程序员在写了几年代码之后并没有什么长进,代码仍然烂的让人不忍直视,原因有两个主要方面:

环境是很重要的因素之一,在烂代码的熏陶下很难理解什么是好代码,知道的人大部分也会选择随波逐流。

PS:重构不是万能药

程序员最喜欢跟程序员说的谎话之一就是:现在进度比较紧,等X个月之后项目进度宽松一些再去做重构。

不能否认在某些(极其有限的)场景下重构是解决问题的手段之一,但是写了不少代码之后发现,重构往往是程序开发过程中最复杂的工作。花一个月写的烂代码,要花更长的时间、更高的风险去重构。

从技术上来说,重构复杂代码时,要做三件事:理解旧代码、分解旧代码、构建新代码。而待重构的旧代码往往难以理解;模块之间过度耦合导致牵一发而动全身,不易控制影响范围;旧代码不易测试导致无法保证新代码的正确性。

而另一面,许多技术负责人也意识到了代码质量和重构的必要性,“那就重构嘛”,或者“如果看到问题了,那就重构”。上一个问题解决了,但实际上关于重构的代价和收益仍然是一笔糊涂账,在没有分配给你更多资源、没有明确的目标、没有具体方法的情况下,很难想象除了有代码洁癖的人还有谁会去执行这种莫名其妙的任务。

于是往往就会形成这种局面:

不写代码的人认为应该重构,重构很简单,无论新人还是老人都有责任做重构。

写代码老手认为应该迟早应该重构,重构很难,现在凑合用,这事别落在我头上。

写代码的新手认为不出bug就谢天谢地了,我也不知道怎么重构。

「真诚赞赏,手留余香」