写点什么

如何提高代码质量

2020 年 9 月 14 日

如何提高代码质量

说起代码质量,脑子里会冒出很多词,命名规范、格式规范、日志规范、单元测试覆盖率…

但我觉得,代码质量总结起来就两个:好看和好用。

好看是指代码可读性好,容易理解、容易维护,别人接手了不骂你;好用则指代码健壮,不容易出错,机器跑着不骂你。即使出错,也容易定位,容易止损和恢复。


为何需要提高代码质量?

以下是我认为的几点:


  • 提升代码的可维护性,降低新人接手的成本

  • 促进交流,促进知识共享,做好 backup

  • 促进风格一致,降低团队间应用流转的难度

  • 建设写好代码、做好设计的团队氛围


但有一点需要说明,我认为写代码本身是一个创造过程,能让人享受其中,如果有太多的条条框框约束,写代码就失去了创造的乐趣,所以,这里为代码质量建设立一个原则:


  • 只提供建议,不强制遵循

  • 鼓励创造性的编码

  • 鼓励艺术性的编码


如何才能拥有高质量的代码

有两种途径:


  • 第一种途径:先有好的设计—>然后用优秀的编码去实现—>再把优秀的编码风格延续下去

  • 第二种途径:从糟糕的代码开始—>不断去重构,向优秀的设计方案和代码风格不断逼近—>再延续下去


代码质量建设怎么开始呢?

首先得知道什么是好的代码, 这就要有标准,那就是我们常常看到的各种各样的规范,但我觉得要有几个简单的原则,太多了,记不住,有几条原则简单的原则,可以时不时拿来判断,当前做得对不对。


然后就是去实践规范,这里需要一些技巧、一些工具,来帮助我们更好地遵循规范。


接着是度量, 看我们对规范实践的效果,这就是我们常说也常做的 Code Review,但 Code Review 也需要遵循一定的规范,应用一定的技巧。


度量之后是改进, CR 结果要及时跟进,这是最重要一环,否则 CR 就没有实际意义。


总结不可少,复盘是一种很有用的工具,CR 也需要复盘,总结 CR 流程、过程等方面好的和不好的地方,更新规范和 checklist。


接下来我们分别聊一聊各个步骤。


规范: 先知道什么是好代码

从上边高质量代码的诞生途径我们可以看出,设计也是很重要的一环,所以我们的规范包括设计规范和编码规范,结合我们的生产实际,这里加上安全生产的规范,所以规范有 3 部分:设计、编码、安全生产。


设计: 先有优秀的方案

设计推荐多用图表达,图比文字有更直观的传达能力:


首先是业务流程图, 它能快速构建起我们对业务的认知,带着对业务的理解再来看代码,事半功倍。


然后是用例图, 清晰地表达出我们系统的职责、边界、服务对象,结合业务流程图,能快速构建起我们对系统职责的认知。


接着是架构图, 从我们日常的设计需求来看,架构图是需要的。好的架构图能快速给人搭建起理解的框架,再来看系统的细节部分,就很好理解。架构图推荐 C4 规范,它是我目前接触的表达最清晰的架构图规范。


接着再用时序图、状态图、ER 图 等把关键和复杂部分的设计表达出来。


但日常我们的需求有大有小,方案也不需要都遵循统一的范本,为了设计而设计,就徒增加工作量了。以按需为第一原则,能把要做啥,怎么做的表达清楚即可。这里按场景推荐各个图的使用场景:


新建应用/对原有应用进行重大修改/复杂项目


  • 业务流程图(交代业务背景)

  • C4 的系统上下文、容器、组件这 3 张图

  • 用例图:有多个外部参与者

  • 类图:关键模型超过 5 个

  • 状态图:对象状态超过 3 个

  • 时序图:关键流程或复杂链路的参与对象超过 3 个

  • ER 图:涉及数据库变更(包含数据表结构文档)


一般项目/重大日常


  • 业务流程图

  • 时序图(复杂功能、关键流程)


