1

代码审核的两种方向

 2 years ago
source link: https://zhuanlan.zhihu.com/p/437951945
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

代码审核的两种方向

problem solver
  • 不改变现有的写法(语法和api),让机器去适配现状,用机器代替人
  • 改变写法(语法或者api),让机器提供更强的辅助

用机器代替人

这个方向毫无疑问是 AI 的天下了。基于人工编写规则的搞法会逐步式微的。关注这个方向的可以从了解 SOTA 开始:Papers with Code - Computer Code

比较常见的问题域:

  • 用 AI 对源代码进行抽象执行(虚拟执行)。可以结合运行时流量录制做为真值
  • 运行时流量录制有采样代价的问题,所以 AI 也可以代替人工进行流量的选取(包括RPC协议的分析,有代表性样本的自动采样)
  • 用 AI 对某些 invairant 的运行时行为进行静态推断,解决 concurrency & distributed 下的正确性证明问题
  • 用 AI 进行安全漏洞分析
  • 用 AI 进行常见逻辑错误的分析
  • 对产品经理的产出物(需求文档,mockup,高保真原型)进行语义理解,推断未声明的部分,进行代码生成
  • 对代码进行逆向总结,输出类似产品经理文档这样的东西

因为不改变现有的写法,所以就要面对大量的语言变种,各种RPC协议,各种代码框架的API。所以基于手工规则的搞法是无法支付成本的。必然需要用 AI 来代替手工编码的规则,包括手写的语法 tokenizer 和 parser 这些。

难点是成本投入问题难度接近 AGI,但是收益连无人驾驶的零头都比不上。我有这样的技术能力,用来去做更高收益的 AI 工作不行么?拿来发 paper 是可以的,但是工程上用经济账是算不过来的。

让机器提供更强的辅助

我们要看到,人是便宜好使的。比如

  • 如何保证一套UI界面,拆解成了可复用的UI组件呢?谁来保证该复用组件的时候复用了组件,不该复用组件的地方个性化来写,UI设计师设计错了的时候可以提醒 UI 设计师这里像素多了一个呢?最靠谱的方法就是放一个人在那里审核组件。通过人的强人工智能来做到归并同类项。
  • 每个 RPC 接口是否限定了只有有权限的人才能访问,并且只能访问自己的数据?RPC 接口是否信任了客户端传入的数据,从而暴露了业务上的风险(比如客户端直接把商品价格标为0来下单)。这样的问题除了放个人来把关审核,还有更好的办法吗?

用机器辅助人来实现对代码的收口管理,强制某些代码必须经过某些人来 review 在当下的技术条件仍然是靠谱的。各种类型检查,实质上都是在干这个辅助人工审核的工作。如我在 https://autonomy.design/ 所说的

要保持代码是“好”的状况很难。代码腐化似乎注定的

  • 最初:没有谁是不想好好写的。都有一个宏伟的规划,这次一定
  • 途中:Code Review 如同“堂吉诃德”一般,根本架不住大批量大批量的修改
  • 放弃:躺平了,下次一定

如此循环往复。然而腐化了之后,是无法起死回生的。

  • 食品防腐是 low tech 的事情,但是中毒身亡之后起死回生是天顶星技术
  • 新冠疫苗已经被人类掌握,但是免疫风暴造成的多脏器衰竭仍然是天顶星技术

工程上成本最低的方式不是让程序员随便写,然后用 AI 来擦屁股。而是从一开始就要做好代码审核,限定其写法,不要乱来。

但是这也是政治成本最高的方式。相比工程代价,我们更害怕支付政治成本。所以我们可以看到开发者习惯的强大惯性。

除了政治成本,还有一个令人担忧的趋势是静态类型检查的覆盖范围正在缩小。越来越多的逻辑需要分散到多种不同的语言来编写,每个语言都是只扫门前雪,跨语言的类型检查是没有人来做的。越来越多的业务被拆分成运行时组装的模块。在编译期,我们甚至连装配关系都没有,更不要说做跨模块的静态类型检查了。这种多语言化,动态化的趋势蔚然成风,让开发者趋之若鹜。结合政治成本的原因,大家都不愿意和职业程序员的就业前景对着干,只能顺风使舵,跟着卖微服务的在线课程。

但是 rust 的成功让我们看到了改变开发者习惯的希望。rust 的实质就是用更多的静态类型标识,让开发者付出额外标记的代价,同时获得更可靠的安全性。但是 rust 仍然是太迁就开发者现有的习惯了,比如指针可以传来传去,ownership可以转来转去。实质就是不敢去挑战现有的 code centric 的开发者习惯,不敢强推 data centric 的代码组织方式。

让机器提供更强的辅助,其实质就是

  • 缩小需要 code review 的范围。rust 可以保证 safe 域下的代码你不用去审核安全性,只要重点审核那些标记了 unsafe 的代码
  • 让需要审核的代码更显眼。如果改变接口只是把 private 变为 public,那需要逐行审核才能知道。但是改 package.json 来调整接口和实现,则要显眼得多。也就是把行内的文本改变,变成对特定文件,或者特定目录的修改。

能够做到这两点,前提当然是有个机器人代替人,做了第一道把关的角色。也就是现在 commit 之前的本地 build 过程。这里包括了编程语言的静态类型检查,以及 ci 脚本外挂的 lint 检查。而这些静态类型检查的前提当然是足够的 annotation 标记,而这些标记都需要人来标。减少标记代价是重要的问题

  • 开发者是多语言的,需要每种语言都有一套自己的标记么?如何做到跨语言,跨运行时模块的静态类型检查?
  • 类型推断。特别是对生成的代码做类型推断,基本上还是空白。导致我们必须用 generic 语法,同时表达代码生成和类型生成。或者代码生成搞一套,平行写一套类型生成。
  • 复用已有的开发者习惯。比如文件代表什么,目录代表什么。当我们把两个函数放在同一个文件的时候,本身就代表了一定的语义,这样就可以减少一些额外 annotation 的需要。

最终革命不是请客吃饭。推 rust 就是革 C++ 的命,就是你死我活的关系。能让开发者习惯稍微变变的,只有雄厚的资金加上恰好的历史窗口。

更看好什么?

都不看好。因为收益太小。两个方向都解决不了投入太高,收益太小的问题。

正如大家都喜欢耍聪明时说的废话:做正确的事,远比正确的做事更重要。

ran bing luan


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK