Code Complete — 代码改善

动感小前端

代码改善的目的是为了提高质量,这个质量不仅包括从用户角度出发的软件质量,也包括从开发者角度出发的代码质量。

软件质量的普遍原理就是改善质量以降低开发成本。提高生产效率和改善质量的最佳途径就是减少花在这种代码返工上得时间,无论返工是由需求、设计改变还是调试引起的。提高质量的方式有协同构建、调试与测试、代码重构和调整等多种方式,本文记录了一些主要策略。

协同构建

协同开发实践往往能比测试发现更多的缺陷,并且更有效率。

“协同构建”包括结对编程、正式检查、非正式技术复查、文档阅读,以及其他让开发者共同承担创建代码及其它工作产品责任的技术。结对编程成本太高,代码审查倒是非常普遍。

不得不说“协同构建”是确保软件质量的有效途径,结对编程本身有很多好处以及技巧,书中花了大量篇幅来介绍,但是在国内互联网公司,基本上是一个程序员同时负责好几个项目,很难让两个程序员来写同一份代码,因此通过”协同构建”来确保质量的重任就落在了代码审查上面。

代码走查是比较通俗的说法,细分的话可分为正式检查(Formal inspections),走查(Walk-Throughs),代码阅读(Code Reading)。

正式检查过程中,每个人都需要扮演一个明确的角色:

  • 主持人。主持人一般是老手,能hold住场以及控制进度,不能因为某些细节问题争论太久而导致会议延长。审查的主要目的是发现尽可能多的缺陷,而不是讨论技术问题。

  • 作者。作者的首要任务是向大家清晰的解释代码。

  • 评论员。评论员是同设计和代码有直接关系的人,但又不是作者,一般不止一个。

  • 记录员。记录发现的问题,以及记录因此而产生的修复计划。

检查会议的准备工作以及角色指定通常由主持人负责,会议结束后主持人/记录员需要写一份缺陷报告,然后作者负责修复缺陷,修复后由评论员来审核这些问题。

代码走查是一个很随意的概念,公司的Code Review大多是这种。走查的所有参与者都会看代码并发现错误。走查的重点是检测错误,而非修正错误。所以一般走查会议完成后,并没有专门去检查最后作者是否将这些缺陷修复了。

代码阅读就更加灵活了,就是直接看代码来发现问题,通常一个人就能完成。虽然检测出缺陷的概率不如前两种,但是对于发现一些低级bug还是非常有效的。

测试

测试是改善质量最常见的方式,写代码的时候我们需要单元测试/组件测试/集成测试/回归测试/系统测试,有的是程序员完成的,有的是专门测试团队完成的。前端的系统化测试流程一直是个难题,我们团队有人在专门研究这块,此处不作展开。

调试

调试是确定错误根本原因并纠正错误的过程,对大部分程序员来说,这可能是开发中最困难的部分,尤其是在前端。

不会调试的程序员会受尽bug的折磨,先看下几种导致调试效率低下的想法:

  • 凭猜测找出缺陷。将print,console,debug散布在程序中,根据输出来找bug:)

  • 只找出问题并用最快的方式修复它,并不去一探究竟。

  • 迷信式调试,坚信这个bug不是我造成的,要么是环境问题,要么是其他程序员造成的。如果你遇到了这样的程序员那恭喜你,因为你再也不会遇到比他更糟的了:)

前端的调试比后台复杂的多,不过无论何种程序员应该都有一个共识,那就是不仅要发现它,解决它,更要理解它,优雅的解决它。

前端调试发现的问题多半是兼容问题,有的是浏览器不支持某些属性造成的,有的是浏览器内部机制造成的,我们需要理解它并且记录下来,避免下次犯同样地错误。

有一个问题就是,我们经常会在周会里面将自己遇到的一些问题分享出来,避免大家再次采坑,但是分享过后并没有一个统一的地方来记录和归类这些问题,之后在遇到类似问题也很难去查周报记录,或者有新人进来再次犯错的时候,它根本不知道这是个坑,很可能在掉进坑之后才意识到。

寻找缺陷

调试包含了寻找缺陷和解决缺陷,通常寻找缺陷并且理解缺陷形成的原因会占到整个调试过程90%的时间,只要能重现问题并且发现原因,修复工作通常很快。所以这里重点讲下如何优雅的寻找缺陷。