日常


  • 按需


编码: 优秀的方案需要优秀的编码

编码最重要的是可读,控制复杂度,做到自解释,能让人像读自然语言一样读自己的代码,这是最高境界,也是神仙境界。然后是可维护性和可变更性,能快速、安全地修改代码是目标。最后是对优雅实现的要求,卓越的代码会让人拍着大腿叫好,这个不稀奇,我们乱糟糟的代码里也偶尔会有闪光的片段。


编码最高原则:


可读性


  • 控制复杂度

  • self-document


可维护性


优雅


✎ 分层规范

合理的代码分层,能控制各层的复杂度,以分层的思路去设计,也能提高代码的复用性。对于分层,我认为熟悉的就是好的,能满足工作中的大部分情况就好,这里不谈六边形架构、清晰架构、DODAF 等概念,自己驾驭不了,还不能拿出来吹。我推荐 DDD 最基础的 4 层分层架构,如下:


用户界面/接口层   应用层   领域层 基础设施层
复制代码


这里举个我实际项目中用到的例子:


-- bootstrap    -- BeanConfig-- application    -- pv        -- ChannelPvApplicationService    -- sns-- domain    -- abtest        -- AbtestService    -- address    -- coupon        -- entity            -- Coupon            -- CouponStatus            -- CategoryCouponTemplate    -- category    -- user        -- UserRepository        -- service            -- OneIdService            -- UserService    -- item        -- ItemRepostory    -- live        -- LiveStatus-- infrastructure    -- concurrent        -- ThreadPoolExecutorFactory        -- MonitorableCallerRunsPolicy    -- dal        -- IGraphDal        -- TuringDal        -- DefaultUserRepository    -- dao        -- MybatisItemDao    -- util        -- DateUtil        -- MoneyUtil        -- UriUtil    -- monitor        -- Event        -- Timing        -- TimingAspect        -- TimingEvent        -- Monitors-- view    -- atomicwidget        -- BannerWidget        -- CrazySubsidyWidget        -- FeedItemsWidget        -- NavigateBarWidget        -- LiveWidget    -- page        -- HomeScreenPage        -- CategoryFeedsPage        -- SearchCardPage    -- widget        -- Widget        -- DispatchableWidget        -- Debuggable        -- AbstractWidget        -- AbstractDispatchableWidget        -- WidgetDispatcher        -- WidgetResult        -- WidgetContextIncompatibleException
复制代码


上述项目结构中,因为是导购项目,view 相当于用户界面层,application 是应用层,domain 是领域层,infrastructure 是基础设施层。


再对包的划分说明一下:


  • 领域对象、值对象、DTO、Service 等定义都放在子域的包下,不要有大而全的 entity、service、impl 等包(这里的子域是一个内聚的逻辑概念,对应的是领域设计里的子域,如上例中的 item 在我们的导购里就是商品这个子域)

  • 常量定义尽量跟着相关的类走,作为类的静态字段,不要有大而全的 Constant 类(Switch 相关的除外,但也要按职责尽量拆分开关类)


✎ 代码规范

代码规范就推荐阿里经济体开发规约,很全面,也是阿里同学的基本要求。代码规范就推荐「阿里经济体开发规约」,很全面,也是阿里同学的基本要求,开源版本:阿里巴巴 java 开发手册 https://github.com/alibaba/p3c


结合自己的经验,重点说几点:


命名


  • 命名不用泛称(反例:processData)

  • 尽量用完整的单词描述清楚作用和意图,不要怕字多

  • 对象后缀领域对象不带后缀 DTO:RPC 接口提供的对象以作为 VO:跟前端交互的对象 PO:跟数据库直接交互的对象


日志


  • 所有后台都要有操作日志、数据变更日志

  • 日志要配置异步写盘

  • 线上仅保留 WARN 和 ERROR 级别日志

  • 所有日志都要有 traceId

  • 异常日志要有堆栈、入参、能说清楚是什么错误的信息(可以出统一组件)

  • 打印日志时,禁止直接用 JSON 工具将对象转换成 String


