目录 · 12 个章节
重构,是一些项目进展到一定阶段必需做的一件事,前面打天下的人跑路了,后面接盘的人无法接手。推倒重来吗? 显然不能,祖传下来的烂代码不是一天两天产生的,推倒重来意味着前人踩过的坑要重新踩一遍,我曾经工作过的一家公司老项目是coffeescript写的,烂到没有一个人能完全hold住整个项目,重构时采用完全不同的另一门语言,造成在这个过程中无数的bug,这重构+修bug的过程真的不是在用另一门语言重写一遍烂代码吗?(烂代码的产生往往是因为赶进度和修紧急bug,程序猿得到足够休息以及好心情才能产出优美的代码,因此产生了程序猿鼓励师这一职业)
我最近重构了一些公司项目的代码,总结了一些经验,按照这些流程下来,基本上做到了零风险
格式化代码
没有任何风险,除非工具有问题,良好的代码格式有利于后面的工作
去除未使用的代码
同样没有任何风险,主要去除掉未使用的导入模块、变量,无意义的代码。 但要注意任何注释不要删,注释掉的代码没研究清楚前可能有用,一些奇怪的注释可能是前人留下的坑
使用更先进的启动方式
原有的老项目是使用forever进行启动的,但这个工具功能少并且已经两年没有更新维护了,pm2有更强大的功能,从forever迁移到pm2需要修改的代码很少,并且不会修改业务代码,风险是很低的
拆分不合理模块,用到哪个导入哪个,不允许在一个文件里面导入导出
module.exports = require('underscore');
exports.log = require('./nlog');
有的人喜欢偷懒,于是就这么干了,对于第二行代码,有时候这样写也是可以理解的,但第一行是应该绝对禁止的,这样有可能导致后面的代码覆盖掉前面的代码。
所有的导入不能依赖导入顺序
// a.js
const a = require('./b').db;
// b.js
exports.init = function(){
exports.db = connectToDatabase();
}
上面的代码使得a.js必需等待b.js初始化完毕才能使用,导致require()甚至需要在函数里面去执行
const b = require('./b');
async function start(){
await b.init();
const a = require('./a');
a.xxx();
}
但是,b.js又必需先初始化。注意,不要把初始化顺序和导入顺序搞混,只要a.js稍微改变一下,代码就清晰了,所有文件都可以将require放到文件顶部,无需担心任何的副作用
// a.js
const b = require('./b');
exports.xxx = function(){
const db = b.getDB();
db.xxx();
}
禁止在导入的模块上加东西
const a = require('a');
a.haha = function(){}; // 禁止
这种代码同样引起了导入顺序的依赖,必需绝对禁止
exports.register = function(name){
exports[name] = function(){}
}
虽然js代码的动态性有时候真的非常方便,但绝对不能滥用,这种代码使得在运行时才能确定模块里面有什么,编辑器完全无法跟踪
禁止修改传入参数
一个函数的执行结果绝对不能影响传入参数的值,js自带的一些操作数组的函数违反这一原则,实际上是很不方便的,我不常用那些,每次用到都得去查一下手册
见过一个例子,去修改ctx.query,什么都往里面放,最后还整个存到mongo,后面的系统还会取出来用,结果导致这部分代码几乎完全没有重构的可能,因为query有用户传入的键名,根本不知道会用到什么,除非对整个系统进行全面分析整理
在闭包中修改闭包参数是极其危险的。
明确变量的生命周期
变量的生命周期尽可能短,尽可能使用const限定禁止修改
禁止使用特殊的技巧
const v = 3.14;
const result = v | 0;
这是一个取整的技巧,这样的技巧晦涩难懂,如果一定要用,记得用函数封装起来,并写上详细的注释
三遍或更多遍检查
再小心的改代码也会出错,多检查几遍
少量多次的优化
这些代码健康化的过程中,每做一步提交一次代码,即使你在改变量名的过程中发现某个逻辑很不合理,改起来又很简单,你也不要在一次提交中修改,这样便于以后再次对提交的代码进行审查
下面是一些风险比较大的操作
更新模块
项目使用的模块可能已经很旧了,升级是有必要的,升级时需要注意新版本的模块与旧版本有可能是不兼容的,必需要严格测试过
使用typescript
到这一步,代码已经比较健康了,但动态类型仍然隐藏了许多难以发现的bug,typescript带来了类型检查,从js迁移到ts并不难,在迁移的过程中,许多隐藏的bug很容易就可以发现并解决,使用ts之后bug量甚至可以减少一半以上
使用其它语言重构
用其它语言重构是当前的语言已经无法满足项目的需要时才应该考虑,否则意味着前人踩过的坑又得重新踩一遍