一、背景
但凡工作过的同学都会亲自经历过或者听过各种故障。
轻则受到批评,重则影响绩效,甚至被罚钱、开除。
那么,作为 Java 工程师,我们该如何尽可能避坑呢,有没有一些经验可以交流分享的呢?
本文,结合自己的经验,谈谈自己的看法。
二、避坑宝典
2.1 遵循开发规范
强烈推荐大家阅读 孤尽老师的《阿里巴巴 Java 开发手册》,里面有很多具体的开发规范。
书中涉及:编码规约、异常日志、单元测试、安全规约、数据库、工程结构和设计规约等内容,条目众多,案例清晰,都是常犯的错误,血的教训。
这本书初看不难,但是开发中还会有很多同学违反其中的规范,导致出现一些BUG 和隐患,建议人手一本,反复阅读。
电子版下载地址: Java开发手册
2.2 多和master 比较
在开发过程中,多将当前分支和 master 分支进行对比。
以便能够:
(1)尽早发现误修改
(2)尽早发现 merge 错的代码
(3)自我代码审查
有时,我们多个功能一起开发,可能将项目 A 的改动放到了 项目 B 分支;有时,我们可能误删了某个文;有时,即使我们已经很小心翼翼, 合并代码发生冲突时,很有可能合并错误。通过和 master 分支比较,很容易发现这些问题。
通常我们写代码容易陷入某个类或者某个函数中,通过和 master 分支比较,我们更容易 zoom out,站在更高的视角看我的改动。对自己的代码进行审查,能够尽早发现一些问题。
2.3 静态代码检查
大公司有自己的 PMD 插件和文件,如果公司没有可以下载一些开源的 PMD 插件,对常见的规则进行扫描,常见的问题在开发阶段就尽早修复。
2.4 重视代码审查
插件能够检查出来的都是比较常见的问题,很多问题需要人工审查才能发现。
比如
(1)一些潜在的性能问题
(2)违反一些设计原则的问题,如违反高内聚,弱耦合原则;违反单一职责、违反开闭原则、违反迪米特法则、违反依赖倒置原则等。
(2)一些业务上未考虑全面的校验
(3)一些缺乏拓展性的设计
有些公司强制代码审查才可以合并到 master 分支,如果没有强制要求代码审查,建议主动和周围同事相互审查。
实际代码审查中我们的确也发现了很多问题,比如:
(1) for 循环中调用 RPC 接口 --> 使用批量接口提高性能
(2)敏感信息没有脱敏
(3)手动创建线程 --> 使用线程池
(4)没有关闭资源
(5)没有做好参数校验,可能存在安全性问题,可能存在潜在的空指针异常
(6)打印了很多非必要的日志,打印了大文本 -> 降低日志级别,精简日志内容等
关于代码审查,强烈推荐Jetbrains 公司出品的《What to Look for in a Code Review》 这本书。
该书从代码设计、代码的测试完整性、性能问题、数据结构是否合理、是否符合 SOLID 原则、是否符合安全性原则等角度谈代码审查。
书中针对不同的原则给出了与之匹配的案例,如:
其他参考文章:
《What to look for in a code review》
《Code Review Best Practices》
2.5 多看错误日志
有些功能由于在代码中有一些降级逻辑等原因,测试时功能看似符合预期,实际上会有一些报错。
有些功能偶发性报错,测试时可能没有注意到。
我们在自测、前后端联调和提测阶段,都应该勤看错误日志,才能尽早发现问题,才能尽早修复问题。
比如前后端联调时,前端同学触发了某个异常场景,出现了空指针异常,你在错误日志里第一时间发现,等她反馈给你时,你早已修复并重新发布。
比如测试同学验证你的功能时,触发了某个你未考虑的情况引发某个异常,你第一时间察觉并偷偷修复。
通过多看错误日志,可以降低沟通成本,尽早解决问题,尽早发现和修复一些潜在的隐患。
2.6 检查清单
可以通过思维导图或者笔记的方式将一些重要的设计原则,一些开发中自己常犯的错误等记录下来,每个项目开发过程中或者开发完毕后对着清单进行检查,可以极大提高编码质量,减少掉坑的概率。
比如一些重要的设计原则:
(1)高内聚、弱耦合
(2)降低复杂度(技术复杂度、业务复杂度等)
(3)设计模式的原则:单一职责原则、迪米特法则、里氏替换原则、接口隔离原则、开闭原则、依赖倒置原则
(4)面向失败编程。如果某某接口调用超时怎么办?是否需要兜底?是否需要重试?
(5)为了更好地拓展,还可以做哪些优化?
(6)是否存在哪些安全性隐患?
(7)是否存在一些复杂度较高的代码?
(8)...
比如一些自己常见错误:
(1)使用线程池时,不要忘记传递 trace 上下文,导致出错时 traceId 丢失。
(2)调用批量接口时,一定要注意下游对参数集合的数量限制。
(3)...
虽然很多原则大多数人都知道,但是写代码时,还会出现很多违反上述原则的情况。
因此编码过程中或者编码后期我们可以对着检查清单来看自己的代码,进行优化,可以少趟不少坑。
2.7 多看依赖的服务源码
不仅要埋头前行,还要抬头看路。
如果开发工期并不是特别紧,强烈拉取下游服务的仓库代码,查看当前功能需要对接的服务的具体实现,做到心里有底。
比如我们这个功能需要调用商品中心的批量接口,批量查询商品信息。
通过拉取商品中心的仓库代码,查看服务的源码我们发现下游对商品ID 的 size 有限制,超过 50 个会报错。
我们在开发阶段就会主动分批调用,而不是自测时都不超过 50 个商品,上线后出现超过 50 个商品报错的 BUG 后再去修改代码,重新发布。
我们多看依赖的服务源码后,开发功能就会心里有底,遇到问题我们能够更快地排查。
2.8 多看中间件源码
如果有时间常见的中间件的源码和原理都要掌握。
掌握好常用中间件的源码和原理才不容易误用,掌握好才能在遇到报错时知道为什么,尽快解决问题。
部分图书推荐:
(1)Spring 推荐《从 0 开始深入学习 Spring》、《SpringBoot 源码解读与原理分析》
(2)Redis 推荐 《Redis 深度历险》
(3)ES 推荐看 《Elasticsearch 实战》
(4)Dubbo 推荐看 《深入理解 Apache Dubbo 与实践》
(5)HBase 推荐看 《HBase 不睡觉书》
(6)Kafka 推荐看 《深入理解Kafka核心设计与实践原理》
(7) ...
2.9 多写单测
如果开发工期并不是特别紧张,尽量编写有效的单元测试。
我们通常要求单测覆盖率在 80% 以上。
很多常见问题在单测时就能够提前暴露。
有了单测,如果对某个方法进行了误修改,也能够尽早发现。
2.10 充分自测
我们自己要充分自测,避免前后端联调时出现很多问题,影响项目进度,避免前端、测试同学对自己能力产生质疑。
不仅要考虑正常场景,每个项目都要思考有哪些异常场景,才能尽量将问题暴露在自测阶段。
2.11 符合三板斧
所谓三板斧,即 可灰度、可监控、可应急。
功能要可灰度,不断切流,降低问题的影响面。
要可监控,通过监控尽早发现问题,而不是对用户造成实际损失时等待用户反馈。
要可应急,出现问题时可以尽早应急。
这里提一个小技巧,很多同学会对开发的功能留一些动态配置(如 apollo )的开关,当出现问题时快速切回老的逻辑。
2.12 复盘
并不是所有的项目问题都是技术问题,还有可能是规划问题、沟通问题等。
很多人做完项目并不会复盘,以前存在的问题以后还会存在,导致做了很多项目却没太大进步。
我们在做完每个大项目后都应该去复盘,避免下个项目还存在一样的问题。
比如这个项目联调前一两天没有和前端确认她是否开发完毕,等联调当天,前端同学还没开发,进度就会受到影响。一次下个项目联调前就要和前端同学确认是否可以按时联调。
比如这个项目工期比较紧,没有进行代码审查就测试上线了,最后出现了某个 BUG ,而如果进行代码审查,这个 BUG 很可能就可以避免。
比如产品的某个表达和我们的理解不一致,我们没有对概念进行对齐,验收阶段才发现问题。下个项目遇到容易有歧义或者自己并没理解好的地方要及时和产品拉通对齐。
比如有些新同学写系分不够详细,评估工作量不准确,导致项目延期。下个项目就让新同学系分写的详细一些再评估工作量,评估工作量时适当留一些 Buffer。
比如发布计划写的不够详细,发布前还在捋发布顺序,导致发布延期或者发布出现问题。下个项目发布计划一定要写详细一些。
三、总结
只有养成良好的编码规范,提高单测覆盖率,坚持经典的软件开发原则,坚持代码审查,坚持查看错误日志,坚持复盘等,才能尽可能降低趟坑的概率。
希望大家都能少一些 BUG ,多一些专业性;少一些故障,多一些涨薪,且行且珍惜。
创作不易,如果本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励,是我创作的最大动力。