异常


  • 怎么抛:尽量使用非受检异常,提高代码可读性

  • 怎么处理:统一异常用切面处理,或依赖 SpringMvc 的 ControllerAdvice 统一处理

  • 异常 catch 范围尽量小,分清稳定代码和非稳定代码

  • 禁止直接吞掉异常

  • 时刻警惕 NPE,多用 Optional 处理


注释


  • 注释只为了说明为什么这么做,不用来说明是在做什么


面向对象


  • 遵循原则:SRP/OCP/LSP/ISP/DIP

  • 尽量只暴露行为,不暴露数据

  • 慎用继承,优先使用组合方式


其它规范


  • 方法行数保持在一屏之内(30 行以内)

  • 代码提交 commit message 一定要讲清楚做了啥控制每次提交的代码量(一个功能一提交)

  • 参数尽量用不可变对象(不对入参做修改,保持明确的入参和出参)尽量不用隐式入参(ThreadLocal)

  • 数据结构无随机读取时,用 LinkedList 替代 ArrayList

  • 风格做好分层,同层用统一的风格(设计/编码)


安全生产

安全生产还没有系统总结过,结合自己做稳定性的工作经验提几点,后边跟负责安全生产的同学多学习学习,再来更新:


防资损


  • 要有资损评估/监控


易恢复


  • 任何新功能上线都要有灰度能力


监控/报警


  • 兜底设计/监控

  • 性能监控

  • 异常监控

  • 低容忍错误要报警

  • 关键指标要监控(业务/技术)

  • 减少不必要的报警


降级/限流


  • 识别出弱依赖,保证弱依赖可降级

  • 识别出可限流的依赖方,做好监控和限流配置


实践: 如何去实践规范+

给一些原则和技巧建议,帮忙落地规范。


设计

  • 图都不是必须的,只要能讲明白是怎么做的,为什么这么做


编码

  • 使用 Aone-Idea,它已经集成了 PMD/FindBugs/CheckStyle 功能,给开发的同学点个暂,超牛逼。开源版本:Alibaba Java Coding Guidelines alibaba https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines

  • 使用 lombok,保持代码的简洁性

  • 不断重构,且遵循以下原则: DRY/YAGNI/Rule Of Three/KISS/POLA 每次需求都是重构契机反问自己,能不能在[可读性/易维护性]做得更好

  • 使代码读起来像自然语言

  • 功能性代码和非功能性代码分离


安全生产

这得跟着公司和部门规范来,学习学习再来补充。


度量: 如何去验证实践效果-CodeReview

Review 时机


  • 项目提测后第一时间:不要在项目上线的前夜 review,来不及改,review 结果容易搁置,浪费参与人的青春


Review 方式


  • 小模块:随时/Aone 代码评审/@backup 同学

  • 项目代码:面对面投屏/Aone 代码评审 + IDE show/项目组+重点关注同学


Review 内容


  • 关注代码的设计是如何落地需求的

  • 总体流程

  • 关键设计

  • 重点功能


Review 前提


  • 代码是编译通过的

  • 开启 Aone-Idea 的实时检测


Checklist


  • 规范性


可读性


可维护性(高内聚低耦合、面向对象原则、实现复杂性等)


可变更性(扩展性等)


  • 安全性/健壮性输入检查异常处理边界检查

  • 性能依赖合理性


改进: 跟踪 CodeReview 结果的执行

  • 有运行时风险的问题必须上线前完成改动

  • 其它问题尽量上线前完成修改,如未修改则要加 todo,指定人和时间来修改

  • 用工具来统计和提示 CR 结果改进情况


总结优化

  • 定期回顾和总结(周会环节)

  • 更新 checklist 和代码规范

  • 发现好的代码和设计,定期展示,给与奖励


后话

