某公司员工小 A 即将离职,开始着手交接工作。员工小 B 被点名要求接手原先小 A 的工作。当小 B 打开小 A 独立负责的项目后,瞬间惊呆了……半个月后,小 B 基本消化了小 A 遗留下来的代码,此时业务部门发来邮件,说某系统出了一个 BUG,经技术部门排查发现可能出在原小 A 的代码中,于是小 B 开始 Debug,却无法确定 Bug 所在,几欲抓狂……又过了半个月,市场部负责人和技术部门负责人一起走到小 B 处,告诉他有个新需求要加一下,不用担心,只是一个小需求,于是小 B 开始在小 A 遗留的代码里添加「小的新需求」,却无法找到一些基础的组件,几欲抓狂……半年后,小 B 痛苦不堪,有了离职的打算……
上述场景是一个虚构的场景,假设了一个员工离开公司后可能对团队带来的影响。在这个虚拟场景中,我们可以归纳出该公司的一些问题:
- 小作坊式的开发模式
- 文档缺失
- 代码不规范
- 代码难以被扩展
- 项目结构不规范,不可避免地重复造轮子
- 缺乏代码审查
- 团队缺乏足够的沟通和经验交流
- 项目管理令人绝望
- ……
上述场景只是中国大陆 IT 行业的冰山一角,我相信还会有更加奇葩的故事等待着我们去挖掘。本文将重点讲述代码规范的重要性,以及做好代码规范后能给我们带来什么好处。本文不会告诉你代码规范的详细细节,这些细节因公司而异,因所使用的编程语言的不同而异,需要读者在工作和经验中进行总结,笔者避免越俎代庖。
开篇
高质量代码的三大要素:
可读性、可维护性和可变更性
做好代码规范、提高代码质量,能显著增强代码的可读性、可维护性和可变更性。本文将这三大要素合称为代码的读写可维护性,努力提高代码的读写可维护性,是做好代码规范的必要非充分条件。代码规范和架构设计是软件的灵魂所在,代码质量偏低,就像是人失去了三魂七魄中的一魄,就会丧失活力,影响正常运行,增加软件交付后维护成本,出现推迟完成、超出预算、特性缺失等现象。
任何语言都需要强调编码风格的一致性。只要是团队开发,每个人都以相同的方式编写代码就是至关重要的。这样大家才能方便地互相看懂和维护对方的代码。
O'REILLY Maintainable JavaScript,Nicholas C.Zakas
实际上,每家公司都会有一份(或多份)代码规范,因此提高代码的读写可维护性的关键在于是否能落实公司的相关文档,公司的技术总监、项目经理或相关代码审查机关是否具有应有的执行力。如果不能落实,那么即便代码规范画得再美,具体的代码也会丑到崩溃。
由于代码规范和架构设计是软件的灵魂所在,所以本文在介绍前者时将不可避免地涉及到后者。如果您不打算细读下文,那么您可以直接跳过,因为下文并没有向你展示一个全新的世界,也没有为你提供一个更加精彩的世界。下文是基于现有世界的事实基础上的再次演绎,不会颠覆你的认识,也不会告诉你斑马不是马而是驴,下文最大的特点是一而再、再而三地强调标题的重要性。因此笔者认为,如果您不是正在为公司内部演讲或毕业论文准备素材而烦恼,您大可不必浪费时间通读全文。
代码规范
如果不想为以后挖坑,做好代码规范是程序员和团队负责人、项目经理的必修课。如何保证当前项目开发过程中压力正常,而不是在后期面对过多的压力、以至于噩梦缠身?最简单的办法就是照看好你的代码,也就是落实好公司的代码规范工作。每天为此付出一丁点的努力,便可以避免代码「腐烂」,并保证代码产品日后不至于变得难以理解(可读性)和维护(可维护性)。
新项目开始着手开发时,它的代码很容易理解和上手。然而,随着开发过程的推进,项目不知不觉中演变为一个庞然怪物。发展到最后,往往需要投入更多的精力、人力和物力来让它继续下去。……开发人员在人称任务时,可能会难以抵挡诱惑为节省时间而走「捷径」。然而这些「捷径」往往只会推迟问题的爆发时间……
via 高效程序员的 45 个习惯——敏捷开发修炼之道
因此公司有必要制定或修订自己的代码规范,以确保规范本身不存在太大疏漏,而后加强执行力度,确保程序员自觉编写符合代码规范的编码产品。
每一种语言都有各自的特性,因此需要对每一种语言单独制定代码规范。但同一家公司的多份代码规范不能差异过大,应在兼顾语言风格和官方编码指南的基础上进行统一。
代码的可读性
代码的可读性是指代码让人容易阅读、跟踪和理解的程度。提高代码的可读性可以为代码阅读者节约时间(避免阅读时浪费过多无谓的时间)和精力(Debug、扩展功能或是性能优化的前提条件是你要读懂这段代码)。以下是摘选的可供参考的策略:
- 编码风格一致
- 代码清晰表达意图
- 写人看得懂的单词,如果选用英语,那么避免日语、法语和汉语拼音等,尽量使用语义化的命名组合;
- PIE 原则:意图清楚而且表达明确地编程;
- 能够让人快速看懂(最低限度的要求是自己一个月后能快速读懂);
- 恰到好处的注释
- 不能太多或太少,注释的形式根据代码具体的情况有不同;
- 避免用注释包裹代码;
- 尽量留下简明扼要的注释;
- 评估取舍
- 避免写一些现在不需要、将来也不太可能需要的功能:
- 不完美主义:不多写代码(如会话存储拆分);
- 避免做没有太大价值的优化工作;
- 区分任务的轻重缓急:
- 头疼医头也医脚:先容忍失败,再解决问题(如节点关闭逻辑);
- 不头疼不医头:量化分析(如参数调整回滚等);
- 综合考虑一下性能、便利性、生产力、成本和上市时间……
- 避免写一些现在不需要、将来也不太可能需要的功能:
- 简单就是美,避免简单的功能写出复杂的代码;
- 保持简单的代码远比写出复杂代码要难得多,但这是值得的;
- 不编写讨巧的代码;
- 避免无谓的条件嵌套和过度封装;
- 第一眼看上去就能知道其用处的代码,才是简单而美的代码
- 坚持操作方法的原子性,而后使用组合模式实现业务逻辑;
- 避免大段代码,要写高内聚、低耦合的代码;
- 「如果在方法中还使用到了
#region...#endregion
,那么你的方法需要拆分了」
——何镇汐
- 「如果在方法中还使用到了
- 等等……
Hoare 谈软件设计
软件设计有两种方式。一种是设计的尽量简单,并且明显没有缺陷。另一种方式是设计得尽量复杂,并且没有明显的缺陷。
——Charles Antony Richard Hoare
1934.1.11-,英国计算机科学家,排序算法中「快速排序」算法发明者,图灵奖得主
当你期待别人写出不让自己犯晕的代码的时候,你也要给别人带去一缕阳光,阅读这篇文章、这篇文章以及这篇文章会让你受益良多,这篇文章则介绍了项目依赖的一些技巧。
代码的可维护性
软件可维护性是指理解、改正、改动、改进软件的难易程度。通常影响软件可维护性的因素有可理解性、可测试性和可修改性。笔者这里将其分为两大类:编写时可维护性和运行时可维护性。
编写时可维护性
编写时可维护性是指在程序或系统上线后爆出 BUG,开发团队能够及时扑灭这个 BUG 且不会爆出其他 BUG。保持方法的原子性,提高代码内聚,能使某处修改的影响降到最低,这样某处方法出现 BUG,也不太会影响到其他模块的正常运作。编写时可维护性还包括了代码的「可测试性」。
JavaScript Module AMD 标准执行者(如 require.js)和 CMD 标准执行者(如 sea.js)要求谨慎使用全局变量,将变量限制于一个个模块之中,使得 JavaScript 代码变得更有条理且更便于维护。
运行时可维护性
运行时的可维护性是指在系统运行过程中(或无需再次编码发布、只需系统重启一次)修改系统的某项配置并使其生效,且不影响现在正在进行的业务和用户的操作。这要求软件工程师不能把代码写死。例如配置文件、数据库连接字符串、资源文件、日志等。
笔者曾有幸亲眼目睹将配置写死的后果:某系统会将处理完的图片存到指定的盘符(F:盘),一次演示中,由于演示设备只有 C 盘,演示到这一步后软件当场崩溃。
代码的可写性
代码的可写性包括「代码的可变更性」,代码的可变更性是软件理论的核心,OOSE 花了极大篇幅都在讲软件的变更;敏捷开发则天然地要求软件产品具有高逼格的可变更性。
代码的可写性是建立在代码的可维护性上的,而代码的可写性与可维护性又都建立在代码的可读性上。如果代码难以阅读,那么 BUG 的修正将变得难以入手,新功能的添加就更是无从入手了。
不过前人已经为我们指明了许多方向,例如设计模式、RDD、TDD、DDD 等。使用设计模式可以显著提高代码的可写性,尽管设计模式看上去难以理解且高深莫测,但通过多阅读优秀代码、优秀框架,多通过社区、群、博客、邮件组和小面积聚会与在此道经验富于己者交流,能让自己的水平快速上升。最关键的是多实践,实践是检验真理的唯一标准,也是掌握技术的唯一法门,前文小结中言及道:「(程序员)富有探寻事物本质之精神」便是此理。程序员几乎无一例外地会将自己看到的能使自己进步的知识与技巧立即(或不久之后)进行尝试(实践)。
代码重构
代码重构是代码可写性的一种特殊情况,把不稳定的积木塞塞紧。软件开发的整个过程也是一个不断迭代、不断优化、不断重构的过程。代码重构旨在不改变现有功能的基础上,通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性(可写性)和维护性(可维护性)。
重构的目的是最大限度避免软件走向生命周期的终结(维护开发成本高于开发一个全新产品的成本时,老的软件便步入了生命周期的晚期)。通过不断重构,纠正和改进软件设计,使代码更易为人所理解,有助于寻找到隐藏的代码缺陷,使系统对新需求的变更保持较强的适应能力(可写性)。
关于重构的知识就此打住,强烈建议感兴趣的读者购买《重构:改善既有代码的设计》和《大话重构》这两本书。
代码检查
代码检查应采取自动化检测与人工代码审查相结合的方式进行,后者倾向于更为重要的设计错误上(如 BUG、竞态条件和潜在死锁等)。
代码检查的目的是理解代码,改进代码,确保代码能正常工作,而不是批评任何人。
关于代码检查清单,可以阅读这篇文章;关于代码审查的专业知识与技能不是本文关注的重点,可以先阅读这篇文章,然后购买相关的专业书籍进行学习。
编码规范推荐
以下是一张可供参考的编码规范示例,供读者借鉴。
- C# 编码规范
- Java 编码规范
- HTML 编码规范
小结
自觉遵守代码规范要求,确保代码的读写可维护性,努力避免或缓解软件代码变质,是程序员的基本专业素质。同时落实好公司既有的代码规范,提高领导层与执行层的执行力,是团队负责人、代码审查机关的必要权力体现。
我们接着补完本文开头处小 B 的故事。小 B 经过谨慎思考后,最终决定离开这家公司,另觅东家。
参考资料与来源
- 编写可维护的JavaScript,Nicholas C.Zakas,人民邮电出版社,2013
- 代码规范的那些事儿,Kimy @CSDN,2012
- 代码规范的重要性,Kdboy @ITEYE,2009
- 再谈开发中的代码规范,Zachaty_Fan @CnBlogs,2014
- 高质量代码有三要素:可读性、可维护性、可变更性,光辉飞翔,2012
- 高质量编程规范,火龙果
- 团队项目开发“编码规范”系列
- 8 种常见的 Java 不规范代码
- C#代码规范 程序员必备的秘笈
- O'REILLY Maintainable JavaScript,Nicholas C.Zakas,Posts&Telecom Press, 2013
- Optimize for readability first,Valentin Simonov,2014
- Practices of an Agile Developer,Venkat Subramaniam,Posts&Telecom Press,2010
- 编写可读性代码的艺术,Boswell, D. / Foucher, T.,机械工业出版社,2012
- 防止代码变质的思考与方法,QQ客户端团队博客,2014
- 我们如何进行代码审查,InfoQ,2014
- 指数级增长业务下的服务器架构改造,梁宇鹏(环信首席架构师,前新浪微博通讯技术专家),2015
- 我理解的正确的代码,B1ncer @CnBlogs, 2015
修订历史
- 2015/06/25,完稿;
- 2015/06/27,第一次修订。
- 2015/12/04,第二次修订。
__EOF__