思考,问题和方法

转眼已是七月。距我上次更新公众号,已经一月有余。离我加入 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 时,我们将能够很快地支持。

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

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

原文发布于微信公众号 - 程序人生(programmer_life)

原文发表时间:2018-07-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏阮一峰的网络日志

关于战略问题的通信之六

《Joel谈软件》一书的翻译,我好久没提了。 合同规定今年1月交稿。假定我没有违约的话,此书现在应该上市了。可是,实际上......就算到下个月,我恐怕都翻译不...

2947
来自专栏编程一生

多服务器终端交互利器--polysh和atnodes到高逼格日志中心

1904
来自专栏数据和云

DBA入门之路:学习与进阶之经验谈

初入数据库之门的朋友们,总是关心如何能够快速提高,不断进步,事实上任何一个技术方向,都没有太多的捷径可走,勤奋与坚持必不可少,但是有一些方法和他人的经验可做借鉴...

3036
来自专栏IT大咖说

程序员15条生存法则

1165
来自专栏惶心 - 技术博客

被称为低价屠夫的 Virmach,实际体验如何?

Virmach 是一家比较久以前就听过的 虚拟主机 / VPS / 独服 厂商,因为比较低廉的价格被众多大佬称为低价屠夫。这次在黑色星期五以及网络星期一活动里面...

1.1K7
来自专栏前沿技墅

架构设计考古:Bob大叔的整洁之道

The Clean Coder、Clean Code等名著作者Bob大叔,从1970年起编程至今。他是cleancoders.com和UncleBob Cons...

2082
来自专栏极限编程

一枚程序员眼中的单元测试

如今程序员群体赶上了中国最庞大的农民群体,大街上随便抓一把,十有八九是程序员,还一个刚从某国企离职报名参加软件培训班。我想码农的称号或许就是这么来的吧。

2073
来自专栏编程一生

Redis各种数据结构性能数据对比和性能优化实践

1562
来自专栏华章科技

必收!有问题不求人?4种方法max你的搜索技能

随着大数据时代的到来,信息量较几年前是指数型增长,这个时代呈现知识大爆炸的趋势,不少时间管理、精力管理以及学习能力,还有思维方式的课程和大牛风起云涌,数不胜数。

983
来自专栏编程一生

一款低延迟的分布式数据库同步系统--databus

3706

扫码关注云+社区

领取腾讯云代金券