思考,问题和方法

2018 年 7 月 2 日 程序人生

转眼已是七月。距我上次更新公众号,已经一月有余。离我加入 Arcblock,也有两月。如果把人看做一个运行的软件,那么这两个月我已经迭代好几轮,就像龙珠里在飞往那美克星的太空船里不懈修炼的悟空。

人的成长是有诱因的,但不外乎得到他人指点,和自己开悟两种情况。吕蒙原本一介武夫,经孙权劝学后,发愤图强。有次鲁肃跟他议事,大惊失色,于是有了「士别三日,当刮目相待」的故事 —— 这是他人指点迷津。我是因为换了新的环境,破而后立,往日的积累和思考有了实施的空间和对象,又加上自己在西雅图孑然一身,事情又多,于是就把时间都扑在工作上,想得多,做得更多,能力也就上来了 —— 这是自己开悟。

孔子说三十而立,立的是什么?是立德、立功、立言这三不朽么?还是小家子些的,立身,立家,立业?每个在奔四路上的人都会有自己的体味和解读。但不容置疑的是,三十岁往上,要渐渐形成自己的思想和方法论。上篇文章 Code is Law,我为 Arcblock 的 github repo 定义的一套规范,就是我自己的思想和方法论的产物 —— 你在任何已有的公开的文档中找不到类似的做法。它完美么?不,肯定不,我们在使用一段时间后已经有了一些新的感悟;它独特么?独一无二,且很有价值。

我在 上帝说:要有一门面向未来的语言,于是有了 erlang 引用了 Joe 老爷子在其博士论文中提到的他对 erlang 的 worldview:

  • everything is a process.

  • process are strongly isolated.

  • process creation and destruction is a lightweight operation.

  • message passing is the only way for processes to interact.

  • processes have unique names.

  • if you know the name of a process you can send it a message.

  • processes share no resources.

  • error handling is non-local.

  • processes do what they are supposed to do or fail.

而在 Joe 的眼里,erlang 其实没有什么神秘的,仅仅六个函数就能涵盖它的全部:spawn,send,receive,register,whereis,self。

(感谢小山同学贡献的老爷子亲笔阐述)

仔细想想,它简单地可怕,就像物理学的大一统理论一样,试图从纷繁复杂中跳脱出来,回归本源。更可怕的是,这六个函数不仅仅涵盖了 erlang,似乎也可以解释软件领域里的很多系统 —— 它们无所不在,在系统里面的意义就像原力之于星战。

  • spawn:创建一个资源。对于 erlang,这资源是 process;对某个 service,是 service 本身。

  • send / receive:给资源发指令和接受指令。对于 erlang,这指令是 message,封装成 erlang term,走的是 IPC/RPC;而对某个 http service,指令是 request,封装成 json / msgpack / protobuf,走的是 http / http2。

  • register / whereis:资源怎么注册,怎么发现。对于 erlang,这是 process 在 name register 的注册和发现;对于某个 service,可以是 Consul / DNS。

  • self:返回自己的 identity。在 erlang 里,这是 process 找寻自我的方式;在 micro service 的场景下,每个 service 隐含着有自己的 identity。

我喜爱 Joe,和我喜欢 Rich Hickey 一样,他们在传播知识的同时,传播他们自己对事物独特的理解和思考。

回到我自己对做事的流程和方法的感悟。那些表层的方法之下,其实蕴含着一个重要的思考:如何让团队低成本的沟通和协作。我的方式是:convention by configuration。你弄懂了一个 repo 的结构,知道 make init,make build,make run,make create-pr 干些什么之后,就自然懂得我们在 arcblock 里所有 repo 的运行法则 —— 不管它是 elixir,还是 nodejs,还是 python。也不管 elixir 是否使用 asdf,nodejs 是否使用 nvm,python 是否使用 virtualenv,一个 make init 就把所有的环境帮里构建好,然后就可以安全地 make build 以及 make run 了。一个新来的工程师不需要跑来问我(或者其他人),这个 repo 怎么初始化,怎么运行起来,这大大节省了我们的时间;同时他也不需要为了能运行起来费力去读 READMME.md 里一个长长的「安装须知」的章节,因而也大大节省了他的时间。

这只是冰山的一角。我们还做了很多其它降低团队沟通协作成本的工作。而为大家节省下来的时间和精力,可以被用来做那些更重要的事情。这样点点滴滴的累积,最终有机会转化成一倍或者多倍的生产力,从而形成竞争优势。

我们的 Open Chain Access Protocal(OCAP)选用 GraphQL 而非 REST 接口来做 API 层,也有类似的考量。对于开发者而言,起初,他们有一些学习曲线,适应之后,我们无论是提供 1 个 API 还是 100 个 API,是支持一条链还是若干条链,对使用者的使用成本都是近似的。而 REST API,学习 100 个形态各异的 endpoints 对开发者来说将会是个梦魇;对于我们自己而言,初期搭设地基需要很多时间,随后,花样繁多的 query 背后,都是几种基本的 resolver 的组合。

「如何让团队低成本地沟通和协作」是我过去两个月的重要思考,也是我过去若干年知识和经验的储备的一次厚积薄发。

这两个月我的另一个尚处在摸索中的思考是:「如何用更先进更高效的方式来构建我们的服务及其生态?」

