技术债务的分类标准
这一节,我们要制定技术债务的分类标准,为后面的技术债务梳理,提供依据。
软件质量的衡量标准
技术债务出现的根本原因,是为了及时上线做了妥协,使用了不合理的实现方案。
梳理技术债务,我们首先需要明确什么是合理的实现方案,即什么是高质量的代码。
《Sonar code quality testing essential》一书中从七个维度定义了代码的这种内在质量,Sonar开发团队上纲上线的戏称为开发人员七宗罪:
- 编码规范:是否遵守了编码规范,遵循了最佳实践。
- 潜在的BUG:可能在最坏情况下出现问题的代码,以及存在安全漏洞的代码。
- 文档和注释:过少(缺少必要信息)、过多(没有信息量)、过时的文档或注释。
- 重复代码:违反了Don’t Repeat Yourself原则。
- 复杂度:代码结构太复杂(如圈复杂度高),难以理解、测试和维护。
- 测试覆盖率:编写单元测试,特别是针对复杂代码的测试覆盖是否足够。
- 设计与架构:是否高内聚、低耦合,依赖最少。
参考:代码质量与技术债
代码的坏味道
《重构》一书列出了代码的24种坏味道,及其对应的重构方法。
神秘命名(Mysterious Name)
重复代码(Duplicated Code)
过长函数(Long Function)
过长参数列表(Long Parameter List)
全局数据(Global Data): 全局数据的问题在于,从代码库的任何一个角落都可以修改它,而且没有任何机 制可以探测出到底哪段代码做出了修改,又难以定位bug。
可变数据(Mutable Data) :对数据的修改经常导致出乎意料的结果和难以发现的bug
发散式变化(Divergent Change) :一个变化导致多处代码修改,让需求变更之后的修改变的困难。
霰弹式修改(Shotgun Surgery) :遇到变化,需要在代码种进行多处细小的改动,修改时很容易漏掉。
依恋情结(Feature Envy) :一个函数与另外一个模块中的函数或数据交互的格外频繁。
数据泥团(Data Clumps) :类中的某些字段总是成组的出现,这些字段应该有属于自己的类。不要建大而全类。
基本类型偏执(Primitive Obsession) :用基本类型来代替,金额,坐标,重量这些数据。重构方式是,使用对象包装这些基本类型。
重复的switch (Repeated Switches) :在不同的地方反复使用同样的switch逻辑,的问题在于:每当你想增加一个选择分支时,必须找到所有 的switch,并逐一更新。
循环语句(Loops) :管道操作可以帮助我们更快地看清被处理的元素以及处理它们的动作
冗赘的元素(Lazy Element) :过度的封装,将简单到方法名称几乎和实现一样的方法提炼成方法,或将一个简单的方法独立成类。重构方式是:内联函数 (115)或是内联类(186)
夸夸其谈通用性(Speculative Generality) :设计和增加了一些自认为未来可能用的上的功能,企图以各式各样的钩子和特殊情况来处理一些 非必要的事情,让代码变的复杂。
临时字段(Temporary Field) :其内部某个字段仅为某种特定情况而设。这样的代码让人不易理解。重构方式是:使用提炼类将这些为某些特定情况而增加的字段独立出去,同时将相关的函数也搬移出去。
过长的消息链(Message Chains) :消息链指链式调用,例如用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另一个对象。
中间人(Middle Man) :某个类的接口有一半的函数都委托给其他类就是过度运用委托。
内幕交易(Insider Trading) :对于模块之前不可避免的数据交换都应该放在明面上。
过大的类(Large Class) :大类往往出现过多的字段,重复的代码就不可避免。类内如果有太多代码,也是代码重复、混乱并最终走向死亡的源头。
异曲同工的类(Alternative Classes with Different Interfaces)
纯数据类(Data Class) :一个类仅有一些字段和读写这些字段的方法,除此之外没有其他东西。
被拒绝的遗赠(Refused Bequest) :子类不应该继承他们不需要的数据和方法。尝试使用组合重构
注释(Comments) :如果需要注释来说明一块代码了做什么,或者是需要解释其行为,说明需要用提炼函数或者改名函数声明来重构。
技术债务的分类
结合上文的对软件质量的评估标准。
我们结合自己的实际需求,大致可以把技术债务分类成以下几类:
业务无关的债务
- 缺少编码规范
- 测试覆盖率低
- 缺乏监控
- 研发资产未被管理
业务相关的债务
- 现存或潜在BUG
- 缺少文档和注释
- 项目架构不合理
- 代码实现不合理
- 衡量指标包括:重复度、复杂度、是否带有“坏味道”等等
- 具体治理方式:参考《重构》这本书
- 第三方组件问题
- 组件是否安全、是否合理、是否导致包尺寸过大
- 已废弃的代码
- 性能问题与用户体验
- 性能问题:包括页面加载速度、页面错误率。
- 用户体验:包括loading、错误提示等交互是否完整
技术债务治理的原则
这一节制定标准,明确哪些技术债务需要治理,治理时机是什么时候。
何时治理
- 业务无关的技术债务,需要长期治理,将在业务排期较少的时候视情况排技术需求。
- 业务相关的技术债务,重构的最佳时机是在添加新功能之前。
哪些需要治理
- 业务相关的技术债务,长期未修改、功能稳定的代码,不需要治理。
- 低质量代码导致难以添加新功能时,可以考虑重构改功能相关代码。
重构原则
- 不要一边重构,一边添加新功能。
- 分小步走,每一个都有git提交,没一小步都需要回归测试原功能。
参考书籍
《重构:改善即有代码的设计》
《代码整洁之道》
参考文章
程序员应知——技术债务
代码质量与技术债
重构指北——《重构,改善既有代码设计》精读