如何提高代码质量

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 说起代码质量,脑子里会冒出很多词,命名规范、格式规范、日志规范、单元测试覆盖率...但我觉得,代码质量总结起来就两个:好看和好用。好看是指代码可读性好,容易理解、容易维护,别人接手了不骂你;好用则指代码健壮,不容易出错,机器跑着不骂你。即使出错,也容易定位,容易止损和恢复。

原创 朱天富(海培) 淘系技术  2020-09-04


为何需要提高代码质量?



以下是我认为的几点:


  • 提升代码的可维护性,降低新人接手的成本
  • 进交流,促进知识共享,做好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,我还觉得有理,我也会更新进来。


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

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2月前
|
SQL JavaScript 安全
代码审查
【10月更文挑战第13天】
|
2月前
|
设计模式 关系型数据库 测试技术
进阶技巧:提高单元测试覆盖率与代码质量
【10月更文挑战第14天】随着软件复杂性的不断增加,确保代码质量的重要性日益凸显。单元测试作为软件开发过程中的一个重要环节,对于提高代码质量、减少bug以及加快开发速度都有着不可替代的作用。本文将探讨如何优化单元测试以达到更高的测试覆盖率,并确保代码质量。我们将从编写有效的测试用例策略入手,讨论如何避免常见的测试陷阱,使用mocking工具模拟依赖项,以及如何重构难以测试的代码。
58 4
|
2月前
|
Java 测试技术 程序员
如何提高代码质量?
本文从软件和硬件两个角度探讨了如何提高代码质量。在软件方面,文章强调了代码规范、测试、Code Review及编程原则的重要性,详细介绍了命名约定、缩进、注释等方面的准则;在硬件方面,则推荐了一套高效的开发工具组合,特别是针对开发者的明基RD280U显示器。无论是提升代码质量还是保护视力,都是程序员不可忽视的重点。欢迎分享你的开发工具,一起提升代码质量,爱护双眼。
36 0
|
3月前
|
开发者
代码审查的艺术:提升团队协作与代码质量
在软件开发中,代码审查是提升代码质量和促进团队协作的关键实践。本文探讨了代码审查的重要性、最佳实践及其面临的挑战。通过制定明确的审查指南、利用自动化工具、提供建设性反馈等方法,可以显著提高代码质量并促进知识共享。尽管存在抵触情绪、时间投入等问题,但通过团队合作、合理安排时间和培训审查者,可以有效克服这些挑战,实现代码审查的最大价值。
|
4月前
|
Java 测试技术 开发者
提高代码质量:深入实践测试驱动开发(TDD)
【8月更文挑战第14天】测试驱动开发是一种强大的软件开发方法,它通过先写测试再编写代码的方式,显著提高了代码质量。通过实践TDD,开发者可以编写出更可靠、更易于维护的代码,并加速开发进程。虽然TDD需要一定的学习和适应过程,但其带来的长期收益是不可估量的。如果你还没有尝试过TDD,现在就开始吧!
|
7月前
|
测试技术 开发工具 开发者
如何提高代码质量
在编写代码的过程中,我们注重代码的功能性和效率性,但是往往忽略了代码的可读性、可维护性和可扩展性。本文将分享一些技巧和建议,帮助您提高代码质量。
|
7月前
|
程序员
提高代码质量的五个技巧
【2月更文挑战第2天】写出高质量的代码是每个程序员的追求,但是实现这一目标并不容易。本文将介绍五个技巧,帮助你提高代码的质量。
|
Java 测试技术 C语言
代码质量保障第2讲:单元测试 - 浅谈单元测试
代码质量保障第2讲:单元测试 - 浅谈单元测试
154 0
|
SQL 安全 测试技术
如何进行高效的代码审查
代码审查是软件开发过程中至关重要的一环。它是指由开发团队中的其他成员对代码进行检查,以确保代码的质量和一致性。 代码审查可以帮助发现潜在的问题,例如内存泄漏、安全漏洞或性能问题。通过及早发现这些问题,可以避免它们在后期的软件开发过程中变得更加复杂和昂贵。
186 0
|
开发者
聊聊软件开发的代码审查
聊聊软件开发的代码审查
聊聊软件开发的代码审查