arcblock 目前是个小团队,即便研发团队发展到数十人的规模,依然很小。在我们想要做的事情的范畴上来看,如果找不到一个更行之有效的开发方式,我们会开发得很累,且开发进度会比较缓慢。就拿 OCAP 来说,打造一套供开发者使用的 API,不仅仅是 API 及其背后的服务那么简单。API 要有文档,要有 SDK,要有 API interface 的定义,以及支撑这个 interface 的服务。这里面会有很多重复的劳动:API doc 和 API interface,以及 SDK 都在不断地重复类似的内容和代码。当我们对 API 的定义进行改变的时候,往往牵一发而动全身,数个地方都需要修改,而这些都是非常机械的行为。所以,我们要寻找能够「降维打击」的方式。

在 Tubi,我做的 UAPI 系统,就整合了 API 和 API 的文档,使其可以一次定义,两处生效,节省大家的时间。而对于 OCAP,我们更进一步,试图把问题定义成这样:

  1. 定义一门「语言」,来描述我们的 API

  2. 撰写不同方向上的 Parser(Code generator),将其转换成特定场景的代码

  3. 将 Parser 构建在 build pipeline 中,可以一次 build,生成各种结果

  4. 生成的结果要能很方便地扩展,以及和系统里的其他部分整合

我们定义的语言,姑且称之为:AADL(Arcblock API Description Language),为了方便每一个人撰写和理解(比如,产品经理也可以很方便地定义),我们使用了 yaml 格式,比如 RichestAccounts 这个查询,其定义为:

而这里面使用的 data structure,是这么定义的:

通过这种定义,我们生成:

  • slate 风格的 API 文档(github.com/lord/slate)

  • Absinthe 的 GraphQL 的 query schema 定义(Absinthe 是 elixir 的 GraphQL lib)

  • Absinthe 的 GraphQL 的 type notation 定义

  • Ecto 的 DB repo 定义

  • Ecto 的 DB schema 定义

  • Ecto 的 DB migration 的定义

  • 各种语言的 SDK(比如 nodejs,python,go,etc. 筹划中,还未开始)

然后在一个 build pipeline 里,生成所有代码。比如生成的 Absinthe 的 query 长这个样子:

以上生成的代码符合前文中所述的「生成的结果要能很方便地扩展,以及和系统里的其他部分整合」这个限定条件。在这个例子里,程序员只需要进一步撰写 Resolver.paged_bitcoin_accounts 这个函数就可以了。

目前这套流程还在实验当中,我们线上的服务,OCAP playground 就跑的是生成的代码。我们自己写了大约 3500 行 elixir,1000 行 yaml;生成出来 1500 行 elixir 代码(Elixir 支持 Macro,所以我们生成出来这些源码只是方便自己排查问题)。

虽然还有很多问题,但这套系统最大的好处是,在开发过程中,我们可以随意调整 API 的结构而不必每次调整都苦逼修改很多地方的代码。这在我们对很多 API 的行为还没有一个良好定义的时候,是个莫大的福音。而之后,当我们要大规模增加新的 API 时,我们将能够很快地支持。

这目前是我们对「如何用更先进更高效的方式来构建服务及其生态?」的一个答案。它离完美还有十万八千里,但立等可用。很多时候,问对问题比找对答案更有意义。好的问题就像在黑暗的隧道里寻觅出口,突然手边摸出一把手电筒,瞬间照亮整个征途。

先写这么多吧,希望能引发你的思考和问题。


登录查看更多
0

相关内容

Processing 是一门开源编程语言和与之配套的集成开发环境(IDE)的名称。Processing 在电子艺术和视觉设计社区被用来教授编程基础,并运用于大量的新媒体和互动艺术作品中。
还在修改博士论文?这份《博士论文写作技巧》为你指南
多智能体深度强化学习的若干关键科学问题
专知会员服务
186+阅读 · 2020年5月24日
专知会员服务
53+阅读 · 2020年3月16日
《代码整洁之道》:5大基本要点
专知会员服务
49+阅读 · 2020年3月3日
强化学习最新教程,17页pdf
专知会员服务
174+阅读 · 2019年10月11日
对 ResNet 本质的一些思考
新智元
6+阅读 · 2019年4月12日
【机器学习】深入剖析机器学习中的统计思想
产业智能官
14+阅读 · 2019年1月24日
【TED】以新的角度思考从女人到母亲的转变
英语演讲视频每日一推
9+阅读 · 2019年1月8日
针对计算机视觉一些问题的分析
AI研习社
6+阅读 · 2018年8月20日
人机交互与智能的思考
人工智能学家
9+阅读 · 2018年2月18日
教你简单解决过拟合问题(附公式)
数据派THU
5+阅读 · 2018年2月13日
【强化学习】如何开启强化学习的大门?
产业智能官
13+阅读 · 2017年9月10日
A Modern Introduction to Online Learning
Arxiv
20+阅读 · 2019年12月31日
Arxiv
22+阅读 · 2019年11月24日
Arxiv
8+阅读 · 2019年3月28日
Arxiv
5+阅读 · 2018年5月28日
Arxiv
3+阅读 · 2018年4月18日
VIP会员
相关资讯
对 ResNet 本质的一些思考
新智元
6+阅读 · 2019年4月12日
【机器学习】深入剖析机器学习中的统计思想
产业智能官
14+阅读 · 2019年1月24日
【TED】以新的角度思考从女人到母亲的转变
英语演讲视频每日一推
9+阅读 · 2019年1月8日
针对计算机视觉一些问题的分析
AI研习社
6+阅读 · 2018年8月20日
人机交互与智能的思考
人工智能学家
9+阅读 · 2018年2月18日
教你简单解决过拟合问题(附公式)
数据派THU
5+阅读 · 2018年2月13日
【强化学习】如何开启强化学习的大门?
产业智能官
13+阅读 · 2017年9月10日
Top
微信扫码咨询专知VIP会员