装箱百万奖金,第六届全国工业互联网数据创新应用大赛火热报名中! 了解详情
写点什么

做了 1000 次 Code Review,我学到 3 点经验

  • 2020-02-08
  • 本文字数:2321 字

    阅读完需:约 8 分钟

做了1000次Code Review,我学到3点经验


当我在 LinkedIn 工作时,工作的很大一部分内容是做 Code Review。在这个过程中,我发现一些人很容易犯的错误,于是把它们整理起来并分享给团队。


经验 1:当出现错误时 Throw an exception

我看到的一个常见模式是:


List<String> getSearchResults(...) {  try {    List<String> results = // make REST call to search service    return results;  } catch (RemoteInvocationException e) {    return Collections.emptyList();  }}
复制代码


上面的方法可能是很多新手工程师的做法,但这种模式会有问题。在我曾经参与的移动应用中,这种模式导致移动应用程序的故障。用户搜索开始后,我们的后端发生错误开始 throwing exceptions,但在应用程序的 API server 中并沒有 throwing exceptions。


因此,从应用角度看,前端会收到 200 个成功的响应,然后显示空白的搜索结果给使用者,而团队却毫不知情。


如果 API thrown an exception,那我们的监控系统会立刻发现它,并能及时修复。


很多时候,当捕捉到异常后,我们倾向于返回 empty object。Java 中 empty object 的样例包括 Optional.empty()、null 和 empty list。这种情况经常发生在 URL 解析中。如果 URL 无法从字符串解析得到的话,不要返回 null,而要问问自己:


URL 格式为什么是不合法的?这是一个需要在 upstream 解决的数据问题吗?


对于这种任务来说,empty object 并不是恰当的工具。如果出现异常行为,那么就应该 throw an exception。


经验 2:尽可能使用最具体的类型(type)

基本而言,这条建议恰好与 stringly typed programming 相反。


我经常看到下面所示的代码:


void doOperation(String opType, Data data); // where opType is "insert", "append", or "delete", this should have clearly been an enum
String fetchWebsite(String url);// where url is "https://google.com", this should have been an URN
String parseId(Input input);// the return type is String but ids are actually Longs like "6345789"
复制代码


用最具体的类型(type)可避免很多 bug。


现在问题是:好心的程序员为什么会写出糟糕的 stringly typed 代码?


答案在于外部世界不是强类型的。字符串有很多不同的来源,比如:


  • url 中的查询和路径参数

  • JSON

  • 不支持枚举的数据库

  • 编写糟糕的库


在上述场景中,我们应使用如下的策略来避免该问题:将字符串解析和序列化放在程序的边缘之处。


下面是这样一个样例:


// Step 1: Take a query param representing a company name / member id pair and parse it// example: context=Pair(linkedin,456)Pair<String, Long> companyMember = parseQueryParam("context");// this should throw an exception if malformed
// Step 2: Do all the stuff in your application// MOST if not all of your code should live in this area
// Step 3: Convert the parameter back into a String at the very end if necessaryString redirectLink = serializeQueryParam("context");
复制代码


这种方式有很多优点。立即发现格式错误的数据;如果出现任何问题,应用程序将提前 fails。数据被验证一次后,不必在整个应用程序中继续捕获解析异常。


此外,强类型使方法签名更具描述性,我们不再需要在每个方法上编写那么多的 javadocs。


经验 3:用 Optionals 而非 nulls

Java 8 带来最棒的特性之一是Optional类,它代表一个可能存在也可能不存在的实体。


一个小问题:


唯一拥有自己缩写的例外(exception)是什么?答案是 NPE 或空指针异常。截至目前,它是 Java 中最常见的异常,并被称为价值10亿美元的错误


Optional能让我们完全从程序中移除 NPE。但是,必须以正确的方式使用它。如下是关于使用Optional的一些建议:


  • 我们不能在得到Optional的任何时候都简单地调用它的.get(),相反,我们要仔细考虑Optional不存在的情况并给出一个合理的默认值;

  • 如果还没有合理的默认值,那么像.map().flatmap()这样的方法允许我们推迟到以后再做决定;

  • 如果外部库返回null来表示为空的情况,那么立即使用Optional.ofNullable()wrap 该值。相信我,你以后会感谢自己的。null 值在程序内部有“bubble up”的倾向,所以最好在源代码中停止它们;

  • 在方法的返回类型中使用Optional。这种做法非常好,因为我们不需要读取 javadoc 来确定值是否可能不存在。


额外建议:尽可能采用“Unlift”方法

我们应避免下面所示的方法:


// AVOID:CompletableFuture<T> method(CompletableFuture<S> param);// PREFER: T method(S param);
// AVOID:List<T> method(List<S> param);// PREFER:T method(S param);
// AVOID: T method(A param1, B param2, Optional<C> param3);// PREFER:T method(A param1, B param2, C param3);T method(A param1, B param2);// This method is clearly doing two things, it should be two methods// The same is true for boolean parameters
复制代码


上述不推荐使用的方法有哪些共同点?那就是它们都使用了 container objects 作为参数,比如 Optional、List 或 Task。


如果返回类型是相同种类的 container,那就更糟糕了(比如,param methods 接收 Optional,返回值也是 Optional)。


为什么呢?


1)Promise<A> method(Promise<B> param)要比 2)A method(B param)更缺少灵活性。