以上是摘取的各家之言,加上自己的一些思考。学习是个渐进过程,代码质量的学习我还在进行中,如果有收获,会来更新。如果被人 diss,我还觉得有理,我也会更新进来。


写的这些也是我自己的学习和实践方向,所以,如果发现我的代码没做到这些,吐槽我,然后给我建议,让我做得更好。


本文转载自公众号淘系技术(ID:AlibabaMTT)。


原文链接


如何提高代码质量


2020 年 9 月 14 日 14:064159

评论 2 条评论

发布
用户头像
“打印日志时,禁止直接用 JSON 工具将对象转换成 String” 请问下,这个是基于什么考虑呢?
2020 年 09 月 22 日 10:12
回复
用户头像
”第二种途径:从糟糕的代码开始—> 不断去重构,向优秀的设计方案和代码风格不断逼近—> 再延续下去“, 大多数情况是第二种途径
2020 年 09 月 15 日 10:28
回复
没有更多了
发现更多内容

自助设备系列——上下游

孙苏勇

产品 行业资讯 智能设备

Linux-常用命令

Flychen

Linux

tcp_tw_recycle 【坑】

孤星可

TCP 服务端

Flutter Andorid真机或打包APK杂症记录

北风烈

flutter 打包APK

嵌套文件夹复制实现

Howe

Java 文件复制

5G来临,我们该如何打造自己的家庭数据中心基础篇

ABC实验室

5G 数字资产 家庭数据中心

告别手写,一键生成 Helm Chart README

郭旭东

Kubernetes Helm

从全国首起暗网案件告破说起——暗网,超乎你想象

石君

网络安全 暗网 洋葱网络

最新Idea 2020.1 二种方法激活教程

公众号:V5codings

intellij-idea

docker安装mysql5.7并挂载目录到本地

桥哥技术之路

Docker

当我们说文本编辑器时,到底在说什么

潘浩腾 Peter H. Pan

写作平台 InfoQ markdown

高仿瑞幸小程序 03 创建轮播图

曾伟@喵先森

小程序 微信小程序 前端 移动

打造个人商业模式第一步

一尘观世界

副业赚钱 提升认知 思维方式 商业模式 认识自己

Kafka系列第5篇:一文读懂消费者背后的那点"猫腻"

z小赵

大数据 kafak 实时计算

使用JUnit、AssertJ和Mockito编写单元测试和实践TDD (二)为什么要写单元测试

编程道与术

编程 编程语言 TDD 代码审查 单元测试

freecplus框架,Linux平台下C/C++程序员提高开发效率的利器

C语言技术网-码农有道

Grafana+Prometheus(InfluxDB)+Jmeter使用Nginx代理搭建可视化性能测试监控平台

软测小生

Grafana Prometheus Influxdb Jmeter 性能测试

C语言数据类型

C语言技术网-码农有道

听保洁老大爷讲Java的垃圾回收

侯树成

JVM

2020年程序猿必读10本好书推荐

ABC实验室

学习 2020 程序员 好书推荐

我的工作原则与思考

梁帅

互联网 工作效率 原则

有没有什么上古的程序代码至今依然没被更替?

极客时间

编程 程序员 开发

分析Kubernetes技术体系的层级,慎用比较前沿的技术

韩超

python 文章中图片下载

Flychen

为什么公众号订阅没有「分组」的功能?

Fenng

微信公众平台 产品设计

2020版Kubernetes快速上手指南,让你所见即所得

ABC实验室

Kubernetes 容器 云原生 群集安装 新手指南

NumPy 运算规则总结

张利东

Python

C语言程序的基本结构

C语言技术网-码农有道

C/C++

自动化测试框架知识,读这一篇就够了!

禅道项目管理

程序员 DevOps 自动化 测试

聊聊“坚持”这件事

小天同学

个人成长 写作 坚持 自控力

给程序员的错误找个台阶

曲水流觞TechRill

如何提高代码质量-InfoQ