首先我觉得应该有一个大致思路:

  1. 出现任何问题首先应该审视自己,先检查是否自己代码的问题;

  2. 如果代码中没有发现问题,再确认一下是否自己的构建工具有问题,工具问题需要找维护工具的开发者解决,自己去解决或者规避非上上策;

  3. 如果工具也ok那再确定是否电脑环境的问题,这个环境包括了系统本身的host绑定,网络情况,以及依赖库,或者浏览器配置等等,经常有人遇到此类情况会尝试重启电脑,有时还真的管用;

  4. 如果还有问题,跟你同事确认下是不是其他人有发布相关代码,而这部分代码刚好影响到你工作了。

  5. 如果还是有问题,出去浪一会儿,然后再回到步骤1。

然后再重点说下第一点,毕竟出现问题原因多半在自己。首先要优雅的找出问题,一定要能够优雅的使用各种调试工具,比如chrome dev tool,weinre,Safari,Charles等,除了知道各种常用技能,深入学习还能学到一些特殊动作,比如我们都会用Chrome的断点调试,但是却很少用条件断点,然而条件断点又是一个很实用的技能。这些工具和技巧都能让你更快的发现问题。

当我们遇到问题时,控制台或者编译器一般都会报错,我们根据错误提示去解决问题。
有时候js报错根本就跟错误代码没半毛钱关系,两者差了个十万八千米,而且给出的错误行数也并非总是有用,尤其是在压缩混淆后的代码。

针对这种编译器瞎BB的错误类型,我们很难直接通过错误提示找到原因;而更头疼的时,在有的框架里面,你的代码出问题时编译器居然不报错!这个时候很难从正面去解决问题,那么就得换一种思路。

  1. 先对你的代码做语法检查。如果是方法调用错误或者IO错误的话,提示一般还是很有用的。而某些语法错误,比如少个括号,分号,或者你把某个单词拼错了,或者引用包的路径不对,或者json配置文件少个逗号,这时编译器可能只是进行了一些拙劣的扫描,于是便给出了错误的行号。

  2. 如果错误是最近才出现,或者之前没有的,直接查看版本历史记录,分析代码。

  3. 分而治之。 将程序划分为几个模块,对这几个模块分别测试或者尝试注释掉某个模块来逐渐定位问题。

以上是当通过编译器的错误提示很难找到具体问题时,总结的几种方法。针对这类问题我还没有找到一个很有效的方式,希望有高人指点。

修正缺陷

寻找缺陷虽然占了90%的时间,但是找到问题只是成功了一半,最终还是要解决问题的。

解决问题的结果有三种:一是拙劣的解决了问题,二是优雅的解决了问题,三是解决了这个问题又出现了新的问题!

好吧,这只是经验之谈,因为这三类情况我都遇到过。。。

第一种情况出现的原因有很多种,可能是情况紧急,不得已采取的临时方案,但是治标不治本;也可能是没有理解程序本身,只是解决了某些情况下的问题;也有可能是代码写久了自己懵逼了,思维定势了,出去浪一下说不定就会想到一个更优雅的解决方案。

第三种情况出现的原因多半是不理解业务造成的。尤其是在修改别人的bug时,一定要保证代码的侵入性最小,避免去修改全局变量。而且改完后最好做充分测试。

重构

前端技术发展飞快,我们昨天使用的技术,今天可能就被淘汰了,明天可能又换了新技术。Angular火了,项目用Angular重构,双向绑定真是爽!React火了,又用React重构一次,组件化大法好!vue火了,又开始用vue重构,轻量级就是灵活!后来WEEX火了...(当然实际情况应该不会如此,我只是开个玩笑)

所以说我们最初重构的目的是什么?

当然是为了提高质量,一方面提高软件外部表现质量,比如性能,稳定性等,另一方面提升软件内部质量,比如可维护性,可扩展性等。还有一种因素就是业务因素,不同的框架适用于不同的业务,如果重构使用的新框架能更适合当前业务,那也是极好的。

不过框架只是包含了特定软件思想的外壳,能解决一部分问题,但不能解决代码本身质量问题。因此抛开框架,在重构时我们通常要关注以下点。

  • 重复代码。复制粘贴即设计之谬。

  • 冗长的子程序。很少会有超过一屏的子程序。

  • 长循环与深嵌套。

  • 内聚性太差的类或者模块。如果某个模块包含了许多彼此无关的方法,那么这个模块本身的设计就是不合理的。高内聚,低耦合,能实现这两点的人代码设计水平一定很好。

  • 拥有太多参数的方法。

  • 一处变化,多处修改。如果外部有一个小小的改动,或者产品加了一个新的需求,你需要在多出进行重复的修改,那么这个设计也是有问题的。

  • 流浪数据。把数据传递给某个子程序,然后子程序将数据又传递给另一个子程序,这样传来传去的数据被称为流浪数据。请确定是否有此必要。

  • 命名问题。之前也说过变量命名,方法命名等技巧,重构的时候也需要注意。

  • 给难懂的代码写注释。这里的注释是辩解。某位大神曾说:不要为拙劣的代码写文档——应当重写代码。

  • 全局变量。在修改别人的代码时,你会切身体会到全局变量有多危险,应当避免使用全局变量。我还见过一种写法,就是在一个对象里面包含了一些公共属性,然后在这个类的各种成员方法里面会修改这些公共属性,然后这些公共属性又能暴露给其他对象使用,我觉得这样的写法会破坏封装打破内聚,是一种写起来很爽但是维护起来相当危险的做法。

  • 超前设计。程序中的代码似乎是在将来的某个时候用的。书中认为超前设计是一种画蛇添足的做法,增加了程序的复杂性并带来了额外的工作量。我觉得并不完全正确,一名优秀的程序员应该是充分了解业务的,虽然不能直接写针对未来特定业务的代码,但是可以对目前的某些方法进行增强,增强其扩展性。

以上关注点偏重于前端开发,对于继承,抽象这块的话在JS中体现的并不强所以没有关注,大家也可以去翻翻书。重构的更多细节,可以参考 [Code Complete — 创建高质量的代码](
http://www.jianshu.com/p/966da9b8e1b6)中的一些点来进行。

最后再记录一下重构的几个要点:

  1. 重构成功的关键在于程序员是否知道那些标志着代码需要重构的点,并且程序员本身也能写出高质量的代码;

  2. 要有安全重构的策略。重构有风险,务必要小心,同一时间只做一项重构,测试通过之后再进行下一步会比较好。

  3. 改代码是程序员始终要做的事情,开发阶段的重构是风险最小,提升程序质量的最佳时机!

代码调整策略和方法

很久以前,计算机的性能有限,所以人们通常会书写更利于节省资源的代码。后来硬件越来越强大,程序员们意识到过于关注性能会损害程序的可读性和可维护性,于是便开始转移焦点。时至今日,代码调整的最终目的是什么?是为了高效?还是可读性和可维护性?还是兼具两者?当然兼具两者最好,然而有时候高效的代码并不一定就是“更好”的代码。

性能优化误区

  1. 代码越少程序越快。现在应该不会有人这样想了吧。

  2. 特定的运算可能比其他的快。在某些浏览器下面高效的写法在另一个浏览器下面可能会很慢,所以在代码调整的时候一定要小心效率最低的那个。比如DOM操作。

  3. 应当随时进行优化。当程序没有完成时,你很难知道瓶颈在哪里。而在一开始就想做到最优,往往会干扰自己对程序目标的理解和判断。二八法则同样适用于软件开发:80%的性能,耗在了20%的代码上面。

  4. 程序的运行速度与其正确性同样重要。很显然正确性更重要。

何时调整代码

程序员应当使用高质量的设计将程序编写完成,使之易于维护,之后再去检查系统性能,然后分析瓶颈并对其优化。

常见低效之源

  1. 输入/输出操作;

  2. 操作系统交换内存页面;

  3. 系统调用。调用子程序会涉及上下文切换;

  4. 解释性语言。所以说,这是语言的先天缺陷;

  5. 错误。没有去掉调试信息,忘了释放内存,超时等。

总结

性能只是软件整体质量的一个方面,通常不是最重要的。只有当性能影响到用户体验时才需要进行优化。

相关文章

阅读 4.2k

动感小前端的专栏
后端黑长直,前端黑科技

动感小前端,动次打次

1.5k 声望
271 粉丝
0 条评论

动感小前端,动次打次

1.5k 声望
271 粉丝
文章目录
宣传栏