如果有一个Promise<B>的话,我们可以用 1),也能通过.map函数使用 2)(即promise.map(method))。


但是,如果只有一个 B 的话,我们很容易使用 2),但是无法使用 1),这样来看,2)是更具灵活性的方案。


我喜欢将其称为“unlifting”,因为它与常见的函数式工具方法“lift”恰好相反。采用这种方式进行重写会让方法更具灵活性,对调用者更加易用。


英文原文:


What I Learned From Doing 1000 Code Reviews


2020-02-08 07:006770
用户头像
张卫滨 业精于勤,行成于思。

发布了 440 篇内容, 共 254.8 次阅读, 收获喜欢 529 次。

关注

评论

发布
暂无评论
发现更多内容

这份562页的算法知识点集结刷题手册,将是你拿大厂offer的突破口

了不起的程序猿

程序员 算法 编程语言 java程序员 大厂面试

魔豹联盟:佛萨奇2.0dapp系统开发模式详情

开发微hkkf5566

开源一夏 | 计算机网络:物理层

是Dream呀

开源

「全球数字经济大会」登陆 N 世界,融云提供通信云服务支持

融云 RongCloud

isc N世界

全面认识二极管,一篇文章就够了

矜辰所致

ESD二极管 8月月更 二极管 电子设计基础 TVS二极管

我用这一招让团队的开发效率提升了 100%!

Liam

程序员 前端 后端 开发 API

百问百答第49期:极客有约——国内可观测领域SaaS产品的发展前景

博睿数据

可观测性 智能运维 博睿数据 极客有约 中信证券

KunlunBase 1.0 发布了!

KunlunBase

国产数据库

开源一夏 |【云原生】DevOps(五):集成Harbor

是Dream呀

深入理解IO流(第一篇)

JAVA活菩萨

Java 程序员

不改一行源码,实现 sentinel-dashboard 所有配置支持 apollo 持久化

铁匠

微服务 sentinel 流量控制 sentinel dashboard

什么是SVN(Subversion)?

龙智—DevSecOps解决方案

svn 版本控制 版本管理 版本控制软件

天翼云4.0分布式云赋能千行百业数字化转型

天翼云开发者社区

云存储 云上架构

企业云成本管控,你真的做对了吗?

Kyligence

云成本管控 云成本分析

千万级QPS下服务如何才能平滑启动

HelloGeek

微服务架构 Service Mesh 高并发优化

面试官:可以谈谈乐观锁和悲观锁吗

JAVA活菩萨

面试官

开源一夏 | Python Web开发(八):后端开发中的增查改删处理

是Dream呀

开源

安全至上:落地DevSecOps最佳实践你不得不知道的工具

龙智—DevSecOps解决方案

DevOps DevSecOps

开源一夏 | 数据结构课设:图书信息管理--顺序存储和链式存储

是Dream呀

开源

搭建属于自己的知识库(Wikijs)

开源 wiki 知识库 8月月更

一朵“云“如何带来产业新变革

天翼云开发者社区

云网融合

开源一夏|数据结构课设:基于字符串模式匹配算法的病毒感染检测问题

是Dream呀

开源

玩转云端 | 天翼云对象存储ZOS高可用的关键技术揭秘

天翼云开发者社区

云平台 云存储

发挥云网融合优势,天翼云为政企铺设数字化转型跑道

天翼云开发者社区

数字化转型 数字化基础 云网融合

天翼云4.0来了!千城万池,无所不至!

天翼云开发者社区

云计算 云平台

DevOps之代码检查

凌云Cloud

DevOps 研发管理 代码检查器

做了1000次Code Review,我学到3点经验_文化 & 方法_Steven Heidel_InfoQ精选文章