There are only two hard things in Computer Science: cache invalidation and naming things. — Phil Karlton

写这篇文章的起因也是前面刚把自己byetbase.com前端代码里用New/new的地方都改成了Create/create。至于具体修改的原因放在最后说,先聊聊命名这个话题。

从刚学习编程时接触的匈牙利命名法到现在写Golang的短命名法,命名作为编程界两大难题之一,始终是让我头疼的问题。写bytebase.com的过程中,最消耗脑力的是如何建模(需要设计哪几个模型分别干哪几件事情)。而在建模的过程中,给这几个模型分别起什么名字又是其中相当消耗精力的部分:

  • 有时候模型拆对了,但起的名字有问题,基本也算是失败(想想是不是都碰到过分别不清男女厕所标志的经历?)。
  • 有时候模型拆完了,名字总是想不出舒服的,也挺有可能是建模本身有问题。

业界的例子


PostgreSQL: The single worst mistake ever made (链接)

CleanShot 2021-05-11 at 17.01.43.png

Kubernetes: Pluralizing rest resource names in APIs is a mistake (链接) [1]

CleanShot 2021-05-11 at 17.08.16.png

项目管理软件的例子

项目管理软件里最基础的概念是一条需要完成的事项,这个概念无论在中文还是英文里都有许多的同义词,相应的在国内外项管软件里,这个概念也有几种不一样的表达:

  • 国内软件里常见的有,工作项/任务/问题/工单
  • 国外软件里常见的有,Issue/Task/Ticket/(Work) Item

如果要细细琢磨该选哪个的时候,还是一个有点复杂的问题。比如Issue是在国外比较占主流的选择,无论是GitLab, GitHub还是Jira都是用了这个术语。但Issue这个词也有一个明显的问题,就是这个词的翻译是「问题」,而不少事项只是一个todo,还谈不上是一个「问题」。从词义上来说,如果是做一个通用型项目管理软件,Task这个词更中性,适用面也就更广一些。但是Task也有一个不小的问题,就是项目管理软件里通常还会有一个流程引擎,而许多流程引擎里也有Task这个概念,就会产生混淆,所以我们接下来再看流程引擎的例子。

流程引擎的例子

因为bytebase.com也有一套流程引擎,未来也准备和其他CI/CD工具打通,所以我也研究了一下这些工具的模型。主流工具里,流程引擎的模型多为3层,也有4层的,大致是这么个对应关系:

顶层 顶层-1 顶层-2 顶层-3
GitHub Workflow N/A Job Step
GitLab Pipeline Stage Job Step
Octopus Deploy Lifecycle Phase + Task N/A Task
Jenkins Pipeline Stage Task/Step N/A
Tekton Pipeline N/A Task Step
Spinnaker Pipeline Stage Task N/A

我对于这些模型的理解也未必完全准确,但至少可以看出命名是一件很难的事情。这其中Google发起的Tekton项目还希望成为云原生CI/CD的标准,来统一整个模型和命名。但如果按照表里各大平台模型的比较,要达成统一还是挺有难度的。
CleanShot 2021-05-11 at 17.36.54.png

从New到Create

其实想改这个也有一段时间了,最近开始逐步实现CRUD的后端代码,为了保证前后端代码的一致性,终于下手了。

CleanShot 2021-05-11 at 16.03.57 2.png

综合来讲有这么几个原因促成了这次的改动:

  • CRUD中C是代表Create,所以Create相比New更能保持所有代码文档的一致性。
  • new在许多编程语言中是关键字,比如在vuejs模版里,如果组件参数是new的话,为了避免关键词冲突,代码就需要写成$props.new而不能直接写成new。
  • new在英语里既能当动词,又能做形容词,有时也会用成名词,词义并不清晰。时态上也是,用createdObject可以清楚地表达这是创建完毕的Object,但是如果是newObject的话,则存在将要创建和已经创建间的二义性。

这个改动从代码层面看,我觉得是正向的。但也带来一个问题,因为我还是希望在界面上仍然保持用New而不是Create作为文案。因为New无论从字数还是音节上更加紧凑。不少服务商购还会购买xxx.new的顶级域名,作为新建的快捷入口,比如db.new就会跑到DataStax的注册界面。

写在最后

如何命名是一个经典的工程问题。有时候要根据约定俗成的规范,结合业务场景的输入,再夹杂一些个人的偏好,反复琢磨,才能想出一个内心笃定的名字。可能有人会觉得一个命名真的有那么重要么,或许开头的两个例子还不够说服力,那最后再说一个和new/create有关的故事,当年有人问Ken Thompson

What he would do differently if he were redesigning the UNIX system

Ken的回答

I’d spell creat with an e

这里的creat指的是libc中io子系统里创建文件的函数,是所有软件里都绕不开的执行路径。

当然也有人会说,那也只有像libc,PostgreSQL,Kubernetes这种系统的命名才重要吧。确实对于命名的洁癖程度因人而异,但毕竟现在连狗狗币都能上天,随便写个软件说不定也能一飞冲天呢。


[1] 针对Kubernetes的例子,网上看到的绝大多数资料都是推荐采用复数形式给REST资源命名。而我的观点和截图中作者是一致的,应该采用单数。英语单词复数的语法规则太复杂了,尤其对于英语非第一语言的人来说。有些单复数形式一致,也区别不出来到底是单还是多,还有些也不是单纯的加s。如果干脆都往单数形式后面加s后缀的话,又会造成拼写正确性和开发规范上的冲突,容易造成多人开发中的不一致。所以我的习惯是在REST这种不需要区别单复数的场景,统一使用单数形式,如果是代码中需要区分单复数的,复数表示统一在单数形式后面加上List后缀。