rules and traps for code refactor

先写个提纲吧,以后有空再写:

代码从来就不是一撮而就的,而是需要不断重构才能阻止代码腐烂保持健壮,具体到细节原因可能包含以下多种原因:

(1) Hard to add new logic due to complex
(2) Hard to do unit test
(3) Maintain effort huge
(4) Easy happen errors
(5) Code coverage is low
(6) look ugly
(7) New tech driving
(8) Have free time

总结起来,无外乎两种:“逼得”和“闲得”

重构的一些基本原则:

1 选择好时机:

1.1 主动出击式:
选择风险小的时候,例如新版本的开始阶段,而不是临近release阶段,集中时间去重构。
1.2 以逸待劳式:
做某项Task时,把需要修改代码的Scope部分或周边顺带做下重构。

2 做好充分的准备:

2.1 充分的技术储备

(1)所选择的技术方案是否成熟?
(2)是否有广泛应用的案例?
(3)是否有活跃的社区支持?
(4)是否有一些已知问题无法容忍等?

2.2 充分的评估

(1)需要多久完成?(2)需要得到其他的帮助么?

2.2 充分的风险规避

对于大风险的重构,考虑风险规避:

(1)Feature Toggle: 支持Feature Toggle来切换新老代码;
(2)分段提交:充分证明是稳定可靠后,再提交一部分;
(3)备份代码:留存有据可溯,出问题时一键rollback,或者面对质疑有据可查。(4)平稳切换:

2.3 充分的支持

(1)重构方案是否得到认可
(2)重构是否得到team的支持,或者有更高优先级的事情

3 充分的执行
3.1 具体的实施原则:
(1)重构还是重写?考虑是否直接重写,效益和速率更优;
(2)先大后小: 先解决主要矛盾,架构重构不要扣代码细节,细节重构不要扣架构;
(3)小步前进:避免大的风险;
(4)尽早提交:时间跨度越大,提交冲突的几率也越大,最后也容易被废弃。

3.2 充分的测试
3.3 充分的Review

4. 重构中容易犯的错

除了重构代码写错了,还有一些容易掉进的陷阱,例如:

4.1 编译器不提醒

例如重构了类名或者方法名等,导致用反射实现的一些功能不work.

(1) Spring AOP:

@Pointcut("within(com.xyz.someapp.trading..*)")
private void inTrading() {}

(2)Reflection for JDK

Class.forName("com.xyz.someapp.trading.test");

(3)Reflection for Test Lib

例如jmockit:

Deencapsulation.setField(OurInstance, "member", memberValue);

4.2 误确认IDE重构操作提示

有时候使用IDE提供的重构功能时,自己的误确认,会引发一些错误,例如重构时,修改某个变量名时,遇到配置文件里面也含有同名词,提示是否修改,不注意就确认容易引发难查问题。

4.3 别有用途

很多欠佳的设计,都会导致重构后发生一些错误。

(1)Log关键词抓取

某些特殊需求的应用例如监控服务以一些“关键词”检索做些逻辑,但是重构时,不小心去掉某些关键词,或者改名,导致这个功能不work.

09-02 2015 00:05:35:737 [metrics-logger-reporter-thread-1] INFO metrics – type=COUNTER, name=unavailables, count=0

(2) Empty Folder
一些应用在部署时,为了避免去创建某个特殊目录,在代码里面放置一个空目录,部署后刚好解压到预定位置,重构去掉后,导致不work.
empty_dir

还有一些比较隐蔽的逻辑,也容易让重构发生错误:

例如,一些dev在taskid后面追加一些其他信息,然后重构时,觉得没有用,直接删除,或者自己需要额外加一些功能时,直接也追加,导致原来别人追加的或被删除,或位置被置于中间,让原先的使用无所适从。

4.4 错上加错

例如下面的语句,单纯看,果断想去fix,但是fix后,引发了问题,因为基于这个错误已经做了一个逻辑。