做当前这个项目也快一年半了,回头看看,前一年时间是在做重构,而后一年时间则是在打造一个新的产品。这里稍微总结一下做重构时所学到的一些东西吧。

  重构其实可以是不同目标的,有些人重构是为了让代码更合理,美观;而另一些人则可能是为了实现某个功能;重构也是有不同程度的,有的可能只是在函数、类级别做些修改,而有些则是要对整个的架构,模块做变动;同时重构的投入也是有很大不同的,有的只是在遇到不好的代码或设计的时候才进行修正,而有的则会专门组建一个团队花一大段时间来做重构。

  对基本概念的精确定义是一切讨论的基础,我这里讨论的重构是"为了实现某个功能而专门进行的大规模的代码改动"。

  架构设计,你能看的多远

  简单一点来说,我们要做的,是把一个软件的UI代码与核心功能彻底分开,然后把核心部分做成一个单独的产品。当然,这种所谓的表现层与业务层要分开的道理是谁都懂的,当初的架构里也的确加入了这些概念,但是由于没有严格要求,也从来不会把核心部分单独拿出来跑,经过近十年的开发,代码中核心层对UI的依赖已经相当严重,有静态的,源代码编译上的依赖,也有动态的,运行时的依赖。这个时候要抽取其核心功能,无疑是相当困难和费时的(代码量以百万行记)。看看现在网上的一些开源CAD软件,很多是一开始有明确的Core-UI划分,并且可以运行在核心模式或者UI模式。如FreeCAD。想想如果当年的架构师能够想到这一步,从一开始明确划分,可以肯定的是:一、后期无需花费那么多人力物力;二、其质量,设计会好很多。

  当然,这个其实也不一定是远见不够,一旦和商业利益结合起来考虑,很多好的设计是不得不被放弃的。举个例子,你的产品只是面向Windows用户的,而项目组里都是Windows程序员-为了更快更好的推出产品,你应该不会考虑跨平台吧-可是十年后,老大们决定向Mac进军了~~~所以说,这一块也是尽人事,听天命吧。

  工作模式

  一定要开出一个单独的branch来。这样你可以关起门来"为所欲为"了,不会影响到其他team。这里为所欲为指的是:

  BuildErrors是允许的

  因为是很大的codebase,你的某处修改可能在另外一处导致编译错误,或者你用script做的修改面特别广,而在本机做一个完整的build可能要半天(对的,即使用了IncrediBuild),那check-in之后让服务器帮忙去build,你可以继续工作了,有几个builderror,没关系!

  Regression是正常的

  每次check-in之前,不用跑那些自动化测试之类的。

  当然,这种自由在很多情况下是不提倡的,但在这里,却非常大的提高了效率。

  另外,因为重构的改动量是非常大的,所以要经常的与main或者trunkbranck进行sync,把单次变动控制在一个可以接受的范围内。

  如何保证质量

  上面讲到我们可以不跑自动化测试而check-in,那么如何保证质量呢。

  首先,你一定要有自动化测试-基于代码的单元测试也好,基于script的功能测试也好,只要是自动化的,并且覆盖率足够那ok了-在做重构,尤其是大规模重构的时候,没有自动化测试那简直是找死。

  因为我们是在自己的branch上工作,只要保证回到main/trunk的时候没有regression可以了,中间是什么状态,我们要求不是很高。一般的做法是:

  每周会跑一下smoketest和一些相关的acceptancetest,防止一些重大问题。

  每次和main/trunksync之前,我们都会花大概4天左右的时间来做"automationtriage"-把所有的自动化测试的case都跑一遍,拿到report之后逐个分析-或者逐批分析,因为很多failure都是一样的。

  这种做法极大的提高了效率-要知道,要把所有的case跑完,需要几十台服务器一起跑3天。

  如何管理代码

  重构涉及到很多文件的移动与分拆,需要注意两个地方:

  文件的历史信息不能断

  一个文件是怎么一步步改动过来的是非常重要的信息-你可以方便的查到谁在什么时候改过这个文件,怎么改的。移动,或者分拆文件是非常容易因为疏忽而丢失历史的操作。一定要正确的使用SCM工具来保持此信息,比如perforce里要用intergrate,而不是简单的add。