时间已做了选择,太多感受,绝非三言两语能形容
这是第 35 个年头,我熟悉地扮演着多个角色,父亲、丈夫、儿子,每一刻都在陪伴和成长中交织。 生活的步伐似乎匆匆,但我努力让自己拥有一颗年轻的心,渴望保持对世界的好奇和激情。
时光大多被生活所占据,只有地铁上和饭桌上我能成为时间主宰。 好在我并未感到疲惫或沉闷,反而逐渐适应了这个身份的变化。 或许,正是在这些琐碎的日常中,我找到了一种生活的节奏,一种平和而温馨的状态。
今年我们走过了北京、汉中、西安、淳安、长沙、张家界、台州。 新年即将到来,准备给孩子办理一下护照,走出去看看。
在陪伴孩子的过程中,参与各种自然知识课程,参观各种展览,我发现在陪伴的同时,我们也在不知不觉中共同成长。 生活中的另一个领域,我从母亲那里薅了两只相机,终于决心好好学习摄像, 我把 Canon 6D 出售,保留了 SONY a6500 这支轻便的 APS-C 相机。 期望摄影成为我表达内心、记录生活的一种方式,每一张照片都是时光的凝固,是岁月的见证。
游戏的世界中,我似乎进入了一段电子阳痿期。购买的游戏几乎只能玩上一个小时就变得索然无味, 也许现实生活才是最引人入胜的游戏吧。
或许,人生就是一场不断变化的冒险。在时间的舞台上,我们扮演着各色角色, 演绎着属于自己的故事。
工作上一直压力和张力巨大,我开始进一步成为探索者。 这两年,我在工作中不断推动项目的上线,部门推出的新产品中的一半是我负责的,我很喜欢这个领域,也确实想把事情做好。
但有时候,我感觉自己有点像是公司招进来的清理工,身处于一个「散多垂」的状态, 面对复杂的环境,解决问题绝非易事。在整理垃圾的过程中还在自动产生垃圾,而清理的工作永远不会终结。 现实往往是,大家注意力持续被新事物(比如 AIGC)吸引走,对现存的问题更容易选择性忽视。
企业的大环境在不断变化,一些老朋友选择离开,大部门也经历了一些变革。 从面向风险的团队 re-org 到面向算力的基础设施团队。我认为这是一个好的信号, AI Infra将继续裹挟着整个 Infra 领域前进,算力管理将成为一个新的命题。
在 Github 数据的细碎图形中,映射出一年的自娱自乐,可惜的是,未给开源社区更多的贡献。
今年,我在开源领域主要的贡献是 alswl/excalidraw-collaboration。 这个 self-host 的 Excalidraw 版本集协作和中文化字体于一身。这个项目以及相关项目吸引了近300个 star,成为我个人最有影响力的开源项目之一,尽管它是一个前端产品。
在暑假期间,趁着家中小神兽不在,我开发了一个关于起名的小程序。虽然这款产品目前有点烂尾, 亏损严重,但我依然希望花更多时间进行开发和改进。一个美好的名字可以给家庭带来无限愉悦, 希望这个项目可以养活服务器资源~
另外,今年我重新活跃在 Twitter 上,分享一些技巧和心得。我的 Follower 从几百人增长到近 4000 人, 虽然离有影响力的推友还有差距,但与许多有趣的朋友交流本身就是一种有趣的事情。
今年一年最受欢迎的内容是:
今年最具价值的文章是介绍「许世伟的架构课」X,赚了几个月 Twitter 的订阅费。
今年我还重新开始听播客,聆听了一大半「内核恐慌」的存档,虽然未能赶上他们活跃的时期。 幸运的是,在外滩大会上我有机会参与了他们的聚会,与 Rio 和吴涛面对面交流。搞笑的是,虽然现场还有一位同事, 但我们却没有互相认出来,令人感叹在庞大的公司中,有时即便共事也未必能够相识(我们一起担任 Go 语言评委)。
除了内核恐慌,我还一直在听「硬地骇客」,一集都没拉,最近还开始听「有知有行」的播客。
在博客输出方面,我分享了两篇关于工程实践心得的文章,希望能够对读者有所帮助。
我最想分享的是 Obsidian Tasks 插件,详细信息可以在我的博客文章中找到, 从 Toodledo 到 Obsidian Tasks - 我的 GTD 最佳实践。我也很高兴成为 Obsidian Tasks 的 Sponser。
回顾一年的时间,我意识到自己在业余时光中的每周时间仅有10小时左右,非常宝贵。 期许着未来能够实现财务自由,以获得更多的自由时间,投入更多的兴趣爱好。
读书仍然大部分都是非虚构类书籍。
士可杀亦可辱;过去带来惆怅,现在带来迷惘,未来带来希望。
讲述做人做事的道理,古人的智慧。常读常新,尤其烦躁时候可以翻出来静一下。
就是为了看 中文的常态与变态。
老男爵举家迁新球,贵公子初入沙漠星。 老皇帝密授哈克南,雷托族全体遭判断。 小保罗掌权弗雷曼,杰西卡诞下遗腹子。 穆阿迪布反攻沙丘,娶伊勒朗再封帝位。
国、企、民、央、地。 悲观。
好希望自己能在 20 岁时候读到这本书。(现在的我已经不需要啦)。教读者如何和自己、周边、世界相处,如何和自己对话以及改变自己。和遇见未知的自己属于同一个路数。
治乱循环在反复。群体的无意识;民主和精英政治是否是解药?评估稳定性一个指标是贫富差距。极权下也孕育变革风险。
管理入门快速操作手册
这本书我给不出星级,超出了我的评价范围。 它可能是一个新学科(因果推断)理论,也可能是统计学中的一个星火闪烁。 作者 Pearl 是统计学大拿,也是人工智能领域权威专家,他确在晚年提出了反对自己过去一系列方法路线。 今天为我们所熟知的大部分机器学习技术,都是基于概率上相关性,从啤酒和尿布,到今天 GPT 大杀四方,AIGC 智能涌现。Peral 认为真正有意义的是提出「为什么」,即解释因果关系。因果关系的论述需要智能能够想象不存在的事物,而这正是当前人工智能无法理解的(Maybe?) 本书成于 2019 年,作者今年已经 87 高龄,不知道他对当前 AIGC 风起云涌是怎么看待的。
作者说的正确但是不全面。
高质量陪伴家人,放下手机,走向户外
执行了周三、周五家庭日给小朋友陪伴;每天早上送小朋友上学;周末一定有一天陪出行。
陪伴小孩这块我做的不如我老婆好,感谢老婆对家庭的贡献。
每月输出文章,特别是 Kubernetes / 研发设计领域可以写一些心得
今年输出 6 篇文章,达标率 50%。其中两篇 实用 Web API 规范 和 架构设计 the Easy Way 我都是很满意的。
经历了新冠,今年计划安排个私教教我健身房运动
没有完成。
投资收益率能做到 10%,今年新手阶段投资以股票型基金为主,投资收益 3.9%,跑赢了大盘和余额宝
今年投资收益率 -1.35%,刚出新手村就被暴击,我还是缺乏对市场和商业的理解。
新的一年 Flag:
每段经历,每次重逢,每本书籍,都是独特的命运线。新的一年已经来临, 期待着与家人、朋友一同继续探寻生活的真谛,去体验伟大与渺小。
往年总结:
]]>博客自 2012 年从 WordPress 迁移到静态站点后,就选择了 Disqus 作为评论系统。 但最近 Disqus 硬广告过于频繁,迫切寻找新的评论系统。
Disqus 官方 明确说明,要去掉广告就付费。
What if I want to remove Ads? If you’d like to remove Disqus Ads from your integration, you may purchase and ads-free subscription from your Subscription and Billing page. More information on Disqus ads-free subscriptions may be found here.
OK,那再见吧 Disqus,我会找到可靠、免费、易用的评论系统。 最后既然是寻找新的评论系统,现在 2023 年了, 我希望这个新系统充分使用云服务的便利,要做到 免费、可靠、易运维。
在进入探索之前,我先梳理一下自己的原则和选型要求:
从功能上面分析需要的能力:
非功能需求:
通过明晰这些原则和要求,可以更有针对性地选择合适的评论系统,确保满足核心功能和非功能需求。接下来,将根据这些原则,继续探讨如何选择和搭建评论系统。
现在我们初步试验一些方案并进行一些探索,以方便我们熟悉一下当前常见系统的特性和水准。
我同时还看了一些外部的一些方案评测报告:
根据初步方案探索,我可以明确部署形态基本如下:
这是一个横向对比表格,列举我一些关心的特性以及候选者在这些特性方面的表现。除了上述提到几款常见软件, 我还额外调研了海外常用的评论 SaaS 服务:
Name | self-host | Official SaaS | SaaS Free | Star | Import Disqus | export data | Comments |
---|---|---|---|---|---|---|---|
Utterances | x | v | v | 7.8k | v | Github account required | |
Cusdis | v | v | v? | 2.3k | v | v? | import from Disqus failed |
Cactus Comments | v | v | 100 | Matrix Protocol, blocked | |||
Commento | x | v | $10/month | v | v | ||
Graph Comment | v | Free to $7 | |||||
Hyvor Talk | x | v | $12/month | ||||
IntenseDebate | v | ? | x | too old | |||
Isso | v | x | 4.8k | v | sqlite storage | ||
Mutt | v | $16/month | |||||
Remark42 | v | x | 4.3k | v | v | full featured, one file storage | |
ReplyBox | v | $5/month | |||||
Staticman | v | 2.3k | v | using github as storage | |||
Talkyard | v | €4.5/ month | |||||
Waline | v | 1.5k | v | v | Multi Storage / Service Provider supported | ||
Twikoo | v | x | 1.1k | v | v | FaaS / MongoDB |
根据横向对比我们可以得出几个结论:
小结
符合我需求的几款产品是:Utterances、Cusdis、Waline。
我最后选择了 utterances 和 Waline 进行 PoC, 其中我的英文博客使用了 utterances, 中文博客使用了 Waline。
为什么不选择 Cusdis 和 Twikoo?因为 Cusdis 使用 PostgreSQL, 而 Twikoo 存储使用腾讯云函数(免费额度有限)或者 MongoDB, 存储上 Waline 选择更多。 另外,作为同类型方案,Waline 是三者贡献者数量最多的,Commit 数量也最多, 社区更有保障。
一个搞笑的点,如果这里使用 h3 标题叫做「Waline」,会直接在这里插入一个当前博客的评论框
部署图:
具体操作,跟随官方文档即可:
AUTHOR_EMAIL
SENDER_NAME
SENDER_EMAIL
实施 PR(仅包含前端,因为后端代码包含了密钥,不便于分享): feat: comments on waline · alswl/blog.alswl.com@e34e348
utterances 的部署则更为简单,一个 PR 就可以启用。 feat: comment using utteranc · alswl/en.blog.alswl.com@29028f6 (github.com)
也没有什么特色,主打简单省事,考虑我英文博客访问量极低,就简单方案。
最后我选择了 Waline / utterances 作为我的评论系统,两者的部署成本都是 0。
妥协牺牲了一些访问速度、安全性,但进一步增强了数据可控性,完成了 self-host。 从稳定性上面来看,尽管这个系统链路变复杂了,单机上也存在可用性风险, 但依托 Vercel / LeanCloud / Brevo 三家 SaaS 服务商,整体风险可控。
毕竟只是一个小小评论系统,0 成本 + 正常工作就行了。
欢迎在下面评测测试一下哦~
]]>image via shipvehicles
使用 GitOps 管理交付内容是一个常见的 DevOps 使用模式。 我们会使用 Git 进行版本管理, 并通过 Git Tag 来跟踪部署软件的版本。 虽然这看上去可以工作,但在云原生技术的推动下,版本的概念远非如此简单。
在引入 GitOps 到 DevOps 流程后,我们可以借助 GitOps 的能力进行持续集成和持续交付。 GitOps 解决了三个核心问题:内容、版本 和 协作。然而,我们经常将注意力集中在内容上,却经常忽略了版本管理问题。
在 GitOps 过程中,有哪些版本管理问题需要解决呢?
一套完整的 GitOps 解决方案包括内容描述(Manifest)、构建方案(Builder)和生效方案(Applier)。其中,内容描述衍生出多种描述语言,从最传统的 Ansible / Chef,到云计算和云原生流行起来的 Terraform、Helm、Kustomize 等。引入了这么多内容描述方式之后,当我们想要明确一个应用的版本时,变得非常复杂。
当提到版本时,我们是指应用源代码的版本?还是指镜像的版本?或者是指某个基础设施即代码(IaC)仓库的版本?进一步地,如果我们要发布一组相互关联的应用,例如前端和后端,或者由多个后端应用组成的系统,如何清晰地描述它们之间的版本依赖关系?
一旦版本描述不准确,就会引入一系列问题,例如错误的上线版本、混乱的应用依赖关系、无法回滚等。
大多数团队对于这个问题的解决方案比较模糊:发布最新的版本,先发布后端再发布前端。然而,在一个复杂的业务团队或需要同时保留多个稳定版本的团队中,这种粗暴的方案是无法接受的。
版本管理不仅解决了版本定位的问题,还可以用于管理应用之间的依赖关系。因此,GitOps 版本管理需要解决以下问题:
在所有的交付产品中,版本管理都是一个重要问题。我们将逐步拆分版本管理这个命题,并从原始问题过渡到 GitOps 的版本管理最佳实践。
在开始正文之前,我将简要介绍 GitOps,以避免对关键概念的理解出现分歧。
GitOps 最核心的技术是基础设施即代码(IaC),即使用声明式描述来取代命令式描述。 通常,IaC 的内容基于某种范式,用于描述特定目标的期望状态。这个范式可以是 Terraform、Kubernetes YAML、Pulumi,甚至是 Ansible。而特定目标可以是云服务、Kubernetes,甚至是物理机。 直观的说,通过使用 YAML 取代过去的 Bash 命令,我们可以大大提高变更的准确性和可控性。
对于 GitOps 来说,是否使用 Git 并不是最重要的,我们也可以使用 SVN 来实现 GitOps。只是 Git 具有更广泛的适用范围,并可以充分发挥 Git 仓库在团队协作和持续集成/持续部署中的能力。
引入 Git 仓库后,我们还同时拥有了基于 Git Revision / Tag / Branch 的版本管理能力,这体现在业务上就是版本记录、多版本并行管理等方面。
简单地基于 Git Revision 进行描述还不足以满足我们的实际需求。
在探索版本的源头时,我们会发现最原始的版本是代码的版本。
代码的版本是什么?是代码仓库的版本还是代码编译出来应用的版本。 这个版本并不是代码所在的版本管理系统(如 Git / Mercurial / SVN 等)的版本。尽管这两者经常相关,但事实上,一份代码本身只是一组代码文件,只要构建成功,就会有一个版本。如果没有定义,版本就是未知的,此时与仓库管理没有关联。
注意:下文我们不再区分 Git / Mercurial / SVN 多种版本管理方案,统一使用 Git 进行描述
还需要注意的是,中文中有两个概念(库 Libray 和仓库 Repository)。 无论是哪种定义,都没有表示一个库一定是一个版本化(Git / SVN)仓库, 这意味着我们并没有假设代码库一定是被版本化管理的。当我们将代码文件打包成一个 zip 文件时(GitHub 的 zip 下载就是这种形式),即使这个 zip 文件失去了所有的 Git 历史,它仍然是一个代码库。
代码的版本实质上是应用的版本,这是作者的意图表达。这个版本往往是 vx.y.z
这种形式,而不是 Git commit hash,
最常见的管理方案是基于语义化版本。
我推荐的版本存储方式是使用一个 VERSION
文件将版本存储在代码目录中。例如,Git 的 Version 文件可以清楚地看到当前 Git 的版本是:
GVF=GIT-VERSION-FILE
DEF_VER=v2.42.GIT
其中的 .GIT
也明确说明了这个代码是一个开发模式下的版本。如果我们切换到一个发布版本的代码,例如 v2.39.3 版本,我们可以看到 DEF_VER=v2.39.3
,这是一个遵循标准的制品(Artifacts)格式。这里还有两个最佳实践:
dev
模式,只有在进行标记封版之后才会成为正式版本号。源代码的最终产物不仅包括二进制文件、可执行文件和动态库(.dll
/ .so
/ .dylib
),还包括相应的启动配置文件。这些启动配置文件通常与对应的版本一起进行管理。例如,Nginx 的启动文件 nginx.conf
和 Redis 的启动文件 redis.conf
,这些启动配置文件也应该纳入版本管理。
从源代码仓库构建出来的内容就是制品(Artifacts)。制品已经具有两个版本:
VERSION
文件中定义的版本。引入制品版本管理后,问题变得更加复杂,因为制品带来了更多的问题:
制品的概念非常重要,其中最核心的一个理念是:制品可以通过打包器形成新的制品。
由于制品具有版本,而新的制品将形成新的版本,我们将进入多层嵌套。为了避免最原始的版本信息丢失,我们将 Version 的概念扩展为 Upstream Version,这是软件作者人为指定的版本,是所有版本的源头。
为什么制品可以形成新的制品呢?我举一个 Kubernetes 容器环境下的例子。 容器是一种交付形式,它将可执行文件和启动配置文件写入镜像文件中,并可以在容器环境中运行。形成的镜像文件存在于镜像仓库中,本身也是一种制品。
另外,Helm / Kustomize 也是一种交付形式(打包工具链)。 每个构建层解决其特定问题,并且可以在特定环境(例如容器、Kubernetes、云基础设施)中运行。
每个制品都需要构建,过程中会有自己的额外描述信息(Packaging Info),这些额外的描述信息本身也会发生变化,因此会增加一个版本。在实践中,我们希望制品的版本与其上游版本绑定。每种打包机制可能会包含自己的一些定义配置,但仍然遵循上游的版本。例如,Kubernetes 的 Workload 包含一个镜像,Workload 的描述是附加信息,而镜像仍然受到上游控制。
Artifact + Packaging Info = New Artifact,制品经过打包可以形成新的制品。直到最后的 Installer 放置到相应的环境中生效。
如果这些制品可以通过文件(IaC)进行描述,就形成了各种 IaC 仓库,这些仓库成为了 GitOps 的核心对象。
让我们来理清一下这些略有晦涩的概念:
中文 | 英文 | 解释 |
---|---|---|
源代码 | Source Code | 程序、应用的源文件集合 |
代码仓库 | Source Code Repo | 源代码放到版本管理系统中的管理单元 |
版本 | Version | 源代码对应的应用版本,人为定义,语义化,有些场景会说 Upstream Version |
可执行文件 | Executable File | 源代码构建出来的结果,一般是 ELF 可执行文件,也可以是 Lib 文件 |
启动配置文件 | Configuration File | 配套 ELF / Lib 的启动配置文件,区别于广泛意义上的配置文件(比如 Kubernetes YAML) |
制品 | Artifact | 包含可执行文件和启动配置文件的集合,可以运行在运行时下面,一般是文件形态。制品可以嵌套制品。 |
安装器 | Installer | 将制品安装到运行时的工具 |
运行时 | Runtime | 制品的运行环境,比如特定操作系统,Kubernetes,Docker Engine。 |
打包器 | Packer | 将制品打包成特定格式(新的制品)的工具 |
打包附属信息 | Packaging Info | 制品打包时候需要的额外信息,比如容器的操作系统,进程的运行容量,默认环境变量等 |
这些概念共同构成了制品版本管理的核心要素,帮助我们管理和跟踪制品的不同版本,以及它们之间的关联和依赖关系。
打包器是一种工具,通过打包操作(Packaging)将制品组织成特定的格式,形成全新的制品。 打包的过程涉及编译、链接、合并和存档等常见概念。
它通常以上游(Upstream)作为输入,上游可以是源码,也可以是其他系统生成的制品(Artifacts)。
例如,在打包 Docker Compose 时,输入是镜像(Image),而对于 Helm,输入则包括镜像、启动配置文件和 Helm 模板,而输出则是 YAML 文件。
制品是一种数据集合,可以在特定环境中运行。 它由可执行文件和启动配置文件等组成,通常以文件形式存在,并且可以在运行时环境下运行。制品具有嵌套的能力,可以包含其他制品。
最常见的形态是二进制文件(ELF),也可以是适用于特定环境的运行物,如容器镜像。
制品通常以文件形式进行传输。
安装器是一种工具,用于将制品安装到运行时环境中。 它负责将制品部署到目标环境并确保其正常运行。 例如,dpkg、Pacman 是常见的安装器工具,而在 Windows 平台上,我们常见自引导的安装器。
对于特定的环境如 Kubernetes,我们可以使用 kubectl 命令进行安装,而 Helm 则使用helm
命令来进行安装。
当我们理解了这些概念后,我们或许会惊讶地发现,这些概念与 Linux 社区多年来的实践是如此相似。抛开云原生等新概念,Linux 社区早就拥有了完整的解决方案。
每一层制品都会引入新的配置(Config)/ 扩展(Extension)/ 值(Values)/ 环境变量(Env)等等,无论如何称呼, 我们统一称之为配置。 这些新加入的 Packaging Info 的描述在大规模集群管理下也带来了新的问题。
自豪地使用 ArchLinux。
Arch Linux 使用 Pacman 作为包安装器,并且拥有一套完整的构建方案。
在 Arch Linux 中,PKGBUILD
link用于描述包的构建方式,它本身是 Bash 的子集,是描述包的核心文件。
版本管理方面,Arch Linux 提供了清晰明确的方案,并且设计了完整的制品嵌套解决方案。
在 PKGBUILD
中,pkgver
表示上游版本,并经过适当的修正,使用 _
替代 -
,并调整了时间戳的格式。而 pkgrel
则表示发布号,而不是构建号,每次发布都会增加该号码,用于管理 Arch Linux 的发布动作。当大部分 PKGBUILD
发生变化时,发布号都会发生变化。
此外,epoch
是一个强制构建版本的机制,默认为 0 并且隐藏起来。使用 epoch
是一种兜底的解决方案,通过破坏版本对比来强制进行新版本的升级。
另外,在 PKGBUILD
中,使用了版本依赖的方式来优雅地解决模块的问题。
例如,base-devel
包是对 26 个基础软件的依赖,而该包本身并没有具体的内容。这种方案非常优雅,避免了引入一个新的模型(比如叫做 Group / 产品)。
最后让我们回归到 GitOps 版本管理本身,让我们重新面对文中的几个问题,通过以上的分析和调研,是否已经解决了这些问题呢?
VERSION
文件来确定软件版本,也就是上游版本(Upstream Version)v1.2.3-afe12c
的形式来追踪 Git 仓库中的版本,使用 v1.2.3-afe12c-b1
来追踪镜像构建物的版本。版本管理的智慧,其实已经体现在当年的 RPM / DEB / PKGBUILD 中。 我们通过明确版本定义权交给应用作者,提出制品嵌套的概念,允许版本的概念进行多层嵌套。
我们希望,最后运行的制品版本仍然是原始应用版本(Upstream Version)的衍生。毕竟, 让每个运行的程序都知道自己来自何处、自己是谁,在大规模集群管理下已经变得相当重要。
]]>image via Pixabay
前几日,我在团队内部举行了一场技术分享,我介绍了关于架构设计的最佳实践。将这些实践凝练成了 20 字口诀:
我将顺口溜转到了 Twitter,不少朋友对这些顺口溜产生了浓厚兴趣,希望深入了解。因此,我将我分享中的观点扩展成了这篇文章。
让我们首先澄清 什么是架构设计和系统分析(简称系分)。有些朋友对前者很熟悉,对后者却不太了解。 不过没关系,以下是维基百科上的介绍:
架构,软件架构是有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。
系统分析,旨在研究特定系统结构中各部分(各子系统)的相互作用,系统的对外接口与界面,以及该系统整体的行为、 功能和局限,从而为系统未来的变迁与有关决策提供参考和依据。
来看一下英文定义可能会更清晰:
我们有时候提到的设计文档,可能涵盖整个设计过程,包括架构设计、系统分析以及其他设计活动(交流、PoC)。
软件架构(设计)= Software Architecture
系统分析 = System Analysis
最后,我来解释一下我对这两者边界的理解。实际上,我认为架构设计和系统分析并没有明显的界限。 一个系统或模块不管如何都会进行系统分析,而当出现以下几个特征时,就开始考虑架构设计问题:
在这里,我们讨论的是技术架构,不会涉及业务架构或产品架构等方面。 技术方面的讨论重点是如何更高效地利用技术能力和方法来解决特定类型的问题。
进一步地,技术架构可以分为两种:一种是从顶层向下看,包括业务、战略和框架划分; 另一种是关注工程实现(编码)层面需要解决的架构问题。
那些经验丰富的人常常有较宏观的视角,使用的常见名词有:全局、宏观、领域、战略、平衡、规划。我将这些词汇整理成了一个词云如下:
generted by https://tendcode.com/tool/word-cloud/
以上这些概念在架构设计和系统分析中都非常重要,因为它们帮助我们在整体上考虑问题,甚至超越技术层面, 从业务价值、商业策略和业务战略的角度思考问题。
另一种架构偏重于工程设计和实现。常见的关键词有:领域建模、UML、GoF23,SOLID,高内聚低耦合等等。对应的词云如下:
generted by https://tendcode.com/tool/word-cloud/
架构的话题非常广泛,本文选择从一个切入点出发:通过实践和方法论,使架构意识在日常工作中发挥作用,以满足 80%的工程设计开发场景。 我称之为「架构设计 the easy way」。
理解架构的第一步,也是最重要的一步,就是关注「问题」。也就是说,你遇到了什么问题,你将如何去解决它?
通常情况下,如果我们的业务和系统都稳定运行,没有遇到任何问题,我们就不太需要进行架构设计。但是,只要涉及到架构设计, 必定是因为我们遇到了问题。这些问题可能源自新的需求,也可能是外部环境的变化, 亦或是系统自身随着时间的发展而出现的。无论问题的来源如何,我们都遇到了问题。
遇到问题之后,我们该如何解决?就像将大象装进冰箱一样,需要分成几个步骤。
image via unkown
因此,解决问题也有三个步骤:第一步是将问题描述清楚,第二步是进行协商和决策达成一致,第三步则是着手解决问题。
我还想问一个听上去很愚蠢的问题:为什么不能直接解决问题?
因为问题是复杂的,有许多解决路径,不同的解决方案各有优劣和成本。在架构设计中,我们需要完成这些决策。
那为什么不直接进行决策,甚至直接开始动手?
首先可能涉及到职权问题,架构师未必有最终决策权,需要有决策权的人来做最后的决定。 第二个原因是架构师未必是方方面面的专家,设计一个复杂系统时候需要协调多个部分和领域专家来一起评估决策。
我举 Prometheus 的架构设计来作为例子。
image via Prometheus
这个架构图回答了很多问题,我举几个例子:
问题驱动架构变化,架构方案应对问题,架构评审统一解决方案。
关于决策拍板问题。我强烈推崇架构师根据自己具备的领域知识、对行业的判断以及对现状的了解, 做出自己的思考和独立判断。这些思考过程应该有因果关系的支持,一个优秀的架构师必定拥有自己的观点。
最后,我补充一个小问题:为什么这里没有提到架构分层、模块分层?
不是因为分层和框架不重要,而是在因为大家都很专业。分层和模块化已经是基本常识和技能,因此反而往往不会成为争论和决策的焦点。 如果分层和框架无法快速形成一致,有可能团队构成上存在问题,也可能问题过于复杂已经不是 80% case。
在本阶段,产出的成果包括架构图以及对问题、价值、成本、风险和分工达成一致的认识。
需求是对问题的解答。我个人喜欢用思维导图或白纸来画图,将需求讲清楚。 画什么内容呢?理清角色,并列出各种动作和行为。
那有什么技巧可以将事项都整理出来呢?我经常使用主谓宾状从的方法。 也就是说,明确哪些人,在什么场景(可选),以什么状态(可选)做着什么事情。
image via unkown
通过用例将需求清晰地拆解,并在这个过程中不断与需求提供方进行交流和沟通。
Demo 稿是产品经理的武器,而需求用例则是工程师的武器。
有些初入职场的研发人员会不自然地变成需求的执行者。我比较果断地判断,不了解业务的工程师和外包没什么区别。而需求分析环节是最重要的, 是对业务输入进行理解、梳理、重新设计的机会。通过用例的整理,我们可以将一些不切实际、不可靠的需求反馈给需求方。
这是少数可以推动(反馈)需求方的阶段,一定要珍惜。
这里有一个产品用例的范例:
image via 网易云音乐产品分析报告
实际上,这个用例是敌对势力那边总结的 😄,但仍然能够体现用例的重要性。
除了使用主谓宾的方式来进行设计,还有一些其他技巧:
本阶段的产出物包括:Demo 稿、用例图。
在我看来,设计的核心在于模型:模型确定了数据的载体和边界。而数据确定了组成部分,边界则确定了归属和职责。 在 UML 中,大量的 Entity 和 Object 用于确定模型的边界。 随着业务系统复杂程度的增加,建模也会面临更加复杂的挑战。
我总结了一下我建模的几个要点:
很多人对中英文术语表不屑一顾,但我却很在意这点。有一个效应叫做「外语陌生感」(Foreign Language Effect), 就像博物学使用拉丁语 / 希腊语来描述物种一样。我们非英语母语的工程师,使用英文描述术语可以快速地聚焦问题。
始终牢记 80/20 原则的存在,特别是在设计阶段,一定要关注核心对象,将其放大而非过度关注细节。 一般来说,关注最核心的 20%模型就可以满足大部分场景。
在模型的提炼和抽象过程中要反复斟酌,并且可以将这个过程联动到前期的用例定义和后期的时序设计, 这需要大量领域知识的支持。我个人喜欢在这个阶段参考外部的代码和设计。
模型之间的关联关系主要是 1:1 / 1:N / M:N 关系,需要使用箭头清楚地标记主从关系。主从关系意味着从属关系, 这会影响后续一系列细节设计(如 URL、数据库、生命周期管理等)。 我个人推荐避免使用 M:N 关系,这种形式通常表明中间会有一个凭证(Credential)或关系(Relationship / Binding)。
除了关注静态的数据,还要关注模型的行为(极少量模型才有)。这个阶段可以进一步做一些识别,方便下一步的细节设计。
完成业务模型设计之后,同时要考虑数据模型。对于普通业务系统,这个转换会非常直观简单。业务系统通常是无状态系统, 完全依赖数据库进行存储。如果面临 DIA(Data Intensive Application)系统,就要考虑运行时数据的管理, 以及一系列复杂的生命周期管理和可用性管理(我估计有这个需求的朋友,不会看到这里了)。
我举例一个 Kubernetes 的 RBAC(Role-Based Access Control)系统,这是常见的 AuthZ 授权鉴权系统(注意,不是 AuthN 认证系统)。
image via Kubernetes RBAC - DEV Community
这里我抛几个问题:
这些答案都需要建模来回答的。
在我们的讨论中,更多关注的是业务模型,即用户能感知并产品能理解的模型,通常需要存储在数据库中。
但在基础设施领域,也是有模型的,有时候称之为"概念"(Concept)。基础设施领域的模型通常会简单得多, 而业务模型可能会非常复杂,因为世界本身就很复杂,而基础设施则专注于解决非常垂直领域的问题,因此相对简单。
此外,基础设施领域的特殊性会导致有很多抽象的建模,例如最简单到我们常常忽略的(Manager / Service)类别。 一些带有数据和状态的模型,比如 Executor,是常见的概念,而 Registry / Queue 也是常见的概念。
这是 Kubernetes的 Concepts,十几个子类,上百个概念更显这个系统的复杂性。
模型不仅仅是数据,还涉及边界,边界决定了其归属和职责。
模型的设计需要动静结合来看,静态方面关注其持有的内容,动态方面则关注其提供的功能。
在基础设施领域,模型的产出可能包括 UML Model 图、ER 图、数据库 DML、类文件、OpenAPI Swagger(部分)等。
程序设计 = 数据结构 + 算法 + 流程控制
在将设计转换为模型之前,最后一个重要的步骤是控制细节。对于需求方和决策者来说,这一步可能并不重要, 但对于实施方(开发团队)来说,这个步骤直接影响交付结果的质量和时间。
我认为细节应该在时序图上进行呈现。
通常我们有两种常用的图形来展示细节:流程图和时序图。两者实际上有很多相似之处, 但我个人更喜欢时序图,因为它不仅包含顺序的概念,还清晰地展示了流程和系统之间的交互边界。
我的技巧是,一般每个用例都会对应一个时序图。
这里以 AWS 一个官方博客作为范例:
在上图中,展示了 AWS 中使用 CloudFront 的一个时序图,从时序图中可以清晰地看到多个系统之间请求的流转以及多种异常状态的处理。
这里我总结一下时序图的小技巧:
一般来说,时序图画好了,就可以放心地交给项目团队开始实施,不会有大的错误。如果没有时序图,依赖的就完全是彼此之间的合作经验和信任度了。
产出:时序图、API 文档(Open API Swagger)、前端 service 生成(如果有)。
在这个阶段,尽管我们还没有开始编写代码,但已经清楚了需要做什么,以及实现的样子。 我们也有了类结构、API 定义、前端服务生成等产出。多个团队可以同时开始协作,没有明显的瓶颈。
如果未来需要汇报,汇报材料已经有了 1/3 的内容。如果需要撰写技术分享文档,也已经具备了 1/2 的内容。
如果这个项目是一个简单的 CRUD 应用系统,那么基本不会有什么难点。
如果是一个 DIA 系统(Data Intensive Application),则需要开始设计和实施数据存储部分,并考虑数据一致性和并发相关的问题。对于一个复杂的系统, 还需要继续实施多个系统连接处是否存在不确定性。如果在工程上面临同步方面的挑战,例如应用框架改造、通讯系统改造等, 也要提前进行风险排除。(我认为同时进行技术升级和业务开发并不明智)。
我有一套自己的画图工具套件,涵盖了系统架构图、流程图等绘制。 PS:我甚至还给自己的产品设计 Logo,或许这与我内心渴望成为一名设计师有关吧~
作为一名工程师,必须积累自己的画图 UI Kit,熟练掌握其技巧,构建一套属于自己的工具包, 从而能够将脑海中的构思快速还原到文档中。
我的画图工具组合相当丰富。用于绘制架构图的工具包括:
用来做工程设计(UML)的工具如下:
这里我再软广一下我维护的 Excalidraw(Fork),支持中文手写字体,保持风格的统一。
回到本次分享的出发点,给大家一份简单可行的架构设计方案。 但是对于你这样好学的人来说,肯定不会满足于如此简单的流程, 毕竟还有那 20% 的复杂场景无法完全涵盖。 我给你一个关键词列表和一些建议的书单,帮助你进一步加深学习:
以下是一些书单,可以帮助你深入学习:
]]>
image by stable difussion, prompt by alswl
这个问题困扰了我很长时间,始于我求学时期,每一次都需要与团队成员进行交流和讨论。
从最初的自由风格到后来的 REST,我经常向项目组引用 Github v3 和
Foursqure API(已经无法访问,暴露年龄) 文档。
然而,在实践过程中,仍然会有一些与实际工作或公司通用规范不匹配的情况,
这时候我需要做一些补充工作。最终,我会撰写一个简要的 DEVELOPMENT.md
文档,以描述设计方案。
但我对该文档一直有更多的想法,它还不够完善。因此,我想整理出一份简单(Simple)而实用(Pragmatic)的 Web API 最佳实践,也就是本文。
这个问题似乎很明显,但是深入剖析涉及团队协作效率和工程设计哲学。
API(Application Programming Interface,应用程序编程接口)是不同软件系统之间交互的桥梁。在不同软件系统之间进行通信时, API 可以通过标准化的方式进行数据传输和处理,从而实现各种应用程序的集成。
当我们开始撰写 API 文档时,就会出现一个范式(Design Pattern),这是显式还是隐式的, 是每个人一套还是公用同一套。这就像我们使用统一的 USB 接口一样,统一降低了成本,避免了可能存在的错误。具体来说,这有以下几个原因:
image by alswl
虽然使用统一规范确实有一些成本,需要框架性的了解和推广,但我相信在大部分场景下, 统一规范所带来的收益远远高于这些成本。
然而,并非所有的情况下都需要考虑 API 规范。对于一些短生命周期的项目、影响面非常小的内部项目和产品, 可能并不需要过多关注规范。 此外,在一些特殊的业务场景下, 协议底层可能会发生变化,这时候既有的规范可能不再适用。但即使如此,我仍然建议重新起草新的规范,而不是放弃规范不顾。
在制定 API 规范时,我们应该遵循一些基本原则,以应对技术上的分歧,我总结了三个获得广泛认可的原则:
image by alswl
在 Web API 领域,RESTful API 已经成为广受欢迎的协议。 其广泛适用性和受众范围之广源于其与 HTTP 协议的绑定,这使得 RESTful API 能够轻松地与现有的 Web 技术进行交互。如果您对 REST 不熟悉, 可以查看 阮一峰的 RESTful API 设计指南 以及 RESTful API 设计最佳实践。
REST 是一种成熟度较高的协议,Leonard Richardson 将其描述为四种成熟度级别:
image by alswl
rel
链接进行 API 资源整合,JSON:API 是登峰造极的表现REST 的核心优势在于:
然而,REST 并非一种具体的协议或规范,而是一种风格理念。尽管 REST 定义了一些规则和原则,如资源的标识、统一接口、无状态通信等, 但它并没有规定一种具体的实现方式。因此,在实际开发中,不同的团队可能会有不同的理解和实践, 从而导致 API 的不一致性和可维护性降低。
此外,REST 也有一些局限性和缺陷:
/login
)操作,转换成 session
就非常绕口;
同样的问题在转账这种业务也会出现。HTTP 有限的动词无法支撑所有业务场景。因此,虽然 REST 风格是一个不错的指导思想,但在具体实现时需要结合具体业务需求和技术特点,有所取舍,才能实现良好的 API 设计。 最后,我们是否需要 Web API 设计规范,遵循 REST 风格呢?我认为 REST 能够解决 90% 的问题,但还有 10% 需要明确规定细节。
因为我们的协议基于 HTTP 和 REST 设计,我们将以 HTTP 请求的四个核心部分为基础展 开讨论,这些部分分别是:URL、Header、Request 和 Response。
我的 URL 设计启蒙来自于 Ruby on Rails。 在此之前,我总是本能地将模型信息放到 URL 之上,但实际上良好的 URL 设计应该是针对系统信息结构的规划。 因此,URL 设计不仅仅要考虑 API,还要考虑面向用户的 Web URL。
为了达到良好的 URL 设计,我总结了以下几个规则:
通常情况下,URL 的模型如下所示:
/$(prefix)/$(module)/$(model)/$(sub-model)/$(verb)?$(query)#${fragment}
其中,Prefix 可能是 API 的版本,也可能是特殊限定,如有些公司会靠此进行接入层分流; Module 是业务模块,也可以省略;Model 是模型;SubModel 是子模型,可以省略; Verb 是动词,也可以省略;Query 是请求参数;Fragment 是 HTTP 原语 Fragment。
需要注意的是,并非所有的组成部分都是必须出现的。例如,SubModel 和 Verb 等字段可 以在不同的 URL 风格中被允许隐藏。
设计风格选择
注:请注意,方案 A / B / C 之间没有关联,每行上下也没有关联
问题 | 解释(见下方单列分析) | 方案 A | 方案 B | 方案 C |
---|---|---|---|---|
API Path 里面 Prefix | /apis |
/api |
二级域名 | |
Path 里面是否包含 API 版本 | 版本在 URL 的优势 | ✅ | 🚫 | |
Path 是否包含 Group | ✅ | 🚫 | ||
Path 是否包含动作 | HTTP Verb 不够用的情况 | ✅ | 🚫 (纯 REST) | 看情况(如果 HTTP Verb CRUD 无法满足就包含) |
模型 ID 形式 | Readable Stable Identity 解释 | 自增 ID | GUID | Readable Stable ID |
URL 中模型单数还是复数 | 单数 | 复数 | 列表复数,单向单数 | |
资源是一级(平铺)还是多级(嵌套) | 一级和多级的解释 | 一级(平铺) | 多级(嵌套) | |
搜索如何实现,独立接口(/models/search )还是基于列表/models/ 接口 |
独立 | 合并 | ||
是否有 Alias URL | Alias URL 解释 | ✅ | 🚫 | |
URL 中模型是否允许缩写(或精简) | 模型缩写解释 | ✅ | 🚫 | |
URL 中模型多个词语拼接的连字符 | - |
_ |
Camel | |
是否要区分 Web API 以及 Open API(面向非浏览器) | ✅ | 🚫 |
版本在 URL 的优势
我们在设计 URL 时遵循一致性的原则,无论是哪种身份或状态,都会使用相同的 URL 来访问同一个资源。 这也是 Uniform Resource Location 的基本原则。虽然我们可以接受不同的内容格式(例如 JSON / YAML / HTML / PDF / etc), 但是我们希望资源的位置是唯一的。
然而,问题是,对于同一资源在不同版本之间的呈现,是否应该在 URL 中体现呢?这取决于设计者是否认为版本化属于位置信息的范畴。
根据 RFC 的设计,除了 URL 还有 URN(Uniform Resource Name), 后者是用来标识资源的,而 URL 则指向资源地址。实际上,URN 没有得到广泛的使用,以至于 URI 几乎等同于 URL。
HTTP Verb 不够用的情况
在 REST 设计中,我们需要使用 HTTP 的 GET / POST / PUT / DELETE / PATCH / HEAD 等动词对资源进行操作。
比如使用 API GET /apis/books
查看书籍列别,这个自然且合理。
但是,当需要执行类似「借一本书」这样的动作时,
我们没有合适的动词(BORROW)来表示。针对这种情况,有两种可行的选择:
POST /apis/books/borrow
,表示借书这一动作;POST /apis/books/borrow-log/
;这个问题在复杂的场景中会经常出现,例如用户登录(POST /api/auth/login
vs POST /api/session
)和帐户转账(vs 转账记录创建)等等。
API 抽象还是具体,始终离不开业务的解释。我们不能简单地将所有业务都笼统概括到 CRUD 上面,
而是需要合理划分业务,以便更清晰地实现和让用户理解。
在进行设计时,我们可以考虑是否需要为每个 API 创建一个对应的按钮来方便用户的操作。
如果系统中只有一个名为 /api/do
的 API 并将所有业务都绑定在其中,虽然技术上可行,
但这种设计不符合业务需求,每一层的抽象都是为了标准化解决特定问题的解法,TCP L7 设计就是这种理念的体现。
Readable Stable Identity 解释
在标记一个资源时,我们通常有几种选择:
我个人有一个设计小技巧:使用 ${type}/${type-id}
形式的 slug 来描述标识符。Slug 是一种人类可读的唯一标识符,
例如 hostname/abc.sqa
或 ip/172.133.2.1
。
这种设计方式可以在可读性和唯一性之间实现很好的平衡。
A slug is a human-readable, unique identifier, used to identify a resource instead of a less human-readable identifier like an id .
from What’s a slug. and why would I use one? | by Dave Sag
PS:文章最末我还会介绍一套 Apple Music 方案,这个方案兼顾了 ID / Readable / Stable 的特性。
一级和多级的解释
URL 的层级设计可以根据建模来进行,也可以采用直接单层结构的设计。具体问题的解决方式,
例如在设计用户拥有的书籍时,可以选择多级结构的 /api/users/foo/books
或一级结构的 /api/books?owner=foo
。
技术上这两种方案都可以,前者尊重模型的归属关系,后者则是注重 URL 结构的简单。
多级结构更直观,但也需要解决可能存在的多种组织方式的问题,例如图书馆中书籍按照作者或类别进行组织?
这种情况下,可以考虑在多级结构中明确模型的归属关系,
例如 /api/author/foo/books
(基于作者)或 /api/category/computer/books
(基于类别)。
Alias URL 解释
对于一些频繁使用的 URL,虽然可以按照 URL 规则进行设计,但我们仍然可以设计出一个更为简洁的 URL, 以方便用户的展示和使用。这种设计在 Web URL 中尤其常见。比如一个图书馆最热门书籍的 API:
# 原始 URL
https://test.com/apis/v3/books?sort=hot&limit=10
# Alias URL
https://test.com/apis/v3/books/hot
模型缩写解释
通常,在对资源进行建模时,会使用较长的名称来命名,例如书籍索引可能被命名为 BookIndex
,而不是 Index
。
在 URL 中呈现时,由于 /book/book-index
的 URL 前缀包含了 Book,我们可以减少一层描述,
使 URL 更为简洁,例如使用 /book/index
。这种技巧在 Web URL 设计中非常常见。
此外,还有一种模型缩写的策略,即提供一套完整的别名注册方案。别名是全局唯一的,
例如在 Kubernetes 中, Deployment
是一种常见的命名,而 apps/v1/Deployment
是通过添加 Group 限定来表示完整的名称,
同时还有一个简写为 deploy
。这个机制依赖于 Kubernetes 的 API Schema 系统进行注册和工作。
我们常常会忽略 Header 的重要性。实际上,HTTP 动词的选择、HTTP 状态码以及各种身 份验证逻辑(例如 Cookie / Basic Auth / Berear Token)都依赖于 Header 的设计。
设计风格选择
问题 | 解释(见下方单列分析) | 方案 A | 方案 B | 方案 C |
---|---|---|---|---|
是否所有 Verb 都使用 POST | 关于全盘 POST | ✅ | 🚫 | |
修改(Modify)动作是 POST 还是 PATCH? | POST | PATCH | ||
HTTP Status 返回值 | 2XX 家族 | 充分利用 HTTP Status | 只用核心状态(200 404 302 等) | 只用 200 |
是否使用考虑限流系统 | ✅ 429 | 🚫 | ||
是否使用缓存系统 | ✅ ETag / Last Modify | 🚫 | ||
是否校验 UserAgent | ✅ | 🚫 | ||
是否校验 Referrral | ✅ | 🚫 |
关于全盘 POST
有些新手(或者自认为有经验的人)可能得出一个错误的结论,即除了 GET 请求以外, 所有的 HTTP 请求都应该使用 POST 方法。甚至有些人要求 所有行为(即使是只读的请求)也应该使用 POST 方法。 这种观点通常会以“简单一致”、“避免缓存”或者“运营商的要求”为由来支持。
然而,我们必须明白 HTTP 方法的设计初衷:它是用来描述资源操作类型的,从而派生出了包括缓存、安全、幂等性等一系列问题。 在相对简单的场景下,省略掉这一层抽象的确不会带来太大的问题,但一旦进入到复杂的领域中, 使用 HTTP 方法这一层抽象就显得非常重要了。这是否遵循标准将决定你是否能够获得标准化带来的好处, 类比一下就像一个新的手机厂商可以选择不使用 USB TypeC 接口。 技术上来说是可行的,但同时也失去了很多标准化支持和大家心智上的约定俗成。
我特别喜欢一位 知乎网友 的 评论:「路由没有消失,只是转移了」。
2XX 家族
HTTP 状态码的用途在于表明客户端与服务器间通信的结果。2XX 状态码系列代表服务器已经成功接收、 理解并处理了客户端请求,回应的内容是成功的。以下是 2XX 系列中常见的状态码及其含义:
2XX 系列的状态码表示请求已被成功处理,这些状态码可以让客户端明确知晓请求已被正确处理,从而进行下一步操作。
是否需要全面使用 2XX 系列的状态码,取决于是否需要向客户端明确/显示的信息, 告知它下一步动作。如果已经通过其他方式(包括文档、口头协议)描述清楚, 那么确实可以通盘使用 200 状态码进行返回。但基于行为传递含义, 或是基于文档(甚至口头协议)传递含义,哪种更优秀呢?是更为复杂还是更为简洁?
设计风格选择
问题 | 解释(见下方单列分析) | 方案 A | 方案 B | 方案 C |
---|---|---|---|---|
复杂的参数是放到 Form Fields 还是单独一个 JSON Body | Form Fields | Body | ||
子资源是一次性查询还是独立查询 | 嵌套 | 独立查询 | ||
分页参数存放 | Header | URL Query | ||
分页方式 | 分页方式解释 | Page based | Offset based | Continuation token |
分页控制者 | 分页控制者解释 | 客户端 | 服务端 |
分页方式解释
我们最为常见的两种分页方式是 Page-based 和 Offset-based,可以通过公式进行映射。
此外,还存在一种称为 Continuation Token 的方式,其技术类似于 Oracle 的
rownum 分页方案,使用参数 start-from=?
进行描述。
虽然 Continuation Token 的优缺点都十分突出,使用此种方式可以将顺序性用于替代随机性。
分页控制者解释
在某些情况下,我们需要区分客户端分页(Client Pagination)和服务器分页(Server Pagniation)。
客户端分页是指下一页的参数由客户端计算而来,而服务器分页则是由服务器返回 rel
或 JSON.API 等协议。
使用服务器分页可以避免一些问题,例如批量屏蔽了一些内容,如果使用客户端分页,可能会导致缺页或者白屏。
设计风格选择
问题 | 解释(见下方单列分析) | 方案 A | 方案 B | 方案 C |
---|---|---|---|---|
模型呈现种类 | 模型的几种形式 | 单一模型 | 多种模型 | |
大模型如何包含子模型模型 | 模型的连接、侧载和嵌入 | 嵌入 | 核心模型 + 多次关联资源查询 | 链接 |
字段返回是按需还是归并还是统一 | 统一 | 使用 fields 字段按需 |
||
字段表现格式 | Snake | Camel | ||
错误码 | 无自定,使用 Message | 自定义 | ||
错误格式 | 全局统一 | 按需 | ||
时区 | UTC | Local | Local + TZ | |
HATEOAS | ✅ | 🚫 |
模型的几种形式
在 API 设计中,对于模型的表现形式有多种定义。虽然这并不是 API 规范必须讨论的话题,但它对于 API 设计来说是非常重要的。
我将模型常说的模型呈现方式分为一下几类,这并非是专业的界定,借用了 Java 语境下面的一些定义。 这些名称在不同公司甚至不同团队会有不一样的叫法:
image by alswl
除此之外,还经常使用两类:Rich Model 和 Tiny Model(请忽略命名,不同团队叫法差异比较大):
模型的连接、侧载和嵌入
在 API 设计中,我们经常需要处理一个模型中包含多个子模型的情况,例如 Book 包含 Comments。 对于这种情况,通常有三种表现形式可供选择:链接(Link)、侧载(Side)和嵌入(Embed)。
image by alswl
链接(有时候这个 URL 也会隐藏,基于客户端和服务端的隐式协议进行请求):
{
"data": {
"id": 42,
"name": "朝花夕拾",
"relationships": {
"comments": "http://www.domain.com/book/42/comments",
"author": ["http://www.domain.com/author/鲁迅"]
}
}
}
侧载:
{
"data": {
"id": 42,
"name": "朝花夕拾",
"relationships": {
"comments": "http://www.domain.com/book/42/comments",
"authors": ["http://www.domain.com/author/鲁迅"]
}
},
"includes": {
"comments": [
{
"id": 91,
"author": "匿名",
"content": "非常棒"
}
],
"authors": [
{
"name": "鲁迅",
"description": "鲁迅原名周树人"
}
]
}
}
嵌入:
{
"data": {
"id": 42,
"name": "朝花夕拾",
"comments": [
{
"id": 91,
"author": "匿名",
"content": "非常棒"
}
],
"authors": [
{
"name": "鲁迅",
"description": "鲁迅原名周树人"
}
]
}
}
还有一些问题没有收敛在四要素里面,但是我们在工程实践中也经常遇到,我将其捋出来:
我不是 HTTP 协议,怎么办?
Web API 中较少遇到非 HTTP 协议,新建一套协议的成本太高了。在某些特定领域会引入一些协议, 比如 IoT 领域的 MQTT。
此外,RPC 是一个涉及广泛领域的概念,其内容远远不止于协议层面。 通常我们会将 HTTP 和 RPC 的传输协议以及序列化协议进行对比。 我认为,本文中的许多讨论也对 RPC 领域具有重要意义。
有些团队或个人计划使用自己创建的协议,但我的观点是应尽量避免自建协议,因为真正需要创建协议的情况非常罕见。 如果确实存在强烈的需要,那么我会问两个问题:是否通读过 HTTP RFC 文档和 HTTP/2 RFC 文档?
我不是远程服务(RPC / HTTP 等),而是 SDK 怎么办?
本文主要讨论的是 Web API(HTTP)的设计规范,并且其中一些规则可以借鉴到 RPC 系统中。 然而,讨论的基础都是建立在远程服务(Remote Service)的基础之上的。 如果你是 SDK 开发人员,你会有两个角色,可能会作为客户端和远程服务器进行通信, 同时还会作为 SDK 提供面向开发人员的接口。对于后者,以下几个规范可以作为参考:
后者可以参考一下这么几个规范:
认证鉴权方案
一般而言,Web API 设计中会明确描述所采用的认证和鉴权系统。 需要注意区分「认证」和「鉴权」两个概念。关于「认证」这一话题,可以在单独的章节中进行讨论,因此本文不会展开这一方面的内容。
在 Web API 设计中,常见的认证方式包括:HTTP Basic Auth、OAuth2 和账号密码登录等。 常用的状态管理方式则有 Bearer Token 和 Cookie。此外,在防篡改等方面,还会采用基于 HMac 算法的防重放和篡改方案。
忽略掉的话题
在本次讨论中,我未涉及以下话题:异步协议(Web Socket / Long Pulling / 轮训)、CORS、以及安全问题。 虽然这些话题重要,但是在本文中不予展开。
什么时候打破规则
有些开发者认为规则就是为了打破而存在的。现实往往非常复杂,我们难以讨论清楚各个细节。 如果开发者觉得规则不符合实际需求,有两种处理方式:修改规则或打破规则。 然而,我更倾向于讨论和更新规则,明确规范不足之处,确定是否存在特殊情况。 如果确实需要创建特例,一定要在文档中详细描述,告知接任者和消费者这是一个特例,说明特例产生的原因以及特例是如何应对的。
Github 的 API 是我常常参考的对象。它对其业务领域建模非常清晰,提供了详尽的文档,使得沟通成本大大降低。 我主要参考以下两个链接: API 定义 GitHub REST API documentation 和 面向应用程序提供的 API 列表 Endpoints available for GitHub Apps ,该列表几乎包含了 Github 的全部 API。
问题 | 选择 | 备注 |
---|---|---|
URL | ||
API Path 里面 Prefix | 二级域名 | https://api.github.com |
Path 里面是否包含 API 版本 | 🚫 | Header X-GitHub-Api-Version API Versions |
Path 是否包含 Group | 🚫 | |
Path 是否包含动作 | 看情况(如果 HTTP Verb CRUD 无法满足就包含) | 比如 PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge POST /repos/{owner}/{repo}/releases/generate-notes |
模型 ID 形式 | Readable Stable Identity | |
URL 中模型单数还是复数 | 复数 | |
资源是一级(平铺)还是多级(嵌套) | 多级 | |
搜索如何实现,独立接口(/models/search )还是基于列表/models/ 接口 |
独立 | |
是否有 Alias URL | ? | |
URL 中模型是否允许缩写(或精简) | 🚫 | 没有看到明显信息,基于多级模型也不需要,但是存在 GET /orgs/{org}/actions/required_workflows |
URL 中模型多个词语拼接的连字符 | - 和 _ |
GET /repos/{owner}/{repo}/git/matching-refs/{ref} vs GET /orgs/{org}/actions/required_workflows |
是否要区分 Web API 以及 Open API(面向非浏览器) | 🚫 | |
Header | ||
是否所有 Verb 都使用 POST | 🚫 | |
修改(Modify)动作是 POST 还是 PATCH? | PATCH | |
HTTP Status 返回值 | 充分利用 HTTP Status | 常用,包括限流洗损 |
是否使用考虑限流系统 | ✅ 429 | |
是否使用缓存系统 | ✅ ETag / Last Modify | Resources in the REST API#client-errors |
是否校验 UserAgent | ✅ | |
是否校验 Referrral | 🚫 | |
Request | ||
复杂的参数是放到 Form Fields 还是单独一个 JSON Body | Body | 参考 Pulls#create-a-pull-request |
子资源是一次性查询还是独立查询 | 嵌套 | 从 Pulls 进行判断 |
分页参数存放 | URL Query | |
分页方式 | Page | Using pagination in the REST API |
分页控制者 | 服务端 | 同上 |
Response | ||
模型呈现种类 | 多种模型 | 比如 Commits 里面的 明细和 Parent Commits |
大模型如何包含子模型模型 | 核心模型 + 多次关联资源查询? | 没有明确说明,根据几个核心 API 反推 |
字段返回是按需还是归并还是统一 | 统一 | |
字段表现格式 | Snake | |
错误码 | 无 | Resources in the REST API#client-errors |
错误格式 | 全局统一 | Resources in the REST API#client-errors |
时区 | 复合方案(ISO 8601 > Time-Zone Header > User Last > UTC) | Resources in the REST API#Timezones |
HATEOAS | 🚫 |
Azure 的 API 设计遵循 api-guidelines/Guidelines.md at master · microsoft/api-guidelines, 这篇文章偏原理性,另外还有一份实用指导手册在 Best practices in cloud applications 和 Web API design best practices。
需要注意的是,Azure 的产品线远比 Github 丰富,一些 API 也没有遵循 Azure 自己的规范。 在找实例时候,我主要参考 REST API Browser , Azure Storage REST API Reference 。 如果具体实现和 Guidelines.md 冲突,我会采用 Guidelines.md 结论。
问题 | 选择 | 备注 |
---|---|---|
URL | ||
API Path 里面 Prefix | 二级域名 | |
Path 里面是否包含 API 版本 | 🚫 | x-ms-version |
Path 是否包含 Group | ✅ | |
Path 是否包含动作 | 🚫? | 没有明确说明,但是有倾向使用 comp 参数来进行动作,保持 URL 的 RESTful 参考 Lease Container (REST API) - Azure Storage |
模型 ID 形式 | Readable Stable Identity | Guidelines.md#73-canonical-identifier |
URL 中模型单数还是复数 | 复数 | Guidelines.md#93-collection-url-patterns |
资源是一级(平铺)还是多级(嵌套) | 多级 / 一级 | api-design#define-api-operations-in-terms-of-http-methods,注 MS 有 comp=? 这种参数,用来处理特别的命令 |
搜索如何实现,独立接口(/models/search )还是基于列表/models/ 接口 |
? | 倾向于基于列表,因为大量使用 comp= 这个 URL Param 来进行子命令,比如 Incremental Copy Blob (REST API) - Azure Storage |
是否有 Alias URL | ? | |
URL 中模型是否允许缩写(或精简) | ? | |
URL 中模型多个词语拼接的连字符 | Camel | Job Runs - List - REST API (Azure Storage Mover) |
是否要区分 Web API 以及 Open API(面向非浏览器) | 🚫 | |
Header | ||
是否所有 Verb 都使用 POST | 🚫 | |
修改(Modify)动作是 POST 还是 PATCH? | PATCH | Agents - Update - REST API (Azure Storage Mover) |
HTTP Status 返回值 | 充分利用 HTTP Status | Guidelines.md#711-http-status-codes |
是否使用考虑限流系统 | ? | |
是否使用缓存系统 | ✅ | Guidelines.md#75-standard-request-headers |
是否校验 UserAgent | 🚫 | |
是否校验 Referrral | 🚫 | |
Request | ||
复杂的参数是放到 Form Fields 还是单独一个 JSON Body | Body | 参考 Agents - Create Or Update - REST API (Azure Storage Mover) |
子资源是一次性查询还是独立查询 | ? | |
分页参数存放 | ? | 没有结论 |
分页方式 | Page based | |
分页控制者 | 服务端 | Agents - List - REST API (Azure Storage Mover) |
Response | ||
模型呈现种类 | 单一模型 | 推测 |
大模型如何包含子模型模型 | ? | 场景过于复杂,没有单一结论 |
字段返回是按需还是归并还是统一 | ? | |
字段表现格式 | Camel | |
错误码 | 使用自定错误码清单 | 至少在各自产品内 |
错误格式 | 自定义 | |
时区 | ? | |
HATEOAS | ? | api-design#use-hateoas-to-enable-navigation-to-related-resources |
Azure 的整体设计风格要比 Github API 更复杂,同一个产品的也有多个版本的差异,看 上去统一性要更差一些。这种复杂场景想用单一的规范约束所有团队的确也是更困难的。 我们可以看到 Azaure 团队在 Guidelines 上面努力,他们最近正在推出 vNext 规范。
我个人风格基本继承自 Github API 风格,做了一些微调,更适合中小型产品开发。 我的改动原因都在备注中解释,改动出发点是:简化 / 减少歧义 / 考虑实际成本。如果备注里面标记了「注」,则是遵循 Github 方案并添加一些观点。
问题 | 选择 | 备注 |
---|---|---|
URL | ||
API Path 里面 Prefix | /apis |
我们往往只有一个系统,一个域名要承载 API 和 Web Page |
Path 里面是否包含 API 版本 | ✅ | |
Path 是否包含 Group | ✅ | 做一层业务模块拆分,隔离一定合作边界 |
Path 是否包含动作 | 看情况(如果 HTTP Verb CRUD 无法满足就包含) | |
模型 ID 形式 | Readable Stable Identity | |
URL 中模型单数还是复数 | 复数 | |
资源是一级(平铺)还是多级(嵌套) | 多级 + 一级 | 注:80% 情况都是遵循模型的归属,少量情况(常见在搜索)使用一级 |
搜索如何实现,独立接口(/models/search )还是基于列表/models/ 接口 |
统一 > 独立 | 低成本实现一些(早期 Github Issue 也是没有 /search 接口 |
是否有 Alias URL | 🚫 | 简单点 |
URL 中模型是否允许缩写(或精简) | ✅ | 一旦做了精简,需要在术语表标记出来 |
URL 中模型多个词语拼接的连字符 | - |
|
是否要区分 Web API 以及 Open API(面向非浏览器) | 🚫 | |
Header | ||
是否所有 Verb 都使用 POST | 🚫 | |
修改(Modify)动作是 POST 还是 PATCH? | PATCH | |
HTTP Status 返回值 | 充分利用 HTTP Status | |
是否使用考虑限流系统 | ✅ 429 | |
是否使用缓存系统 | 🚫 | 简单一些,使用动态数据,去除缓存能力 |
是否校验 UserAgent | ✅ | |
是否校验 Referrral | 🚫 | |
Request | ||
复杂的参数是放到 Form Fields 还是单独一个 JSON Body | Body | |
子资源是一次性查询还是独立查询 | 嵌套 | |
分页参数存放 | URL Query | |
分页方式 | Page | |
分页控制者 | 客户端 | 降低服务端成本,容忍极端情况空白 |
Response | ||
模型呈现种类 | 多种模型 | 使用的 BO / VO / Tiny / Rich |
大模型如何包含子模型模型 | 核心模型 + 多次关联资源查询 | |
字段返回是按需还是归并还是统一 | 统一 | Tiny Model(可选) / Model(默认) / Rich Model(可选) |
字段表现格式 | Snake | |
错误码 | 无 | 注:很多场景只要 message |
错误格式 | 全局统一 | |
时区 | ISO 8601 | 只使用一种格式,不再支持多种方案 |
HATEOAS | 🚫 |
image from Apple Music
我最近在使用 Apple Music 时注意到了其 Web 页面的 URL 结构:
/cn/album/we-sing-we-dance-we-steal-things/277635758?l=en
仔细看这个 URL 结构,可以发现其中 Path 包含了人类可读的 slug,分为三个部分:alumn/$(name)/$(id)
(其中包含了 ID)。
我立即想到了一个问题:中间的可读名称是否无机器意义,纯粹面向自然人?
于是我测试了一个捏造的地址:/cn/album/foobar/277635758?l=en
。
在您尝试访问之前,您能猜出结果是否可以访问吗?
这种设计范式比我现在常用的 URL 设计规范要复杂一些。我的规范要求将资源定位使用两层 slug 组织,即 $(type)/$(id)
。
而苹果使用了 $(type)/(type-id)/$(id)
,同时照顾了可读性和准确性。
GraphQL 是一种通过使用自定义查询语言来请求 API 的方式,它的优点在于可以提供更灵活的数据获取方式。 相比于 RESTful API 需要一次请求获取所有需要的数据,GraphQL 允许客户端明确指定需要的数据,从而减少不必要的数据传输和处理。
然而,GraphQL 的过于灵活也是它的缺点之一。由于它没有像 REST API 那样有一些业务场景建模的规范, 开发人员需要自己考虑数据的处理方式。 这可能导致一些不合理的查询请求,对后端数据库造成过度的压力。此外,GraphQL 的实现和文档相对较少,也需要更多的学习成本。
因此,虽然 GraphQL 可以在一些特定的场景下提供更好的效果,但它并不适合所有的 API 设计需求。 实际上,一些公司甚至选择放弃支持 GraphQL,例如 Github 的 一些项目。
Complexity is incremental (复杂度是递增的)
- John Ousterhout (via)
风格没有最好,只有最适合,但是拥有风格是很重要的。
建立一个优秀的规则不仅需要对现有机制有深刻的理解,还需要对业务领域有全面的掌握,并在团队内进行有效的协作与沟通, 推广并实施规则。 不过,一旦规则建立起来,就能够有效降低系统的复杂度,避免随着时间和业务的推进而不断增加的复杂性, 并减少研发方面的沟通成本。
这是一项长期的投资,但能够获得持久的回报。希望有长远眼光的人能够注意到这篇文章。
主要参考文档:
]]>放弃一个合作了十几年的老朋友 Toodledo 让我有些伤感,但是这个过程也促使我总结了一下我在时间管理这个命题上的尝试,并分享了一些我的经验。
Image frrom Pixelbay
在这个快节奏的现代生活中,我们都会遇到大量的任务和信息,而如何有效地管理时间和任务成为了许多人的挑战。在读书时期,我也曾经陷入任务管理的困境。从 2009 年开始,我尝试过使用日历、笔记等工具来组织我的任务和信息,但效果并不理想。直到后来我接触到了 DoIt.im,进而发现了 David Allen 的书《Getting Things Done》,这本书带给了我一种全新的任务管理思路——GTD。往后数十年,我一直使用这个模式来管理自己的时间。
GTD 是由 David Allen 所创立的一种个人时间管理方法 ,旨在帮助人们更有效地管理自己的时间和任务,从而提高工作效率和生产力。这种方法的基本思想是将所有任务分解为具体的行动步骤,并将它们组织在一个可靠的系统中,以便于跟进和管理通过这种方法,人们可以更轻松地掌控自己的工作和生活,减少压力和焦虑,从而更加专注和高效地完成任务。
那么如何实现 GTD 呢?以下是基本的步骤:
注,我整理的结构和外部工具略有差异。我将分类、回顾都合并到组织步骤因为我发现组织本质是做计划,做计划纪要看收集的待办事项,也要回顾进行中项目和已完成项目,何不都在组织环节一起做好?
我曾使用过多个在线任务管理软件,包括 Doit.im、Remember The Milk、OmniFocus 和 Toodledo 等,下面分享一下我的使用历程和对比这些软件的差异。
我接触 GTD 就是从 Doit.im 开始。这是一款国产软件,也是我非常喜欢的一款应用程序。Doit.im 支持 GTD 的基本功能,提供了清晰的任务列表、分类、优先级等功能,还支持多种回顾模式和多平台同步。然而,我后来放弃了它,因为我发现了更为强大的 Toodledo。
Image frrom doit.im
我寻找下一款管理工具时,找到了 Remember The Milk(RTM)这个应用。RTM 提供了基本的任务管理功能,如添加任务、设置提醒、归档任务等等,并支持多平台同步,可以在电脑、手机等多个设备上使用。
然而,有些人可能不太喜欢 RTM 的用户界面设计,因为它看起来可能有些过时,不够现代化。此外,一些高级功能需要付费使用。当时我还是一个穷学生,没有为软件付费的能力,所以很快放弃了使用 RTM。
我从善用佳软了解到 Toodledo 这款任务管理工具(善用佳软是一个著名的软件使用分享网站,我在学生时代从这里获取了相当多的工具和思路)。
Image frrom toodledo.com
我很快在 葡挞生活 找到了一份非常完整的教程介绍 Toodledo 高级使用手法,尤其从其中获得了使用 Search 模块的方法。可惜现在网站已经无法打开了。不过,我们可以从一些其他渠道的截图中,略窥当时 Toodledo 的风采:
Image frrom twitter.com/productivelife
Toodledo 很快成为了我最喜欢的任务管理工具。它提供了非常全面的任务管理功能,支持任务分类、排序、筛选等,同时还支持多种回顾模式和自定义字段。此外,Toodledo 还支持多平台同步,并且具有强大的 API,可以方便地与其他应用程序集成。
由于 Toodledo 产品功能上一直不思进取,访问比较慢。我还曾经探索式短暂使用了 OmniFocus,这是一款专业的 Mac 平台任务管理工具,它提供了任务分解、项目管理、上下文等功能,并可通过 AppleScript 等方式进行扩展。OmniFocus 支持多种同步服务,包括 Omni Sync Server、WebDAV、FTP 等,用户可以根据自己的需求选择不同的服务。所有任务数据都是存储在本地的,因此用户可以在没有网络连接的情况下继续使用软件。
付费使用一段时间之后,我发现 OmniFocus 的搜索功能和 Toodledo 的 Search 功能实在无法相提并论它不能满足我的需求。
Image frrom www.omnigroup.com/omnifocus
我还曾经尝试过一些任务管理工具,包括 Trello、Asana、Basement、Tower、Teambition 等等,
但是由于它们的功能设计往往是为团队协作设计的,而我主要是个人使用,因此使用起来不够便捷。另外,这些软件的自定义搜索功能(当时)往往也比较弱,无法满足我的需求。最终我都返回到了 Toodledo 怀抱,直到现在。
我认为在 GTD 的体系中,收集和处理阶段相对机械和简单,真正考验人的是组织阶段:
下面我将分享一些我个人的理念和操作方法。
为了更好地组织任务,我们需要注意任务的重要属性,包括 Context(环境)、Project(项目)、Status(状态)和 Due(截止时间)Context 决定了当前环境下要做什么,是一个简单的过滤器。我将 Context 分为 Computer / Work / Mobile。Project 则将任务归属到不同的项目中,一个项目的周期比较长,并且其中的任务具有强相关性。Status 描述这个任务是立即可处理(Next)还是需要等待(Waiting),有些任务是周期性的则为 Active。Due 则是描述时间属性,一般有到点干(On),在这个时间之前(Due)以及尽量(Optional)。
除此之外,其他属性,如优先级(Priority)、类型(Folder)也需要考虑,但不是最重要的。
为了更好地管理任务,我每天早上都会打开四个视图:single、subtask、project-xxx 和 inbox
以下是筛选清单,用于确定需要立即进行的任务:
清单中的任务按照优先级、截止日期和执行状态的综合考虑排序。每次进入工作状态之后,只需要从这个清单中筛选出靠前的几个任务进行执行。
我使用它已经超过了 12 年,付费了 8 年,而且它的产品理念深刻影响了我时间管理的策略。 我介绍一些 Toodledo 中做得相当很不错的功能以及细节:
Toodledo 支持循环任务功能,用户可以将一个任务设置为循环任务,并指定它的重复周期,比如每天、每周、每月等。当任务完成后,它会自动重复出现在任务列表中,以便用户再次处理。这个功能可以帮助用户更好地管理一些常规性的任务:日常会议、每日邮件处理、每日 Review 等等。
Toodledo 对日期的支持非常丰富,用户可以根据任务的具体情况选择不同的 Due 类型,比如 Due(截止时间)、Timer(计时器)、Length(时常评估) 等等。这样一来,用户可以更加精细地管理任务的时间,有效地提高工作效率。另外,基于 Timer 计时器,我可以方便地统计一天的时间耗用情况更好地把控时间成本。我还开发了一个 Python 脚本用来分析每周的时间耗用。
Toodledo 的 Search 功能非常强大,支持丰富的逻辑查询(提供了条件组合的与或查询)用户可以根据任务名称、标签、日期、优先级等多个条件进行查询。这个功能可以帮助用户组织任务,提高任务管理的效率,我一度无法离开 Toodledo 也是因为,任务组织这个步骤我高度依赖这项功能。
整体而言,我还是愿意给大家推荐 Toodledo。我自己也在 Toodledo 之上基于它提供的 API 开发了 多款工具和插件。我给大家介绍一下:
Toodledo 提供了日历订阅能力,将待办事项显示在各类日历软件中。但原始的信息非常混乱,没有考虑任务的 Time,并且时间也是计算错误。为此我开发了一个 Web 服务,叫做 toodledo_calendar_filter。
它可以将 Toodledo 的任务更友好地显示在日历系统上。具体来说,它可以过滤 Toodledo 的 iCal,只显示那些具有到期日期和时长的任务。如果你也有相关需求可以直接使用我提供的在线服务(https://toodledo-calendar-filter.alswl.com)。
Before:
After:
当你使用 Toodledo 进行任务管理时,你是否感到操作繁琐,鼠标使用效率不高,非常怀念 Gmail 风格的快捷键?如果是这样,那么我的项目 alswl/my-toodledo 可以帮助你解决这个问题。
它是一个针对 Toodledo 的油猴脚本(User Script),提供了一系列 Gmail 风格的快捷键,让你可以像使用 Gmail 一样快速操作 Toodledo,包括移动任务(j
/ k
)、任务操作(x
标记完成,enter
启动任务)、切换任务视图(g s
跳转搜索)等等。它在 Chrome / Safari / Firefox 上运行稳定,如果你想要提高 Toodledo 的使用效率,不妨试一试吧。
为了快速收集任务,我曾经使用一个 Github 上面 CLI 小工具(wsargent/toodledo)。但它是基于 v1 版本的 API,官方已经在七八年前就说 v1 版本 API 要下线。于是我自己做了一个叫做 go-toodledo 小项目。
一开始 go-toodledo 只是提供最简单的 CLI 功能。后来我逐步补充了完整 SDK、CLI 全功能,甚至还提供了一个互动式的 TUI 小应用让用户可以方便在 Terminal 地使用 Toodledo。
Toodledo 官方并没有提供 OpenAPI 接口,我以逆向的方式生成了一份 go-toodledo/swagger.yaml,如果还有其他人有对 Toodledo API 开发需求,可以基于这份 API 直接使用。
最近,我对一直使用的任务管理服务 Toodeldo 的稳定性越来越失望。 他们换了新的服务提供商, 服务出现不稳定的频率更高,而新团队的产品能力也让我产生了一些质疑。 因此,我决定开始寻找下一个更好的任务管理服务。 因为 Kindle 在中国停止服务的事件让我对云服务的可靠性产生了疑虑。 因此,我开始寻找一些本地化的任务管理方案这些方案可以通过网盘(例如 iCloud)进行服务, 这样可以更好地保护我的数据和隐私。
Obsidian是一款基于本地文件的个人知识管理(KMS)和笔记应用程序,可帮助用户将其笔记和想法组织、链接和分析。它是一款纯粹的本地软件,我们可以使用网盘 / NAS 等方式进行多端同步。
Obsidian Tasks 则是一款强大的 Obsidian 插件它可以让你在 Obsidian 中轻松地管理任务和待办事项。这个插件可以通过简单的语法将任务添加到你的笔记中,并根据任务的完成情况自动标记任务的状态。你可以使用 Obsidian Tasks 来跟踪个人任务、工作任务、学习任务等等。此外,它还具有一些其他功能,例如自定义任务样式、创建过滤器来查找任务,以及可以根据任务状态自动移动任务。Obsidian + Obsidian Tasks 是一个完美组合,可以帮助你更好地管理你的待办事项,提高你的生产力。
Image frrom https://github.com/obsidian-tasks-group/obsidian-tasks
我的个人 KMS 已经完整地从 Notion 迁移到了 Obisidian,现在使用 Obisidan Tasks 迁移成本很低。经过一段时间对 Obsidian Tasks 的学习,我将原来的使用模式较为顺利搬到了 Obsidian Tasks。我接着之前两个场景整理和下一个任务来介绍如何高效使用 Obsidian Tasks。
我通过以下几种形式组织待办任务:
[ ]
标记的普通文档任务,一般是零散小任务。.todo.md
结尾的专用文档,以对项目进行统筹。#todo
标签的文档,一般是一个大事项,我会在顶部拆分出一个 TODO
队列,拆分出若干个 Tasks,拆完之后我会去掉 #todo
标签。基于这个组织结构,我轻松搞定整理和下一个任务这两个:
使用几个视图将 Obsidian Tasks 中记录的任务进行多个视图呈现分别是:
.todo
结尾的文件内,即分散在各个日常事务的碎任务#todo
标签的任务,往往是一个文档需要整体来处理这几个视图可以让你更好地将任务进行分类,并快速检阅各个任务。通过这些视图,你可以更好地管理你的待办事项,提高你的生产力。
这是我的 projects/View.todo.md`:
## Today
**WIP**
```tasks
status.type is IN_PROGRESS
```
**High(规划、设计都是高优先级)**
```tasks
not done
priority is above medium
```
**None repetead due today**
```tasks
((not done) AND (due before in 1 day)) OR (done on today)
is not recurring
sort by priority
```
**Repeated due today**
```tasks
((not done) AND (due before in 1 day)) OR (done on today)
is recurring
sort by priority
```
**Over due before today**
```tasks
not done
due before today
sort by priority
```
**Today complete**
```tasks
done
done on today
```
## Future (no repeat)
**in 1 day**
```tasks
not done
due before in 1 day
is not recurring
sort by priority
sort by due
```
**in 3 day**
```tasks
not done
due after in 1 day
due before in 3 day
is not recurring
sort by priority
sort by due
```
**in 7 day**
```tasks
not done
due after in 3 day
due before in 7 day
is not recurring
sort by priority
sort by due
```
## Singles Tasks
**in 7d(non project x no repeate)**
```tasks
not done
status.type is not CANCELLED
NOT (path includes .index)
NOT (path includes .todo)
NOT (path includes .notodo)
due after in 7 days
sort by priority, due
```
**none project x no due x no repeat**
```tasks
not done
status.type is not CANCELLED
NOT (path includes .index)
NOT (path includes .todo)
NOT (path includes .notodo)
no due date
sort by priority, due
```
## Projects
TODO
## Help
> [Queries Syntax](https://obsidian-tasks-group.github.io/obsidian-tasks/queries/)
在 Obsidian 的 Daily Note 插件中,设置模板为 _templates/daily
,这样每天就获得一个随时可以使用的当天待办事项。
这是我的 Dailly 设置,通过四个区块区分:今天到期单任务 / 今天到期重复任务(事务性,不重要) / 今日已完成 / 今日新任务(一般是 Single)。
这是我的 _template/daily.md
文件:
## TODO
**WIP**
```tasks
status.type is IN_PROGRESS
```
**New tasks**
**Tasks view - today**
```tasks
((not done) AND (due before {{date}})) OR ((not done) AND (due on {{date}})) OR (done on {{date}})
is not recurring
sort by priority, due
```
**Tasks view - today(repeated)**
```tasks
((not done) AND (due before {{date}})) OR ((not done) AND (due on {{date}})) OR (done on {{date}})
is recurring
sort by priority, due
```
**Over due date**
```tasks
not done
due before {{date}}
sort by priority, due
```
**Projects**
YOURS
**Tasks view - today done**
```tasks
done
done on {{date}}
```
首先,需要注意的是,Obsidian Tasks 是 Obsidian 的插件,如果之前没有使用 Obsidian,可能需要花费一些时间上手。不过,如果您已经使用了 Obsidian,那么 Obsidian Tasks 的双链功能将为您在 GTD 场景中提供极佳的支持,这既是其优点也是劣势。
其次,Obsidian Tasks 目前还没有提供计时器功能。不过,对于我个人而言,这一点已经不再重要了。随着年龄的增长以及十多年的 GTD 训练,我已经不需要通过工具来保持专注力。在工作状态下,我会比较专注,并且不会经常性分心。
最后,需要注意的是 Obsidian 自身的问题。Obsidian 是一个基于本地文件的工具,这意味着它没有在线工具(尽管有其他基于云存储的解决方案)。如果您希望找到一个电脑和手机都可以访问的在线服务,那么 Obsidian Tasks 可能不太适合您。对我来说,我使用 Obsidian 的方案是因为我拥有 Apple 全家桶,并购买了 iCloud 存储服务,因此我的数据都存储在 iCloud Drive 上。我使用 Obsidian 的 macOS 应用程序和 iOS 版本应用程序,这使我可以轻松地在 Macbook 和 iPhone 之间使用同一个 Vault(即 Obsidian 知识库)。启动应用程序之前,我会确认 Vault 同步完成后再进入应用程序。
时间管理是现代工作者不可避免的话题,也是我们时常为之烦恼的问题。GTD 的方法论恰恰提供了一种轻松而高效的管理方式,让我们能够迅速结束拖延、分心的状态,全身心地投入工作
工具很重要,但并不能解决所有问题。要做好时间管理,最重要的是要清楚自己的目标是什么当你心里有一个重要的使命时,它会不断地在你脑海中浮现,这时候甚至不需要 GTD 工具的管理。
希望本文介绍的 GTD 方法可以在你的工作和生活中带来帮助,让你更高效地完成自己的任务,同时也不要忘记享受生活的美好。记住,时间是有限的,珍惜每分每秒,抓住每一个机会,让自己的人生更加精彩!
]]>乐坛少有新歌,我经常听一些老歌,有时候顺着豆瓣音乐 Top 250 一点点听下去,感觉还是挺不错。 这中间一直有个麻烦地方:每次都要从豆瓣复制标题去 Apple Music 搜索。有没有简单便捷的方式直接一键抵达呢? 我没有找到答案,于是我自己整理了这批 Apple Music 链接,点击就可以直接跳转到 Apple Music 直接播放了
如上图所示,在本文点击「▶️ Apple Music」按钮,会打开 Apple Music 网站,点左侧的「Open in Music」即可在 Apple Music 打开。
PS:iPhone 上点击链接(Notes / Safari 等)会直接在 Apple Music 打开。
Jason Mraz / 2008-05-13 / Import / Audio CD / 民谣
9.1 ( 115738人评价 )
Coldplay / 2008-06-17 / 专辑 / CD / 摇滚
9.0 ( 117391人评价 )
陈绮贞 / 2005-09-23 / 专辑 / CD / 流行
9.0 ( 88220人评价 )
周杰伦 / 2001-09-14 / 专辑 / CD / 流行
9.5 ( 172202人评价 )
五月天 / 2008-10-23 / 专辑 / CD / 摇滚
9.0 ( 94647人评价 )
孙燕姿 / 2011-03-08 / 专辑 / CD / 流行
8.7 ( 81241人评价 )
Lenka / 2008-09-23 / 专辑 / Audio CD / 流行
8.6 ( 83181人评价 )
王若琳 / 2008-01-11 / 专辑 / CD / 爵士
8.8 ( 75301人评价 )
陈绮贞 / 2004-02-02 / 单曲 / CD / 流行
9.1 ( 98494人评价 )
nil
陈绮贞 / 2009-01-22 / 专辑 / CD / 流行
8.7 ( 74979人评价 )
Glen Hansard,Marketa Irglova / 2007-05-22 / Soundtrack / CD / 原声
9.2 ( 72580人评价 )
Keren Ann / 2004-08-24 / Import / Audio CD / 民谣
8.9 ( 62347人评价 )
Green Day / 2004-09-21 / Explicit Lyrics / Audio CD / 摇滚
9.0 ( 72130人评价 )
张震岳 Csun Yuk / 2007-07-06 / 专辑 / CD / 流行
8.8 ( 81378人评价 )
苏打绿 / 2007-11-02 / 专辑 / CD / 流行
8.8 ( 89048人评价 )
张悬 / 2007-07-20 / 专辑 / CD / 流行
8.7 ( 64429人评价 )
张悬 / 2009-05-22 / 专辑 / CD / 流行
8.6 ( 62928人评价 )
Damien Rice / 2003 / 专辑 / CD / 流行
9.1 ( 52588人评价 )
Green Day / 2005-06-13 / 单曲 / CD / 摇滚
9.4 ( 54233人评价 )
nil
周杰伦 / 2003-07-31 / 专辑 / CD / 流行
9.2 ( 103849人评价 )
周杰伦 / 2004 / 专辑 / CD / 流行
9.1 ( 159642人评价 )
Adele / 2011-01-24 / 专辑 / CD / 流行
9.3 ( 72900人评价 )
张悬 / 2006-06-09 / 专辑 / CD / 流行
8.8 ( 56634人评价 )
王菲 / 2000 / 专辑 / CD / 流行
9.4 ( 66995人评价 )
苏打绿 / 2011-11-11 / 专辑 / CD / 流行
9.0 ( 57675人评价 )
林宥嘉 / 2009-10-30 / 专辑 / CD / 流行
8.6 ( 59038人评价 )
Nirvana / 1991 / 专辑 / CD / 摇滚
9.3 ( 62973人评价 )
周杰伦 / 2002-07-19 / 专辑 / CD / 流行
9.2 ( 87077人评价 )
周杰伦 / 2000-11-13 / 专辑 / Audio CD / 流行
9.3 ( 90828人评价 )
Coldplay / 2000-07-10 / 专辑 / CD / 摇滚
9.2 ( 54165人评价 )
孙燕姿 / 2000-12-7 / 专辑 / CD / 流行
9.0 ( 60779人评价 )
陈绮贞 / 2000 / 专辑 / CD / 流行
9.1 ( 51164人评价 )
Avril Lavigne / 2002 / Enhanced / Audio CD / 摇滚
8.9 ( 55425人评价 )
周杰伦 / 2005-11-01 / 专辑 / CD / 流行
8.9 ( 94955人评价 )
方大同 / 2008-12-19 / 专辑 / CD / 放克/灵歌/R&B
8.6 ( 49661人评价 )
苏打绿 / 2006-10-20 / 专辑 / CD / 流行
8.9 ( 51741人评价 )
蔡健雅 / 2009-08-19 / 专辑 / CD / 流行
8.1 ( 51151人评价 )
Lady & Bird / 2003 / Import / Audio CD / 民谣
8.8 ( 43532人评价 )
万能青年旅店 / 2010-11-12 / 专辑 / CD / 摇滚
9.5 ( 84135人评价 )
Linkin Park / 2003-03-25 / Enhanced / Audio CD / 摇滚
9.1 ( 45142人评价 )
James Blunt / 2004 / Explicit Lyrics / Audio CD / 流行
9.0 ( 43229人评价 )
苏打绿,蘇打綠 / 2005年9月 / 国语 / CD / 流行
8.9 ( 53849人评价 )
梁静茹 / 2009-01-16 / 专辑 / Audio CD / 流行
8.5 ( 46781人评价 )
林宥嘉 / 2011-05-06 / 专辑 / CD / 流行
8.7 ( 48384人评价 )
Yann Tiersen / 2001-04-23 / Soundtrack / Audio CD / 原声
9.4 ( 44514人评价 )
王若琳 / 2009-01-16 / 专辑 / CD / 爵士
8.3 ( 43076人评价 )
Tamas Wells / 2006 / Import / Audio CD / 民谣
9.0 ( 39351人评价 )
nil
田馥甄 Hebe / 2010-09-03 / 专辑 / CD / 流行
8.2 ( 45022人评价 )
孙燕姿 / 2007-03-22 / Import / CD / 流行
8.6 ( 69921人评价 )
王菲 / 1999-09-10 / Import / CD / 流行
9.4 ( 56383人评价 )
Chris Garneau / 2007-01-23 / 引进版 / Audio CD / 民谣
8.8 ( 39023人评价 )
nil
孙燕姿 / 2003-08-22 / 专辑 / CD / 流行
9.1 ( 42823人评价 )
陈奕迅 / 2003-11-20 / 选集 / CD / 流行
9.4 ( 42728人评价 )
nil
苏打绿 / 2009-05-08 / 专辑 / CD / 民谣
8.3 ( 45534人评价 )
盧廣仲 / 2008-5-27 / 专辑 / CD / 民谣
8.4 ( 42135人评价 )
梁静茹 / 2007-11-09 / 专辑 / CD / 流行
8.5 ( 49186人评价 )
陈绮贞 / 2005 / 选集 / CD / 民谣
9.2 ( 36799人评价 )
Joe Hisaishi / 1999-05-19 / 专辑 / CD / 原声
9.5 ( 49564人评价 )
Taylor Swift / 2008-11-11 / Enhanced / Audio CD / 流行
8.7 ( 53119人评价 )
Mika / 2007-02-05 / 专辑 / CD / 流行
8.8 ( 37038人评价 )
陈奕迅 / 2009-03-23 / 专辑 / CD / 流行
8.7 ( 44462人评价 )
林宥嘉 / 2008-06-03 / 专辑 / CD / 流行
8.5 ( 45772人评价 )
Bruno Coulais / 2004-05-03 / Soundtrack / Audio CD / 原声
9.5 ( 37888人评价 )
范晓萱&100% / 2009-08-10 / 专辑 / CD / 摇滚
8.2 ( 39589人评价 )
Damien Rice / 2006-11-06 / 专辑 / Audio CD / 民谣
9.0 ( 36862人评价 )
王菲 / 2003 / 专辑 / CD / 流行
8.9 ( 46704人评价 )
曹方 / 2005年12月 / 专辑 / CD / 流行
8.4 ( 39449人评价 )
nil
李志 / 2007-01-11 / CD / CD / 民谣
9.1 ( 51735人评价 )
nil
方大同 / 2009-08-11 / 自选集 / CD / 放克/灵歌/R&B
8.4 ( 38003人评价 )
周杰伦 / 2006-09-05 / 专辑 / CD / 流行
8.6 ( 71242人评价 )
孙燕姿 / 2001-07-09 / 专辑 / CD / 流行
8.9 ( 44131人评价 )
孙燕姿 / 2005-10-07 / 专辑 / CD / 流行
8.3 ( 45359人评价 )
The Weepies / 2006 / Import / Audio CD / 民谣
8.6 ( 39732人评价 )
陶喆 / 2002-08-09 / 专辑 / CD / 流行
9.1 ( 51691人评价 )
Avril Lavigne / 2004-05-12 / Import / Audio CD / 摇滚
8.6 ( 40059人评价 )
孙燕姿 / 2004-10-1 / 专辑 / CD / 流行
8.7 ( 39004人评价 )
nil
Lady Gaga / 2008-08-19 / Import / Audio CD / 流行
8.5 ( 42436人评价 )
Jason Mraz / 2005-07-04 / Import / Audio CD / 流行
9.0 ( 34498人评价 )
五月天 / 2006-12-28 / 引进版 / CD / 流行
8.6 ( 40435人评价 )
Daniel Powter / 2006 / 专辑 / Audio CD / 流行
8.7 ( 35302人评价 )
Jason Mraz / 2008-12-16 / Single / Audio CD / 民谣
9.4 ( 39203人评价 )
许巍 Wei Xu / 2002-12-01 / 专辑 / CD / 民谣
9.1 ( 38564人评价 )
朴树 / 2003-11-28 / 专辑 / CD / 流行
9.0 ( 59817人评价 )
周杰伦 / 2007-11-01 / 专辑 / CD / 流行
8.2 ( 69903人评价 )
苏打绿 / 2009-09-11 / 专辑 / CD / 摇滚
8.7 ( 41388人评价 )
范晓萱 / 2001-08-25 / 专辑 / CD / 爵士
8.8 ( 35543人评价 )
曹方 / 2009-11-11 / 专辑 / CD / 流行
8.2 ( 33443人评价 )
孙燕姿 / 2003-01-10 / 专辑 / CD / 流行
8.7 ( 37301人评价 )
Radiohead / 1997 / 专辑 / Audio CD / 摇滚
9.4 ( 43284人评价 )
Lana Del Rey / 2012-01-31 / 专辑 / Audio CD / 流行
8.8 ( 48967人评价 )
Pink Floyd / 1979 / 专辑 / Audio CD / 摇滚
9.4 ( 45083人评价 )
痛仰 / 2008-10 / 专辑 / CD / 摇滚
8.7 ( 46100人评价 )
Linkin Park / 2000 / 专辑 / CD / 摇滚
9.0 ( 33231人评价 )
Nirvana / 1994-11-01 / Live / Audio CD / 摇滚
9.6 ( 34354人评价 )
陈绮贞 / 2004年12月 / EP / CD / 流行
9.2 ( 31467人评价 )
Jack Johnson / 2005-03-22 / 专辑 / Audio CD / 民谣
9.1 ( 29785人评价 )
五月天 Mayday / 2004-11-05 / 专辑 / CD+VCD / 流行
9.0 ( 35355人评价 )
五月天 / 2007-07-20 / 专辑 / CD+DVD / 流行
8.8 ( 41018人评价 )
陈奕迅 / 2008-06-30 / 专辑 / CD / 流行
8.4 ( 36475人评价 )
曲婉婷 / 2010-02-24 / 单曲 /
数字(Digital) / 流行 8.7 ( 46762人评价 )
Ennio Morricone / 1999-10-12 / Soundtrack / Audio CD / 原声
9.5 ( 35883人评价 )
张楚 / 1994 / 专辑 / CD / 摇滚
9.2 ( 39886人评价 )
徐佳莹 / 2009-05-29 / 专辑 / CD / 流行
8.4 ( 37502人评价 )
Avril Lavigne / 2007-04-17 / Import / Audio CD / 摇滚
7.9 ( 36865人评价 )
五月天 / 2005-11-18 / 专辑 / CD / 摇滚
9.3 ( 33223人评价 )
The Beatles / 2000 / 选集 / CD / 摇滚
9.5 ( 31020人评价 )
周杰伦 / 2008-10-09 / 专辑 / CD / 放克/灵歌/R&B
7.9 ( 59439人评价 )
Coldplay / 2005-06-07 / 专辑 / CD / 摇滚
8.6 ( 34819人评价 )
Adele / 2008-01-28 / Import / Audio CD / 放克/灵歌/R&B
8.6 ( 35491人评价 )
五月天 / 2003-11-11 / 专辑 / CD / 摇滚
9.1 ( 33602人评价 )
孙燕姿 / 2000-06-08 / 专辑 / CD / 流行
9.1 ( 34442人评价 )
nil
陈奕迅 / 2010-03-12 / EP / CD+DVD / 流行
8.9 ( 32688人评价 )
陈绮贞 / 2008-07-13 / 单曲 / CD / 流行
8.6 ( 29389人评价 )
田馥甄 / 2011-09-02 / 专辑 / CD / 流行
8.4 ( 35566人评价 )
梁静茹 / 2006-10-06 / 专辑 / CD / 流行
8.2 ( 34393人评价 )
王菲 / 2002 / 选集 / CD / 流行
9.5 ( 35275人评价 )
陈奕迅 / 2011-02-22 / EP / CD / 流行
8.8 ( 31725人评价 )
Norah Jones / 2002 / 专辑 / CD / 爵士
8.9 ( 30541人评价 )
朴树 / 2014-07-16 / 单曲 /
数字(Digital) / 原声 9.1 ( 79295人评价 )
nil
陈奕迅 / 2007-04-24 / 专辑 / CD / 流行
8.9 ( 37928人评价 )
Green Day / 2009-05-15 / 专辑 / CD / 摇滚
8.7 ( 30578人评价 )
張懸 / 2012-08-10 / 专辑 / CD / 流行
9.1 ( 39638人评价 )
王菲 / 1998 / 专辑 / CD / 流行
9.4 ( 39092人评价 )
陈绮贞 / 2007-05-18 / 演唱会/Live / CD DVD / 民谣
9.3 ( 26500人评价 )
朴树 / 1999-1 / 专辑 / CD / 流行
9.4 ( 44055人评价 )
GALA / 2004 / 专辑 / CD / 摇滚
8.8 ( 30290人评价 )
nil
孙燕姿 / 2002年1月 / 专辑 / CD / 流行
9.0 ( 29171人评价 )
李宗盛 Jonathan / 2007-09-28 / 专辑 / CD / 流行
9.6 ( 28244人评价 )
曹方 / 2007-11-20 / EP / CD / 流行
8.5 ( 27597人评价 )
窦唯 / 1994-10 / 专辑 / CD / 摇滚
9.4 ( 41855人评价 )
周杰倫,Terdsak Janpan,詹宇豪,陳承麒,黃婉琦,姚蘇蓉,黃俊郎,江語晨,長榮交響樂團 / 2007-08-13 / Soundtrack / CD / 原声
9.1 ( 45209人评价 )
梁静茹 Fish / 2005-9-16 / 专辑 / CD / 流行
8.3 ( 32166人评价 )
James Blunt / 2007-09-18 / Import / Audio CD / 流行
8.6 ( 26484人评价 )
飞儿乐团 F.I.R. / 2004-04-29 / 专辑 / CD / 摇滚
8.9 ( 41045人评价 )
陈奕迅 / 2009-09-23 / 专辑 / CD / 流行
7.8 ( 29505人评价 )
Linkin Park / 2007-05-15 / 专辑 / CD / 摇滚
8.4 ( 27501人评价 )
蔡健雅,Tanya / 2007-10-19 / 专辑 / CD / 流行
8.6 ( 28081人评价 )
Adele / 2011-01-24 / 单曲 /
数字(Digital) / 放克/灵歌/R&B 9.5 ( 40674人评价 )
nil
苏打绿 / 2006-09-18 / EP / CD / 流行
9.0 ( 27165人评价 )
Pink Floyd / 1973 / 专辑 / 黑胶 / 摇滚
9.5 ( 45690人评价 )
王菲 / 2001 / 专辑 / CD / 流行
9.2 ( 31937人评价 )
逃跑计划 / 2011-04-12 / 单曲 /
数字(Digital) / 摇滚 9.2 ( 60184人评价 )
nil
GALA / 2011-03-24 / 专辑 / CD / 摇滚
8.9 ( 36650人评价 )
莫文蔚 / 2010-07-26 / 专辑 / CD / 流行
8.0 ( 27226人评价 )
陈奕迅 / 2003 / 专辑 / CD / 流行
9.1 ( 27667人评价 )
宋冬野 / 2013-08-26 / 专辑 / CD / 民谣
8.9 ( 46048人评价 )
宇多田ヒカル / 2008-05-21 / CD IMPORT / Audio CD / 流行
9.3 ( 26585人评价 )
陈绮贞 / 2002-08-02 / 专辑 / CD / 流行
9.3 ( 30585人评价 )
The Innocence Mission / 2004 / 专辑 / CD / 民谣
8.5 ( 24692人评价 )
Timbaland,OneRepublic / 2007-11-06 / Single / Audio CD / 放克/灵歌/R&B
9.2 ( 32971人评价 )
王菲 / 2009-06-25 / 选集 / CD / 流行
9.4 ( 26218人评价 )
周杰伦 / 2010-05-14 / 专辑 / CD / 流行
7.8 ( 41601人评价 )
孙燕姿 / 2002-5-21 / 专辑 / CD / 流行
8.8 ( 27012人评价 )
李志 / 2004-12 / 专辑 / CD / 民谣
9.0 ( 29948人评价 )
nil
陈奕迅 / 2006 / 专辑 / CD / 流行
9.3 ( 29133人评价 )
五月天 / 2011-12-20 / 专辑 / CD / 流行
9.2 ( 26729人评价 )
陶喆 / 2003-08-08 / 选集 / CD / 流行
9.2 ( 26071人评价 )
Maroon 5 / 2007-05-22 / 专辑 / CD / 流行
8.4 ( 25476人评价 )
张悬 / 2008-10-20 / Bootleg / 数字 / 民谣
8.5 ( 26320人评价 )
nil
蔡健雅 Tanya Chua / 2003-06-00 / 专辑 / CD / 流行
8.7 ( 26152人评价 )
王菲 / 1996 / Audio CD / 流行
9.4 ( 37119人评价 )
自然卷 / 2004 / 专辑 / CD / 民谣
8.3 ( 24874人评价 )
nil
刘若英 / 2010-04-16 / 专辑 / CD / 流行
7.9 ( 24721人评价 )
Rosie Thomas / 2007-03-13 / 专辑 / Audio CD / 民谣
8.8 ( 23227人评价 )
nil
苏打绿 / 2008 / 引进版 / CD
9.2 ( 23965人评价 )
Lily Allen / 2009-02-09 / 专辑 / Audio CD / 流行
8.2 ( 24122人评价 )
手嶌葵 / 2008-03-05 / Import / CD / 流行
9.3 ( 24179人评价 )
李志 / 2009-10-16 / 专辑 / CD / 民谣
8.9 ( 32777人评价 )
大乔小乔 / 2007-07-12 / 平装版 / CD / 民谣
8.3 ( 23581人评价 )
nil
范晓萱 / 2004 / 专辑 / CD / 流行
8.7 ( 22928人评价 )
nil
卡奇社 / 2007-04-20 / 专辑 / CD / 流行
8.2 ( 24481人评价 )
冯曦妤 / 2008-11-20 / 专辑 / CD / 流行
8.5 ( 23442人评价 )
Coldplay / 2000-07-03 / EP / Audio CD / 摇滚
9.5 ( 37346人评价 )
Keane / 2004 / 专辑 / Audio CD / 摇滚
8.9 ( 22915人评价 )
陈奕迅 / 2005-06-07 / 专辑 / CD+DVD / 流行
9.4 ( 29071人评价 )
Maximilian Hecker / 2005 / Import / Audio CD / 摇滚
8.8 ( 21513人评价 )
nil
久石譲(Joe Hisaishi),杉並児童合唱団,井上杏美 / 1993-12-21 / Soundtrack / Audio CD / 原声 9.6 ( 26336人评价 )
崔健 / 1989 / 专辑 / CD / 摇滚
9.4 ( 31792人评价 )
棉花糖,katncandix2 / 2009-05-01 / 专辑 / CD / 民谣
8.0 ( 22804人评价 )
好妹妹乐队 / 2012-07-01 / 专辑 / CD / 民谣
8.9 ( 23906人评价
陈绮贞 / 1998-07-14 / 专辑 / CD / 民谣
9.1 ( 24812人评价 )
方大同 / 2007-12-28 / 专辑 / CD / 放克/灵歌/R&B
8.4 ( 23802人评价 )
方大同 / 2006-12 / 专辑 / CD / 流行
8.7 ( 25456人评价 )
逃跑计划 / 2014-12-09 / 专辑 / CD / 摇滚
9.0 ( 26819人评价 )
盧廣仲,卢广仲 / 2009-10-30 / 专辑 / CD / 民谣
7.9 ( 22531人评价 )
Nirvana / 2002-10-29 / Extra tracks / Audio CD / 摇滚
9.3 ( 22711人评价 )
nil
Coldplay / 2002 / 专辑 / Audio CD / 摇滚
8.8 ( 25535人评价 )
The Velvet Underground,Nico / 1967-04 / 专辑 / CD / 摇滚
9.3 ( 31513人评价 )
Eminem,Rihanna / 2010-08-20 / Single / Audio CD / 说唱
9.3 ( 28411人评价 )
nil
王菲 / 1994 / 专辑 / CD / 流行
9.4 ( 29503人评价 )
Original Soundtrack / 2009-03-30 / Import / Soundtrack / CD / 原声
9.3 ( 20223人评价 )
nil
Oasis / 1995 / 专辑 / CD / 摇滚
9.3 ( 27765人评价 )
陈绮贞 / 2007-02-08 / 单曲 / Audio CD / 民谣
8.7 ( 21369人评价 )
nil
Salyu / 2001 / 专辑 / CD / 原声
9.1 ( 22021人评价 )
Evanescence / 2003 / 专辑 / Audio CD / 摇滚
8.5 ( 21152人评价 )
黑豹 / 1992-12-01 / 专辑 / CD / 摇滚
9.3 ( 27065人评价 )
苏打绿 / 2010-08-27 / 视频 / CD+DVD / 流行
9.0 ( 23902人评价 )
梁静茹 / 2003年3月 / 专辑 / CD / 流行
9.0 ( 22492人评价 )
Suede / 1993 / 专辑 / CD / 摇滚
9.0 ( 24460人评价 )
Radiohead / 1995 / 专辑 / CD / 摇滚
9.3 ( 30381人评价 )
林海 / 2004 / 专辑 / CD / 轻音乐
9.4 ( 21797人评价 )
陈奕迅 / 2011-11-11 / 专辑 / CD / 流行
8.1 ( 24284人评价 )
彭坦 / 2007-07-23 / 专辑 / CD / 流行
8.2 ( 22022人评价 )
雷光夏 / 2006-12-01 / 专辑 / CD / 民谣
8.9 ( 21264人评价 )
范晓萱 / 1999-11-1 / 国语 / CD / 流行
8.8 ( 24064人评价 )
五月天 / 2001-07-06 / 专辑 / CD / 摇滚
9.3 ( 25704人评价 )
Owl City / 2008-03-18 / Import / CD / 流行
8.4 ( 20707人评价 )
李志 / 2009-01-22 / 专辑 / 数字 / 民谣
9.2 ( 23029人评价 )
nil
The Beatles / 1990-10-25 / Enhanced / Audio CD / 摇滚
9.5 ( 25678人评价 )
Lily Allen / 2006 / Import / Audio CD / 流行
8.4 ( 20280人评价 )
林宥嘉 / 2012-06-22 / 专辑 / CD / 流行
8.2 ( 24828人评价 )
牛奶@咖啡 / 2008-03-18 / 专辑 / CD / 流行
7.8 ( 27754人评价 )
梁静茹 / 2004 / 专辑 / CD / 流行
8.1 ( 25599人评价 )
戴佩妮 / 2009-05-16 / 专辑 / CD+DVD / 流行
7.7 ( 21671人评价 )
nil
王菲 / 2010-11-05 / 单曲 / CD / 民谣
9.0 ( 26139人评价 )
nil
Daniel Powter / 2008-10-14 / 专辑 / Audio CD / 流行
8.5 ( 19397人评价 )
五月天 / 2006 / 视频 / DVD / 流行
9.3 ( 20016人评价 )
曲婉婷 / 2012-07-01 / 专辑 / CD / 流行
8.1 ( 21550人评价 )
王菲 / 1997-09-30 / 专辑 / CD / 流行
9.4 ( 30662人评价 )
nil
汪峰 / 2009-07-25 / 专辑 / CD / 摇滚
8.8 ( 20801人评价 )
nil
莫文蔚 / 2002-04-29 / 专辑 / CD / 流行
8.8 ( 22357人评价 )
Maroon 5 / 2002 / 专辑 / Audio CD / 流行
8.8 ( 21946人评价 )
Amy Winehouse / 2006-10-30 / 专辑 / Audio CD / 放克/灵歌/R&B
8.9 ( 22840人评价 )
陈绮贞 / 2001-11-09 / Demo / CD / 流行
9.2 ( 19720人评价 )
nil
孙燕姿 / 2014-02-27 / 专辑 / CD / 流行
8.7 ( 32216人评价 )
Mariah Carey / 2008-04-16 / 专辑 / CD / 放克/灵歌/R&B
8.6 ( 21245人评价 )
Damien Rice / 2006-11-27 / 单曲 / Audio CD / 民谣
9.4 ( 18711人评价 )
陈珊妮 / 2008-11-22 / 专辑 / CD / 流行
8.3 ( 20153人评价 )
郑钧 / 1994 / 专辑 / CD / 摇滚
8.9 ( 24139人评价 )
张震岳 / 2004-06-24 / 选集 / CD / 流行
9.0 ( 20320人评价 )
朱玫玲,董運昌,王雁盟,何真真 / 2004/04/26 / CD / 轻音乐
9.2 ( 17459人评价 )
Green Day / 2009-07-14 / Single / Audio CD / 流行
9.2 ( 20417人评价 )
nil
蔡依林 Jolin Tsai / 2003-03-07 / 专辑 / CD / 流行
8.1 ( 34974人评价 )
nil
五月天 / 2011-12-20 / 专辑 / CD / 流行
9.2 ( 20376人评价 )
Oasis / 1994-08-30 / 专辑 / CD / 摇滚
9.2 ( 24503人评价 )
宇多田ヒカル / 1999 / 专辑 / CD / 流行
9.2 ( 24302人评价 )
张惠妹 / 2009-06-26 / 专辑 / CD / 流行
8.1 ( 23719人评价 )
王力宏 Leehom Wang / 2008-12-26 / 专辑 / CD / 流行
7.3 ( 23629人评价 )
The xx / 2009-08-17 / 专辑 / CD / 摇滚
8.9 ( 22501人评价 )
莫文蔚 / 2009-06-23 / 专辑 / CD+DVD / 流行
8.1 ( 20095人评价 )
Tizzy Bac / 2009-02-13 / 专辑 / CD / 流行
8.2 ( 19128人评价 )
五月天 / 2003年4月17日 / 专辑 / CD / 流行
9.4 ( 21570人评价 )
萧敬腾 / 2009-07-17 / 专辑 / CD / 流行
7.9 ( 24245人评价 )
Lady Gaga / September 23, 2008 / Single / Audio CD / 流行
8.8 ( 25206人评价 )
唐朝 / 1992-12 / CD+DVD / CD / 摇滚
9.1 ( 22391人评价 )
周杰伦 / 2003-12-1 / EP / 音乐CD / 流行
9.1 ( 29108人评价 )
林俊杰 / 2010-12-08 / 专辑 / CD / 流行
8.3 ( 34938人评价 )
]]>
「暴风雨结束后,你不会记得自己是怎样活下来的,你甚至不确定暴风雨真的结束了。 但有一件事是确定的:当你穿过了暴风雨,你早已不再是原来那个人。」
- 村上春树《海边的卡夫卡》
2022 开场非常魔幻,3 月份我们在上海迎来了长达两个多月的封城。 从浦东浦西鸳鸯封再到浦东上海全域静态管理,从开始说的几天封闭到持续两个多月的封闭, 我第一次切身体会到自由是无价的,也学会了如何在复杂多变的环境参与集体活动, 保护自己和家人。
疫情拉近了人和人之间的距离,本来在线的网友纷纷变成了线下的团长、志愿者。 在缺少物资(包括后来开放后阶段缺少布洛芬)情况下,印证了「远亲不如近邻」。 我和夫人两人都是社区积极参与者,早在疫情之前就通过贴二维码方式组件了小区微信群。 这次疫情中,我们两人也轮流参与志愿者,我搞了若干次团购,还总结了一篇 如何组织疫情团购。
年底的开放也是突如其来。 开放之后很快一家人都阳了。不幸中的万幸是阳的有序,至少还有「牧羊人」来做饭。
但一位亲人没有挺过这次疫情,她刚进行了手术。 手术很顺利,已经转入普通病房等待苏醒恢复, 结果遇上了新冠病毒。最后去世的原因不是原来脑部出血问题,而是新冠引起的呼吸衰竭。 我匆匆赶过去做了最后的送别,恍惚间,前几周还在家中健谈小辈各种趣事,突然就变成了天人永别。 悲痛之余,感叹人生命运无常和个人渺小无力,希望生者坚强,穿越阴霾继续前行。
我不想过多谈及政策、医疗制度、清零派和共存派的斗争。 只是感慨普通老百姓的生存不易、疫情的无情和生活的残酷的真相。 人到中年,愈发觉得好消息都是顺其自然应理所应当,而坏消息都是猝不及防怎会这样。
大问题并不需要大规模的解决方案,而是一个行动上的基本框架加无数个微小决策
这是我完全投入 Kubernetes 领域的第二年, 这一年精力都投入在如何大规模集群的优化和相应的工具平台。
目前我管理的核心集群规模超过一万节点,集群数量也近百, 规模带来了性能、容量、爆炸半径的管理难度,用户的需求也常常千变万化。 大规模集群面临的挑战在变多,历史债务却持续积累一直没有偿还干净, 而且还在不断要处理新加入的复杂问题:监管、多环境、对公云服务等等。
公司有稳定的业务和相当多试错场景,但是也包裹着海量的债务。 业务的压力导致了今年还不断在解决问题,没有投入太多的精力去做创造性的事情。 现在工作中面临最大的问题是体系化建设,挑战是如何繁琐的事情中将碎片的事情捏成团, 解决问题同时还要有创新。
今年还陆续给 ArgoCD 提了几个 PR,以解决在如此规模集群下 ArgoCD 工作性能问题。 可惜我投入最多时间的 lock-free 和 lazy watch 两个特性一直还没合入上游,几个前置的小 PR 都处理比较缓慢。
除了本职工作,还做了一些其他有意思的事情。 今年担任了大团队首席学习官,组织了两季 Infra SRE Meetup, 给大团队同学们搭建了一个场子分享, 我充分发挥了运营能力和设计能力,整了好多花活。 另外,我还意外地成为了一位厂里 Go 语言专家评委,参与了我厂 Go 语言规范的制定。 虽然我只是过去水了几场会议,倒是有这个机会跟更多技术老炮们交流学习。
新年的期望:能够持续做正确的事情,最重要的是不仅解决领域中具体的问题, 还能够保持高专业水平,构建一些系统化方案,加速问题解决。 如果还有时间,能好能给社区做做一些反馈,贡献一些代码。
可能是差生文具多的缘故,我一直对制作工具有浓烈的兴趣,今年有两个产出 excalidraw-collaboration 和 go-toodledo。
在封城 Remote 过程中,同事给我介绍了一个产品 Excalidraw。 这款在线协作画图工具产品感非常好, 它的手写风格解决了我画图强迫症的问题, 我挺想给工友们提供这个产品使用,但是首要解决是 SaaS 服务产品数据安全问题。
于是我做了 Excalidraw 的 私有化 不少朋友给了关注,还有人来咨询如何部署, 于是我又给出了一套一键部署方案 excalidraw-collaboration。
除了 Excalidraw之外,我还投入了不少时间撰写一个 GTD 的 CLI / TUI 工具 go-toodledo。 在实现了 CLI 功能之后,我又再接再厉,基于 bubbletea 实现了基于 TUI 的大部分的交互功能。 目前已经堪堪能用。明年还会继续打磨一下,将 go-toodledo 变成一个通用化的 GTD 管理工具, 对接更多平台,甚至提供一个类似 Obsidian 的 GUI 工具。
今年读书不多,主题阅读的话围绕投资相关入门书籍和 Go 相关的书籍:
又薄又好读,语言生动但又不过于活泼,适合入门。
做到世界第一流公司要志向远大,雇佣最一流的人才,并且给他们提供孵化创新的土壤。 更多书评 跟 Google 学开公司 - 谈谈方向、文化和人
化繁为简,剖析投资的技巧和手段手段。
一直以来,我想学会几件事:看懂国家政策消息、看懂公司财报、学会投资。 于是我陆陆续续看了一些关于经济 / 投资的入门书籍, 《投资中最简单的事》,《第一本经济学》,《世界上最简单的会计书》等。 这次读完吴晓波的《历代经济变革得失》,给我带来不少 新的输入,特别是书中对中央、地方、有产、 无产的四种力量和四大基本制度分析法,提供非常好的视角来分析各种事件。 更多书评 读《历代经济变革得失》
我挺少读文学类书籍,更别说是非虚构类文体。这本「活着就是冲天一喊」以一种异样的姿态,进入了我的阅读列表。 坦白说,我多少 有些大城市生活久了人的「固定思维」。中国经济在腾飞发展,乡村在不断变革,教育医疗一次次改革帮助群众生活的更好。虽然还有很 多历史困难,也有很多现实问题,但总体是向上的。这当然也是真的,从各类数据都能看大发展,但写灰尘之中的生活太少太少了,为这 些人发声的人太少,没有声音不代表不存在。 我刚开始拿起这本书时候,甚至还有些猎奇的想法:爆破工人能写出什么样的文字;他们 的日常生活是什么样子的?读着读着才知道,这是一个爆破工人的事情,一个有尘肺病的工人,也记录了一些他的工友、朋友、家庭的故 事。书中没有刻意去渲染苦难,没有唉声叹气和麻木不仁。除了苦难,还能看到的还有生命的活力,对美好的向往。
叙事恢弘,读了小一年才读完。朝代问题除了是军事问题还是经济问题。
回顾一下去年立的 Flag:
希望新的一年有更多时间高质量陪伴家人,
自我评价一下:今年在夫人带动下做到了几乎每周末都陪伴小朋友去户外, 郊野公园,市区公园,无动力乐园,滨江绿地,博物馆,科普馆, 亲子互动展,城市市集,玩水赶海…… 夫人的溜娃知乎号半年都在知乎知势榜榜单中,甚至还拿到了成长榜单第一名。
有时间和机会写更多高水平代码,更多外部输出,
高水平代码写的还不够,很多事情都要自己亲力亲为,不少精力投入在从 0 到 1 的工作。 比如初始化项目底座,约束设计风格、代码规范等等事情。 少量有意思的代码(比如 ArgoCD 的 patch)都是在忙里偷闲产出的。
坦率地说,我司人效比不够高,日常能有 1/4 时间输出已经是不错了。 复杂环境和对 KPI 极其看中的环境中,能有一些有价值的输出还是挺难的。 偷偷看了大团队研发产出数据,我输出几乎是每个月的榜首, 我自我感觉在这个环境下面做的还不错,遗憾的是没有更多的精力给社区做一些贡献。
产生更大的组织影响。
组织影响力,emm~ 可能还是局限在大部门,无法走出团队,缺少好的撬动点。 今年还是得多写多输出多分享。
2023 的 Flag 方向:
2023 愿年家人健康平安,我继续保持乐观和积极,在工作和生活上有些突破, 在人生马拉松中有一段中途加速跑。
]]>
吴晓波是经济史大家,我读过他的《激荡三十年》、《腾讯传》,笔法锋利,时常有深刻洞见评价。 这次是从经济变革的角度出发,历数从秦朝起著名的经济变革,介绍其发生的政治经济背景、手段和成果。 变革有成功的也有失败,但是往往逃不出政治局势的影响力。剖析隐藏逻辑和规律,辩驳得失,让读者酣畅淋漓。
我希望从读史(经济学历史书也算史吧)获取一些对当下有用的信息,回答现实中的一些问题:
变革(改革)这个词经常提到,为什么要变革?变革发起方是谁?
答案是中央,即国家的掌权者。为什么历朝历代这么多次发起改革呢?一言以蔽之,中央没钱了,需要通过一些制度的调整,增加收入。
至于为什么中央要收钱?这就是著名的桑弘羊之问:
- 帝国运转光靠农业的税收远远不够,如果不实行国有专营制度,钱从何来?
- 一旦爆发战争、灾荒等急需用钱,国库却空空如也,该怎么做?
- 如果中央不把重要财源掌控在手上,一旦国内分裂势力起兵造反,该如何抵挡?
总结一下,历史上(也包括现在)国家要用钱做建设;光靠农业税不行,中央要管钱否则国家会分裂。
PS:我们现在的政府收入以税收为大头,专营占比小。由于实行分税制,也不存在中央和地方财源掌握问题。
每次改革都是在特定的历史背景下面,比如商鞅要强国、汉武帝要打匈奴等等。古时中央政权需要钱办事,总需要钱,钱不够就要想办法。 这个就涉及到国家的钱从哪里来?我看看看,国家的收入有这么几种:
古时收钱路子不多,也不是金本位或纸币,常见方式:
纳粮往往是实体,而且纳粮过程中可能存在巨大猫腻,也就是中央政权和地方政权的博弈。 强中央和弱中央在纳粮这块控制能力差异比较大。这也是为什么古代无法依赖农业税的一个重要原因。 专营则不一样,流通的的金属货币,不容易在流通过程中产生损失,或者偷报瞒报。
每次变革的核心内容就是通过归属、制度的调整,重新厘定财源。 经济变革的内容都是搭配着相应的政治制度而来,影子脱离不了实体。
尽管每次变革并非单纯经济层面的变革。我这边还是用一句话讲讲每次变革是怎么回事:
我们可以看当前我国的 2021年财政收支情况, 看一下我们当前政府收入情况:
类目 | 收入(亿元) | 备注 |
---|---|---|
一般公共预算收入 | 202539 | |
政府性基金预算收入 | 98024 | 注:土地使用权、彩票公益、政府住房基金等 |
国有资本经营预算收入 | 5180 |
一般公共预算收入构成:
类目 | 收入(亿元) |
---|---|
国内增值税 | 63519 |
企业所得税 | 42041 |
出口退税 | 18158 |
进口货物增值税、消费税 | 17316 |
个人所得税 | 13993 |
国内消费税 | 13881 |
契税 | 7428 |
土地增值税 | 6896 |
城市维护建设 | 5217 |
印花税 | 4076 |
车辆购置税 | 3520 |
房产税 | 3278 |
关税 | 2806 |
证券交易印花税 | 2478 |
资源税 | 2288 |
城镇土地使用税 | 2126 |
车船税、船舶吨税、烟叶税等其他各项税收收入合计 | 1236 |
耕地占用税 | 1065 |
环境保护税 | 203 |
从饼图可以看到,目前我国的政府收入大头还是税收,税收中大头是增值税和消费税。而国营收入仅占整体收入 2%, 我对经济不够专业,所以对这个 2% 非常震惊。 另外从「政府性基金预算收入」这项占比 32%,也能够显著感受到我国对土地经济的依赖。
读完中国历代经济变革历史,能从其中感受到强烈的治乱循环。经济只是政治的影子。 治乱循环就是从四个阶段的循环,一次治乱往往对应一次朝代的重建。
那么问题是:现在到底是恒纪元还是乱纪元。从军事、民生、国际影响等来看,目前是恒纪元。 那问题就变成现在是恒纪元的上升箭头中,还是下降箭头中?
回顾一下改革开放的重要动作:
我们至今还生活在改革开放的增长曲线中。
从中国历史来看,和平发展 10 年小康,30 年盛世,50 年强国。然后进入不确定性时间。现在可能正在不确定的期间。 是否有信心取决于我们对治乱循环的周期判断。我的几个观察视角,从几个视角出发:
最后,我的观点是:目前经济发展基本盘看好,但是贫富分化在加剧、权贵经济在崛起,后疫情时代绑架了经济发展发动机。 我整体持谨慎乐观,从全书行文之间,也能够感受到吴晓波对国进民退的忧虑。
为了做好万全的准备,如何在 35 岁毕业后能够顺利入职电子厂?
从修键盘学起
我使用的键盘是 ErgoDox,一个人体工程学设计的分体键盘。关于 ErgoDox 更多详情可以见我之前的 回答。
(前任键帽配色 + 手托):
经过七八年工作,它进过水,进过咖啡,还进过豆浆,现在终于有几个键不灵活了,按起来有粘滞感,无法提供顺畅的 coding feel 了。
在使用备胎 Filco 几个月之后,我终于下定决心,要将 ErgoDox 修好。
没有焊接经验的朋友,可以学习一下如何焊接:
又有了那种打字畅快的感觉了~
青春回来了~
最后分享一下我的 ErgoDox Layout 配置:
若有收获,就点个赞吧。
]]>首先感谢岳父岳母带家中神兽去过暑假了,我才能有周末的时间来改这个系统。
另外感谢老婆,周末我两天都搞自己的事情,也没批评我。
最后感谢公司团建,提供酒店住,花了一个晚上时间搞定了中文手写体。
注:这是魔改私有化 excalidraw 开源版本,感谢社区
介绍一下 Excalidraw,产品特性
什么是 Excalidraw,这(可能)是最强的在线协同画图工具 ,你可以访问 Excalidraw 试试看。
如果你觉得打开来都是空白,那么也可以访问这个公共面板 Excalidraw 参与一起创作。
Excalidraw 非常好用,我总结几个点:
这个产品我们经内部同学内部安利之后,大家迅速的喜欢上了,在 Excalidraw 上面了好多图,但也引来一个问题。
Excalidraw 工作原理和私有化原理:
那私有化的核心难点是什么:
解决 excalidraw-storage 的数据存储问题,即替换掉 Google Cloud Platform 的 firebase 服务。
事有不顺反求诸己
- 孟子
求与诸己,不如求于 GayHub
- alswl
让我们先先研究 Excalidraw 的存储系统。 Firebase 是 Google 的 Serverless 服务,以前是独立公司(还挺火),后来给 GCP 收购了。
我一开始想法是替换 Firebase,找了一个社区服务 Supabase(意外发现还有免费的 SaaS 服务,良心啊)
但是仔细研究一下,发现 Supabase 的 API 和 firebase 不兼容,并 不能平替。
Excalidraw 还要靠 Excalidraw+ 卖钱(Plus 服务),怎么可能让你这么轻松就私有化,官方原来有个 excalidraw-json 的仓库,现在也被移除了。但是我们不怕,我们有来自社区的力量:
他们给了条路(虽然后来被证实还是有歪路的):
于是我开始检视他们方案,打开代码一看,思路正确(替换 firebase 的几个接口),使用自己的 KV 存储(Redis / MySQL / Mongo)替换。
尝试部署,立马遇到问题:
怎么这么巧,我是 前端实习生 // 社区打补丁专家 // Kubernetes 清洁工 // YAML 资深专家 ,专治这么几个毛病。
Excalidraw 是 SaaS 服务的免费版, Excalidraw+ 是付费版 ,有什么区别呢
总结:
或者还是直接购买 Excalidraw 企业版本服务吧,少折腾多享受。
有个问题, 中文字体不是手写体 ,很违和。先看看哪些字体能用吧:
macOS 支持的中文手写体:
cursive 家族:
windows 中文支持手写体比较差劲,必须安装 Office 才有更多选择:
华文行楷;方正舒体;(Office)
不行的话,只有系统自带的楷体 KaiTi 可以工作。
最后,靠我三脚猫的前端水平,做了一个 PoC,并给官方提交了一个 PR:feat: simple impl of multi font support, for chinese font by alswl · Pull Request #5604 · excalidraw/excalidraw
这是最终的效果:
可以访问 excalidraw.alswl.com 查看效果,这是一个静态站点, 支持中文字体,但是无法在线协作。
有几个网友来咨询如何进行部署。于是基于上述的方案,我提供了一套基于 Docker Compose 的一键拉起服务:带协作、中文字体支持。 仓库见 alswl/excalidraw-collaboration。
]]>我居然也有胆来想这个命题了。
大部分年轻人都在一个商业组织(即公司)中工作。 我们似乎对这个商业组织的运作已经很熟悉: 接触公司并进行面试和岗位匹配,在特定的岗位里面工作,过程也许开心或沮丧,通过组织的种种管理手段(KPI / OKR)来完成上级分发的任务。
这个过程中,我们往往遇到不少困惑,有些朋友还经历过职场 PUA(Pick-Up Artist);有些朋友可能感觉自己已经干的足够出色却无法得到晋升;有些朋友感慨合作的上下游太不专业了;总之,我们对公司不满是常态,而对公司满意则是反常。
其实很多时候,雇员的感受是公司运行的规律的投影。 我们制定 OKR 时候想去创造,应该通过什么方法论去开拓工作?这是公司战略决定的。 公司是怎么定义优秀人选的,我有没有机会获得晋升?这是公司用人态度决定的。 996 这么辛苦,我们该怎么去保持生活工作平衡?这是公司价值观和文化决定的。
Google 在这个方面似乎做得不错,从 2022 福布斯雇主(America’s Best Large Employers 2022)排名来看,Google 排在 34 名,互联网领域则是第 4 位(前三名分别是 Microsoft / Ultimate Kronos Group / LinkedIn)。
Google 做对了哪些事情?让我们换一个视角,从雇员视角切换到雇主,甚至再宽阔一些,以投资者视角来观察公司。 来解答一些关键问题:公司是如何决定前进方向的?公司每年的目标是应该怎么制定出来?公司通过什么方式筛选和激励人才?
重新定义公司 这本书告诉我们很多 Google 的回答, 国际大公司的方案未必能拿来立即用上,但这些答案可以给我们很多启发。
Google 说:战略、人才、文化、沟通,这些是定义公司的关键。
公司发展的真正原动力是什么?我思考答案的朴素的:科技在发展,世界在变得更复杂,人们希望自己生活更安全、幸福、高效。这时候就需要一些机构,尤其是利润驱动的商业公司来提供更好的服务来满足大家。从这个角度出发,公司只要是与人为善,帮助用户解决确实存在的问题,就创造了相应的价值。
那具体怎么创造,往哪个方向发展?追寻利润是唯一的驱动方向么?显然不是的,否则很多公司就要去围绕人性的弱点(黄赌毒)去开展业务了(你别说,这种公司还真的挺多)。
伟大的公司之所以伟大,是他们仅仅将利润看作一个副产品。真正指引是 基于对新兴技术的洞见而形成的判断。这是一种那什么是「洞见」呢? 技术洞见是创新方式应用科技或设计,达到成本或者特性的 10X 变化,甚至是代际变化。
制定的战略要足够有挑战。 Google 说 [这个战略是否足够吸引人,就好比 NASA 登月计划一样,让渴望的人聚拢在一起。而不是单纯一份体面的工作。」
如果想做一鸣惊人的事情确实比较困难,也不符合科技发展规律。 有时我们的创新仅仅是「组合式创新」,将领域 A 的知识复用到领域 B, 就形成了创新。计算机为什么持续受到热捧,因为这个作为基础领域可以跟大量其他领域进行相结合。
战略实施中,是否需要时刻盯着竞争对手? 这样做反而会落了下乘, 最关键的还是自己要找到自己的专业领域,一以贯之持续投入。 成为这个领域的专家。很多时候技术洞见,本质还是来自于人对领域的了解, 如果不是领域专家,那就不是洞见而是空想。
阿里集团和蚂蚁的愿景是什么?「让天下没有难做的生意」/「为世界带来微小而美好的改变」。这个愿景确实非常伟大, 也很有现实价值意义。 大公司提出的愿景也非常宏伟。宏伟也带来了不真实感, 字节「建设“全球创作与交流平台”」搭建平台,创造空间,提供场合,或者说大白话提供一个流量交换地。 Google 的愿景是「整合全球信息,供大众使用,使人人受益」,其实也是抽象的概念。 这些公司的愿景都是足够伟大,上限足够高,可以在这个方向上持之以恒挖掘代际感的产品/方案出来。
每个公司都有自己的文化,文化是设计出来的么?是也不是。 顶层设计时候都会有一些期望的文化状态(一般都是正确的废话)。 但真实的文化是民间形成的,这是在一次次冲突和决策下培育出来的。 比如阿里味、腾讯瑞雪、字节范儿(不过字节范儿据说官方自己定义了)。
Google 的文化,大家最熟悉的可能就是「不作恶」了。除此之外还有几个可以总结出来的点:
除了这些标语式的文化,Google 一些公司形态也很值得了解:
文化不是定义出来的,阿里味的文化其实也是民间长出来的,官方创新地将多个场景的「土话」提炼成阿里价值观。 阿里味:(我的总结)敢批评,勇于接受批评;内部产品创新;使命必达;组织结构动态调整。 外部所谓恶臭的味道:互联网黑话、面向 KPI、PPT、谈概念、PUA, 其实是某些媒体和个人的妖魔化。他们对立了雇主和雇员的关系,而弱化了创新、竞争的初心。但也不可否认,清水时间久了会变脏,文化不沉淀就会被稀释,
为什么产生这么大的认识差异。我理解:扩张太快,人才质量良莠不齐;外部社会形势变化,雇员压力被放大。 尤其是第二点,价值观这一件内核严肃的事情,从内心出发的感想行动,变成由外力驱动的事情。 为了和在这个价值观下群体行动一致,倒逼成为假阿里味。
是价值观错了?太形而上么?我对此持乐观态度,是有这么一群人,向善而行,也希望将公司做大做强。 越伟大的愿景,对成员的要求越高,也越难达到所有成员高度认可价值观状态。 在薪酬差异不大、业务技术创新性要求没那么高时候,要允许一部分人混口饭吃。
文化,尤其是民间自发形成的文化,没有对错,什么土壤结什么果。 这个公司做大做强,他的文化就是好文化了。一个成功的文化移植另外环境,未必一样能获得成功。 说白了,企业内部的文化,就是职场最佳实践。
公司要找什么什么样的人?老黄牛要不要,野狗要不要?是要高性价比人才还是要奢侈品?
Google 使用的招聘策略很特别,即用溢价来招聘最一流的成员。 他们甚至因为担心 B 级人才会引入 BCD 级别人才,而拒绝 B 级人才。
这一切的前提是 核心业务运行稳健并足够赚钱,才能让公司用溢价来留住优秀的人员。 另外,人才密度高也是一把双刃剑,过高的人才密度可能导致内卷。 想留住人才还是要靠优秀的现金流、提供足够有挑战的任务,良好的合作环境,以及良好的社会环境。
如果不满足这几个点,贸然学习 Google 的人才策略,就是东施效颦,会适得其反。 人才筛选说到底,还是投入产出比问题。有钱了喜欢买奢侈品,没钱平替也很好用。
Google 鼓励所有成员参与招聘,并且在绩效/影响力评估会考虑这个点。 同时设立了招聘委员会来对人才评级,避免出现不专业出现。 令人惊讶的是,所有人员的最终入职确认,都要 CEO 过目。 想象一下阿里的人都要逍遥子审批,这是多么巨量的工作。(我对 Google 这个讲法深表怀疑,可能呈现的数据是结构化之后的数据)。
合作中遇到最大的问题,就是点头和摇头。点头不附和,摇头有理由。 Google 鼓励成员用数据做决策,并且鼓励过程中的摇头,以及最终的分歧消灭。 话都是对的,但在真正执行中,如何能够创造一个敢说真话,讲信息含量高的话环境?
Google 也没讲出来。
我只能默认由于愿景广阔、人才质量高,人才溢价高,导致大家的合作是更为理性,更不考虑本位而是考虑真正的问题。 更愿意从公司的价值思考。但有人的地方就有江湖,人性也总是自私的。 理性决策最终还是要靠制度才能进行下去,也不存在最性价比的方案。
尽管想一下子弄清楚这么多问题是困难和不现实的。Google 的一系列做法给我们很多启示: 做到世界第一流公司要志向远大,雇佣最一流的人才,并且给他们提供孵化创新的土壤。
合上书闭上眼深思,回想自己身边,不敢评价全局,只能说身边的技术团队,现在在一个历史时间: 债务多,演变不彻底。技术洞见不少,但是落地困难。还有没有机会提出「登月计划」,引领下一代的科技变革呢?
老样子,我整理了大纲和一些有意思的摘录,供反复研读。
框架和纲要:
Google 招聘之行为准则:
- 雇用那些比你更聪明、更有见识的人。
- 不要雇用那些不能让你有所收获也不能对你构成挑战的人。
- 雇用那些能对产品和文化带来价值的人。
- 不要雇用那些无法为产品和文化带来积极影响的人。
- 雇用那些做实事的人。
- 不要雇用那些只想不做的人呢。
- 雇用那些满腔热情、自动自发的人。
- 不要雇用那些只想混口饭吃的人。
- 雇用那些能启发别人且善于与人相处的人。
- 不要雇用那些偏爱自己单干的人。
- 雇用那些能随着团队和企业一同成长发展的人。
- 不要雇用那些枯燥乏味、不具备全面技能的人。
- 雇用那些多才多艺、兼具独特兴趣和天赋的人呢。
- 不要雇用那些只为工作而活的人。
- 雇用那些道德高尚、坦诚沟通的人呢。
- 不要雇用那些趋炎附势、工于心计的人呢。
- 务必雇用优秀的候选人。
- 宁缺毋滥。
Goole 对高质量人才定义叫做「创意精英」,什么是创意精英呢?
]]>所谓创意精英,不仅拥有过硬的专业知识,懂得如何使用专业工具,还需要具备充足的实践经验。
创意精英有分析头脑,他们对数据运用自如,可以利用数据做出决策,同时也懂得数据的误导性,因此不会沉迷其中。他们认为,数据
对做判断大有帮助,但绝不对被数据牵着鼻子走。
创意精英有商业头脑。他们知道专业技术、优质产品与商业成功是环环相扣的,也对这三个要素的价值了然于胸。
创意精英有竞争头脑。在工作中,他们的杀手锏源自创新,但也离不开实干的积累。他们追求卓越、干净十足,即使在工作之余也不回
停止前进的脚步。
创意精英拥有用户头脑。无论身处那个行业,几乎没有人能比他们更懂得用户或消费者对产品的看法。我们把创意精英叫做超级用户,
因为他们对自己的兴趣并非浅尝辄止,而是近乎痴迷。
创意精英是新颖原创构想的源泉。他们用不同于你我的展新视角看问题。
创意精英充满好奇心。他们总是在提问,绝不满足于守常不变。
创意精英喜爱冒险。
创意精英自动自发。
创意精英心态开放。
创意精英一丝不苟
创意精英善于沟通。
创意精英必须具备商业头脑、专业知识、创造力已经实践经验,这些都是基本特征。
这两年疫情来袭,生活、工作的方方面面都产生了巨大变化,也是我人生的最艰难的一段时光。
2020 年中,母亲身体抱恙,术后逐渐康复,全家紧绷的神经也得到了一丝放松。但在年末,小朋友出了一些状况。 具体情况我不过多谈及,圈内人称为地狱爬行。三十而立的年龄,奔跑中时突然撞上了一道无形的墙壁,抬头望去, 一片漫无边际的黑暗笼罩。好在经过全家的努力,终于在 2021 年末柳暗花明。 这两年,压力之下,也重新在思考自己在家庭、团队、个人的角色定位。
在蚂蚁的前两年,我主要开发一款面向外部商户的 DevOps 系统,过程中不仅要设计开发,也要跑到用户那边做需求挖掘和产品交付。 还要对内讲清楚业务价值。这种「既要又要还要」非常反工程实践,一度让我沮丧。工程师内核坚持和「业务价值落地」的冲突, 我无法将其融合在一起。最终就变成干苦活没回报,产出被收割。在我快失去对工作的掌控和热情时, 托另外一位朋友福及时转岗,
新的领域是容器调度领域(Kubernetes),这个领域生机勃勃,是 Cloud Native 基石,也是云计算的基础。 这个领域特点是,背靠社区、客户稳定、需求(规模的扩大、稳定性的提高、性能的提升)可预见 之前工作时候,若干次接触过这个领域。 14 年左右我初次接触了 Mesos 调度系统,将其作为 Hadoop Yarn 的替代品用在离线计算任务上。 后来我又开发过一套名为 Prisim 的开发环境,基于容器技术提供独立的 Dev 环境,具备静态和基于域名的简单 Service Discovery, 当时土法造炮堪堪能用,支撑起公司的整个研发环境。
蚂蚁前两年的工作,几乎中断了我在基础设施领域的工作,内容发散不聚焦。现在终于重新拉回了主干路线。 虽然新工作也并不轻松,但深挖领域、探索新方向、寻找增长点很有意思。 一句话概括所做的事情:管理好这些大规模集群,并以这个目的为出发,设计开发变更、可见性、应急、生命周期管理等工具。 团队 TL 来自 Google,同伴也普遍有相当的水准(小组最近两年每年给 KubeCon / SREcon 贡献一场分享),能够在一个不卷的地方深挖,可能是大部分技术人的梦想了。
这两年做的还不够地方,是影响力建设,输出社区太少,输出内容也太少。时间金贵,总是感觉不够用。
正面管教 (豆瓣) 坚定、和善,发掘孩子的天性。
无条件养育 (豆瓣) 唯有爱,帮孩子身心健康,走得更远。
Java Concurrency in Practice (豆瓣) 并发相关殿堂级书,应该放到大学读,不管是不是 Java 开发者都应该阅读。我的笔记。
how to (豆瓣) 换个思路解决问题,有趣的小故事。
星之继承者 (豆瓣) 硬核科幻,想象力之大。
过去两年过的比较压抑,我面对了很多生活的不顺遂。这段仿佛漫长的时光,也给了很多我反思自己的机会。 三十而立,不仅仅指的对个人视野立定远谋,也指的就是这种上有老下有小,要肩扛腰背的立。 希望新的一年有更多时间高质量陪伴家人,有时间和机会写更多高水平代码,更多外部输出,产生更大的组织影响。
]]>SRE 工程师往往会负责一个具体组件,有时也称为服务或系统(下文都称之为组件)。 需要关注的有这个组件生命周期各类事项:运行状态、日常迭代、变更计划,以及在大促等活动中的筹备、预案等等, 有些组件是团队已经在长期持续维护着的,而有些则是要去新接入。 那么,当 SRE 接手(on-borading)这样组件时, 需要做哪些事项呢, 如何将「接手」这个行为做得有掌控力、顺畅且体面?
第一步永远是了解现状,孙子兵法谋攻篇说,知己知彼,百战不殆。 现状包含组件的当前运行状态、环境, 还包含当前 SRE 团队的能力、平台是否可以顺利衔接。
了解一个组件,可以先以用户角度进行切入。 去理解这个组件提供什么功能,服务对象是谁,服务的规模如何? 能否将组件进行归类?是属于普通业务系统,还是基础设施? 如果时间充裕的话,还可以跟这个组件的用户进行几次沟通,咨询,他们关于这个组件使用上的痛点。
近期和长期的规划也是需要重点关注的内容。 在组件设计和规划上面有没有什么计划和目标。根据规划我们可以推断出该组件处于何种生命周期。 生命早期的组件要多关注变更和基础能力建设; 成熟期组件往往承担了较为重要的角色,很可能承担了相当的生产流量,这时候变更、可观测性和应急方面就要花更多精力。 生命周期末期的组件则关注点是在维稳,优先考虑如何找到人,并且尽量低成本复用现有能力平台,甚至还要适当关注服务迁移和下线计划。
第二步是以技术的视角来切入, 分别从架构、依赖、部署、可伸缩能力、容量等角度切入,具体需要回答的问题如下:
结合 SRE 团队服务的其他组件,还要思考一下有哪些其他组件和当前组件类型一样,有什么差异点? 有没有特殊的部署要求?
对一个组件需要了解到什么程度才能接手? 这里我用几种程度来描述掌控力:
在经过一轮 PRR 完整流程之后,SRE 应当至少需要具备 L2 级别能力。 接管一段时间之后,随着对组件不断的了解,SRE 应当具备 L3 级别能力。
了解现状这个动作基本上是以静态的视角来看待组件。 完成之后,还要换成动态视角来看:有哪些日常操作(Operational)和紧急状态(Emergency)的操作?
需要关注的领域有可观测性、变更、应急预案:
在应急方面,还需要去了解过去历史上出现过哪些的问题。 翻看组件的故障复盘文档,了解历史上故障过程,故障原因,对应的 Action 是否落地? 尤其注意的是要关注 Action 工作机制是阻断式、检查式还是发现式? 老故障放到当下如果要避免是依赖系统、流程机制还是人工?
由于 SRE 日常工作主要构成是两类:能力建设和 On-Call。 在了解日常、应急场景事项时,还需要持续思考这些日常事项和应急动作能否基于 SRE 的工具平台、能力平台完成。 按照能力模型等级:手工 -> 工具 -> 自动化 -> 智能化来划分,当前日常、应急动作进入哪个环节了? SRE 也可以借事修物,借接收这个环节,重新审视自己的各类工具平台,是否满足这些日常、应急动作,能否更快更强更安全更准更好用?
为了保证接手过程的顺畅,以及日后合作的体面,服务范围必须要在接手环节明确清楚。
什么是服务范围?就是接手之后工作内容的边界和日常合作模式。
一个最常见的边界划分是。变更:SRE 团队负责 CI / CD 环境建设,而 SWE 团队使用 CI 环境完成日常的部署。 SRE 团队则使用自行建设的 CD 系统进行变更管理。 日常和应急:在日常 Operational 事项和应急中,SRE 会按预案进行处置并保障组件回到最理想状态。 SRE 还需要建设可观测性、应急相关的技术基础设施,对组件全生命周期监控和应急处理。 SRE 最终承诺的组件对外 SLA,并将 SLA 拆解为 SLO 跟 SWE 共背。 在接受过程中很重要的一个工作就是,理清楚组件的 SLI / SLO,并且根据现状跟 SWE 团队商榷出对外承诺的 SLA。
除了服务范围,SRE 和 SWE 还要建设任务协作机制和沟通机制。 有没有统一的任务记录和流转平台?遇到稳定性相关的反馈如何,如何将需求转化为任务并追踪完成? 故障相关的 Action 如何追踪落地?一些基础环境变化以及业务上活动变化,是否有统一场合进行同步? 比较理想的情况是,基于任务管理系统,一个需求/缺陷从提出,到设计,到实现,到变更,SRE 都参与其中。 考虑到成本,现实执行时候可以根据精力、理解成本、重要程度、组件生命阶段进行微调。 有一个简单低成本执行方式,将服务领域组件进行划分后,每个领域派遣一个 SRE 进入对口的 SWE 团队进行追踪: 参加 SWE 团队的周会和日会,并将信息带回 SRE 团队同步。
谨记,划分没有统一的标准,不同团队,不同技术成熟度,不同生命周期组件都会导致不一样的边界和合作模式。
「接手」是管理的第一步。 在了解现状、明确日常和应急事项、明确服务范围等一系列动作之后, 相信 SRE 已经具备初步掌控力了。有了方法论,还要持续精益求精,将掌控力从 L2 进步到 L4。 想把事情给真正做好,核心是持续学习思考在对应领域的基础技能,并且持续了解客户的需求变化。 保持一专多能,成为随时可以顶上去独当一面的 SRE,这才能避免成为一个工单人。🐶
image from Twitter
扩展阅读:
]]>静态类型正在逐渐成为潮流, 2010 年之后诞生的几门语言 Go、Rust、TypeScript 等都走了静态类型路线。 过往流行的一些动态语言(Python、PHP、JavaScript)也在积极引入语言新特性(Type Hint、TypeScript)对静态类型增强。
我曾使用 Python 开发规模较大的项目,感受过动态语言在工程规模变大时候带来的困难: 在重构阶段代码回归成本异常之高,很多历史代码不敢动。 后来技术栈转到 Java,被类型系统怀抱让人产生安全感。
最近一年在一个面向稳定性的运维系统耕耘。系统选型之初使用了 Python。 我在项目中力推了 Python 3.7,并大规模使用了 Python 的类型系统来降低潜在风险。
追根溯源,我花了一些时间了解 Python 在类型系统的设计和实现, 本文以 PEP 提案介绍一下 Python 在类型系统上面走过的路。
谈类型系统之前,要厘定两个概念,动态语言和动态类型。
动态语言(Dynamic Programming Language)则是指程序在运行时可以改变结构。 这个结构可以包含函数、对象、变量类型、程序结构。 动态类型是类型系统(Type System)其中一类,即程序在运行期间可以修改变量类型。 另外一种是静态类型:在编译期就决定了变量类型,运行期不允许发生变化。 类型系统还有一种分法是强类型和弱类型,强类型是指禁止类型不匹配的指令,弱类型反之。
动态语言和动态类型这两个概念切入点不一样, Python 是一门动态语言,也是动态类型语言,还是强类型的动态类型。 这篇文章主要讨论 Python 语言的类型系统,不会涉及动态语言特性。
行业里面一直有一个争论:动态类型和静态类型哪一种更强大。 静态类型的支持者认为三个方面具备优势:性能、错误发现、高效重构。 静态类型通过编译期决定具体类型可以显著的提高运行期效率; 编译期就能够发现错误,在工程规模逐步变大时候尤其明显; 类型系统可以帮助 IDE 提示,高效重构。 动态类型的支持者则认为分析代码会更简单,减少出错机会,写起来也更为快速。
Python 开发者们并非没有看到这个痛点, 一系列 PEP 提案应运而生。 在保留 Python 动态类型系统优势前提,通过语法、特性增强,将类型系统引入 Python。
Python 在 2014 年即提出了 PEP 484,随后提出一个精粹版 PEP 483(The Theory of Type Hints), 其工程实现 typing 模块在 3.5 发布。 经过 PEP 484,PEP 526,PEP 544,PEP 586,PEP 589,PEP 591 的多次版本迭代,Python 的类型系统已经很丰富。 甚至包含了比如 Structural Subtyping 以及 Literal Typing 这边相对罕见的特性。
PEP 483 在 2014 年 12 月发布, 是 Guido 起笔的核心概念版,简明扼要的写清楚 Python 的类型系统建设方向、边界、要和不要。
PEP 483 没有谈具体工程实现,提纲挈领地讲了一下 Python 类型系统如何对外呈现。
厘定 Type / Class 差别,前者是语法分析概念,后者是运行时概念。
在这个定义下面 Class 都是一个 Type,但 Type 未必是 Class。
举例 Union[str, int]
是 Type 但并不是 Class。
PEP 483 还介绍内建基础类型:Any
/ Unison
/ Optional
/ Tuple
/ Callable
,这些基础类型支撑上游丰富变化。
静态类型系统最大的诟病是不够灵活,Go 语言现在还没有实现泛型。 PEP 483 介绍了 Python Generic types 泛型使用方法, 形式如下:
S = TypeVar('S', str, bytes)
def longest(first: S, second: S) -> S:
return first if len(first) >= len(second) else second
最后,PEP 483 还提了一些重要的小特性:
PEP 483 的实现,主要依赖了 PEP 3107 – Function Annotations
这个提案。PEP 3107 介绍 function 注解使用。比如, func(a: a1, b: b1) -> r1
这段代码,
其中冒号后面的描述符记录会到 func 的 __annotations__
变量中。
PEP 3107 效果展示如下,可以清晰看到函数变量存放:
def add(x: int, y: int) -> int:
return x + y
add.__annotations__
# {'x': int, 'y': int, 'return': int}
PS:现在 Python 有了 Decorator 装饰器 / Annotation 注解,其中 Annotation 的设计还和 Java 的 Annotation 同名,一锅粥。
PEP 484 – Type Hints 在 PEP 483 基础上完整讲述 Python 类型系统如何设计,如何使用,细节如何(typing 模块)
这篇提案开宗明义地点出:
Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention.
一句话断绝了 Python 在语言级别进化到静态系统的可能。
提案除了 PEP 483 已经讲解的特性,还有以下吸引我的点:
.pyi
文件描述 Python 代码的带类型签名。
这个方案和 TS 的 @types
文件有异曲同工之妙。@overload
进行类型重载,这也是活久见,Python 居然可以(在某种意义上)支持重载了。Sized
/ Iterable
这些基础接口。
我个人认为这个工作量是其实挺大,是给已有的类进行一次依赖梳理。@typehints(foo=str, returns=str)
)、comments、Stub files、DocstringPEP 526 – Syntax for Variable Annotations 核心提案是给变量加上 Type Hints 支持。
和 function annotation
类似,也是通过注解方式存放。
差异是并不是给实例添加一个 __annotations__
成员,而是将变量的 annotations 信息存放在上下文变量 __annotations__
之中。
这个其实也比较好理解:定义一个变量类型时候,这个变量还没有初始化。
我写一段 Demo 展示一下:
from typing import List
users: List[int]
# print(__annotations__)
# {'users': typing.List[int]}
可以看到,上述 Demo 效果是在上下文变量创建了一个 users
,但这个 users
其实并不存在,只是定义了类型,
如果运行 print(users)
会抛出 NameError: name 'users' is not defined
。
观察字节码会更清晰:
L. 1 0 SETUP_ANNOTATIONS
L. 1 2 LOAD_CONST 0
4 LOAD_CONST ('List',)
6 IMPORT_NAME typing
8 IMPORT_FROM List
10 STORE_NAME List
12 POP_TOP
L. 3 14 LOAD_NAME List
16 LOAD_NAME int
18 BINARY_SUBSCR
20 LOAD_NAME __annotations__
22 LOAD_STR 'users'
24 STORE_SUBSCR
26 LOAD_CONST None
28 RETURN_VALUE
可以清晰看到,并没有创建一个名为 users 的变量,而是使用了 __annotations__
变量。
注:Python 存储变量使用 opcode 是 STORE_NAME
。
PS:本提案中有不少被否决的提案,挺有趣的,社区提出了很多奇淫巧计。 可以看出社区决策的慎重,存量系统升级的难度。
PEP 484 里面类型系统讨论的是 Nominal Subtyping, 这个 PEP 544 – Protocols: Structural subtyping (static duck typing) 则是提出了Structural Subtyping。 如果非要翻译,我觉得可以称为具名子类型 / 同构子类型。 注意,也有人将 Structural Subtyping 称之为 Duck Typing,其实这两者不相同,具体可以见 Duck typing / Comparison with other type systems。
Nominal Subtyping 是指按字面量匹配类型,而 Structural Subtyping 则是按照结构(行为)进行匹配, 比如 Go 的 Type 就是 Structural Subtyping 实现。
这里写个简单 Demo 展示一下后者:
from typing import Iterator, Iterable
class Bucket:
...
def __len__(self) -> int: ...
def __iter__(self) -> Iterator[int]: ...
def collect(items: Iterable[int]) -> int: ...
result: int = collect(Bucket()) # Passes type check
代码中定义了 Bucket 这种类型,并且提供了两个类成员。这两个类成员刚好是 Interator 的定义。 那么在实际使用中,就可以使用 Bucket 替换 Iterable。
PEP 586 – Literal Types
在 Python 3.8 实现,支持了字面量作为类型使用。
比如 Literal[4]
,举一个更有语义的例子 Literal['GREEN']
。
我第一反应这和 Scala 里面的 Symbol 非常像,Scala 中写法是 Symbol("GREEN")
。
这个特性使用挺学院派,很容易在 DSL 里面写的天花乱坠。
Scala 官方有说过可能在未来移除 Symbol 特性,建议直接使用常量替代。
PEP 589 – TypedDict: Type Hints for Dictionaries with a Fixed Set of Keys
给 Dict 增加 key 的 Type,继承 TypedDict
。
PEP 591 – Adding a final qualifier to typing
增加 final
/ Final
两个概念,前者是装饰器,后者是注解,标注该类 / 函数 / 变量无法修改
至此,Python 3.8 已经具备我们日常需要的类型系统特性(非运行时 😂)。
遗憾的是,typing
模块在文档鲜明的标注:
The Python runtime does not enforce function and variable type annotations. They can be used by third party tools such as type checkers, IDEs, linters, etc.
即:Python 运行时(Intercepter / Code Evaluator)并不支持函数和变量的类型装饰符。 这些装饰符只能由第三方工具检查,比如类型检查器、IDE、静态、Linter。
这个信息说明了 Python 在类型安全上尝试的局限性。所有的限制、约束都不会发生在运行时, 想要从类型系统中收获工程上面的价值,只能借助第三方工具。
诚然,Python 社区在竭力向类型系统靠拢,但是这种非语言级别 Runtime 的支持,到底能走多远呢? Python 缺少金主爸爸,干爹 Red Hat 投入资源也有限。连社区从 Python 2 切换到 Python 3 都还没走完,为何? 投入产出比太低,新特性缺乏足够的吸引力,替代品太多。
另一方面,看看竞对们: 动态语言在往静态语言靠拢,而静态语言也在不断吸收动态语言的特性。比如 Java 14 里面的 REPL(Read-Eval-Print-Loop), Kotlin / Scala 等语言的类型推断(Type Inference)。 也许这种演进方式更能够让用户接受吧。
随着业务规模扩大、团队组成变复杂,如何降低项目实施风险,降低软件复杂度变得尤为关键。 我从 Martin Flower、Joel Spolsky(软件随想录 作者) 等巨匠智慧中寻找解决复杂工程之道,其中 Code Review 是行之有效手段。 我认同 Code Review 价值也是忠实执行者。
加入蚂蚁以后,我在所接触项目中都大力推广 Code Review。 感谢团队信任和支持,目前 CR 协作进展顺利, 项目 CR 从最早不主动,到现在形成基于模块 Owner 制度 CR 和 Peer Review。 我也曾经在 3 个月内处理完成 700 多个 Pull Request,并在 PR 讨论中中都留下一些有价值讨论。 这里我将自己对 Code Review 一些理解记录下来。
这一篇先讲讲进入 Code Review 之前需要做准备工作。
不管是在一个遗留系统上推广 Code Review,还是在一个新团队推广 Code Review, 起手式不是立马挽起袖子猛干,而是应当观察一下当前所处情况是否允许自己推动这个「艰巨」任务。 最需要观察的是团队成熟度。
我将团队成熟度粗略分为草台班子、成长型团队、成熟团队。 具体衡量指标可以有成员技术储备、团队成员信任度、团队负责人对工程管控力度。 相信集团内大部分团队处于成长型、成熟型团队。 但如果考虑到生态同学一起协作,或者临时因为紧急项目凑在一起,就比较容易成为「草台班子」。
如果还停留在草台班子,那就得先在团队内形成一定共识: 包括技术共识和合作模式的共识。 技术共识需要包括:技术栈共识、好代码定义、研发流程共识等。 合作模式共识需要包括:冲突解决机制、职责范围边界、决策机制等。 如果基本共识还没有形成就仓促进入 Code Review,那就容易将一件好事变成工程师战争。
并不是所有团队、项目都适合无差别引入 Code Review。这么几种情况建议暂缓 Code Review 推广:
当条件不满足时也不用气馁。先成为一个布道师吧,探索一下团队中有没有相似想法的人,了解一下他们担心点和诉求点。 相信只要条件允许,没有人会拒绝提高工程质量。
Code Review 核心产出物是代码,如何界定代码是好是坏就至关重要。 根据 Bjarne(「C++ Programming Language」作者)、Grady Booch(「面向对象分析与设计」作者)等人定义, 总结下来好代码有这么基本特征(via「Clean Code」):
深思一下这些基本特征背后道理:代码是人类和机器沟通工具,好代码也要利于人类阅读和再次书写。 人比机器更犯错,机器算力在成倍增长,因此简单易维护重要度逐步增高。 这些朴素道理也正是语言从低级语言进化到高级语言驱动力, 也是近几年高级语言语言特性主打点(类型系统、格式系统、面向人类同步思想异步设计)。
但上面这几条原则太底层了,无法成为实践标准。 因此行业基于这些公理衍生了不少实践定理,我将其分为工程规约和设计范式两类。
工程规约包含语言风格规约和应用工程规约。 语言风格规约比较容易理解,开源世界提供了很多选择。硅谷大厂 Google / Facebook / Uber 等都有不少规约提供。 国内还有极具价值的「阿里巴巴 Java 开发手册」,我称之为 50331 ;) (PS:「阿里巴巴 Java 开发手册」不仅有语言风格规约、也有应用工程规约和一部分设计规约)。 这里罗列了一些常见面向语言的 Style Guide。
应用工程规约关注点则更为落地:包括依赖管理、配置管理、应用层次结构、对外服务暴露约束,数据建模,文档注释,测试管理, 甚至包含中间件使用规约。 这些规约逐步从「如何表达代码」延展到到「如何管理工程」。 这块发挥空间比较大,中大规模团队会有应用框架组这样团队。 基于应用框架对使用方式方法约束。针对具体业务使用也会给出针对性建议。 这些定理总结出来往往以轻量 Best Practice 和重量级应用框架对外输出。
这里举两个例子说明一下应用工程规约应该讲清楚哪些问题。 第一个例子 alibaba/COLA 这个项目来说,它约束了整体工程结构,并且明确的通过 Archetype 帮助(约束)用户行为。 第二个例子是 RoR / Django 这类 RoR 框架,他们使用约定大于配置方式,基于 ActiveRecord 这种模式约束了用户如何使用框架, 从 Model、Controller 到 Router,对立面则是 Spring MVC / Flask 这类框架,几乎只提供纯技术特性,不约束开发者行为。
除了阿里 Java 开发手册,这里提供三个应用工程规范供参考:
比工程规约更难定义的是设计范式,核心点是解决「如何做架构决策」。 设计范式决策过程在系统架构设计和系分设计阶段就应当完成了。 如果设计范式差异性导致代码在 Code Review 阶段出现意见不一。 要么是这个团队过于草台班子,要么是这个项目架构师/负责人在前期设计做的工作太少了。
当一个团队遵循相近设计范式,意味着他们达到了同一级别技术储 备,并形成了统一自顶向下设计打法。 在 Code Review 过程中,一些范式使用完全可以将 GoF23 / S.O.L.I.D. / DDD 这些术语概念拎出来。 相信 Commiter 和 Reviewer 可以基于业务场景快速达成一致。
方法论贴在墙上是无法解决开发中中遇到设计问题,在代码中真刀真枪干,Show me the fuck code。
除了工程规约,还有一件事情往往会被忽略,如何解决冲突? 这个冲突不是指代码 Conflict,而是面对技术方案决策时候交流讨论。
文无第一武无第二,两个秀才在一起能吵到天翻地覆面红耳赤。 技术层面 Augue 容易陷入我说我有理你说你有理局面。这种情况如何解决? 没有一个好协作机制,会倒逼 Commiter 和 Reviewer 退缩,不能充分展开讨论。 这是我们不想看见。
很多时候技术决策就是在 40% 正确性 和 60% 正确性中间选择,看中长期收益和短期收益就可能导致结论不一样。 非黑既白反而不容易有争论。想要促进快速决策,一套冲突解决机制就不可缺少。 因此一定要在事前制定一套冲突解决机制。大部分冲突解决都可以使用这么一套逻辑:数据、逻辑、民主、独裁。 当需要进行某个技术或者产品决策时候,最优先方式是用数据来量化,证明自己观点。 当数据不充分时候,可以进行逻辑归纳推衍形成结论。 当形成逻辑不能服众众说纷纭时候,可以使用民主方式进行裁决。 当民主也无法生效,就需要 Owner / 一号位力排众议,进行独裁决策了。
CodeReview 有这么几种形式,同步离线、异步离线、同步在线。 处于同步形式 CodeReview 需要尽快处理掉 PR,这时候协作机制一定要干净利索,避免大家在 PR 反反复复来来回回讨论。 如果每个 PR 都要消耗数次来回沟通,我建议将 CR 形式升级到同步在线,拉个会议立刻将事情解决。
如果在一个实现方案上真无法决策,如果不涉及原则,那我建议使用搁置争议,使用
TODO @commiter what, how, when
方式先标记意见和处理时间,先行将工程开发往下推进。
一切准备好了,谢天谢地,终于可以进入 Code Review 阶段了。 我们下篇文章再来讲进入 Code Review 阶段的事情。
]]>和老友聚餐时候完了一个游戏,大家各自找了一个词形容自己的 2019。我用的词是「累」和「平凡」。
2019 没有进入生门。
加入阿里之后,组织像榨汁机一样将个体精力榨干。 工作日几乎没有自己的时间。这样画面经常出现:回到酒店先倒头趴一会,然后洗完澡看眼儿子视频就睡觉,醒来又是新的循环。 周末的时间则是交给了家人,今年母亲身体抱恙,我尽量每月都能回一趟老家,以至于陪伴儿子的时间所剩无几。
工作日典型的一天是:早上 8:00 起床,酒店就餐,9:30 到公司,项目进度同步,杂项计划, 客户落地沟通,协同外部同事(外包 + 外部合作公司)困难事项解决。 下午 14:00 开始细项工作,写设计方案、写一点代码,做一些技术攻坚。 傍晚到 22:00 很可能开会(需求会、设计会),如果不开会就需要写一些设计文档。 另外一周有两个晚上在出差路上。 这还不算有些团队其他突发的小型项目需求过来。为了追求更多价值输出地方,我来者不拒。
这样算来,每周真正能自由支配时间只有 4-6 小时,这么一点时间刚好够做下工作日志整理,谈不上去学习新知识。 每天最适合学习的时间可能是在出差高铁路和日常打车时坐在后座。
个人 OKR 则是惨不忍睹,Q4 整个季度累的没有翻开 OKR 帐簿。 回过头来看,Q4 有些量化的产出完全靠日常习惯养成大类目。 最佳 OKR 贡献者居然是 RescueTime 效率大于 80 和工作日工作在案时间 8h 这两条。
我的 2019 是累的,由内而外。工作、家庭压力扑面而来,而立之年感受到生命的脆弱,以及个体在大组织的身不由己。 彻头彻尾感受到自己的渺小和普通。 王国维说人生有三种境界,我倒是听过人生三种落空:「对父母的期望落空」、「对自己的期望落空」和 「对子女的期望落空」。落空让我重新思考自己定位,欲望和所得是否匹配。
照例回顾 OKR,四个季度的 OKR 完成度分别是:
今年开局的两个 Q 的 OKR 完成度还不错,第三个季度则是由于家中有变,一下子打破了平衡。 第四个季度更是在照顾家中和 KPI 冲刺双重打压之下更为惨不忍睹。
新年的 OKR 怎么计划,我想了几天。 现阶段遇到的工作家庭负载太高了,强行设定高 OKR 不切现实。 与其总是在疲惫追逐,不如调整一下心态,将注意力收拢在少量的目标上。 因此我砍掉了大部分目标明确型 OKR,除了 Daiily 习惯养成,将 OKR 压缩到 2 个:
期待 2020 有收获,拿回生活和工作的主控权。
Direction |
D Rank |
Objectives |
O Rank |
Key Result |
KR Rank |
Rank Score |
Long Task |
Progress |
Plan |
Done |
Done Norm |
Score Total |
习惯养成 |
20% |
运动 |
20% |
每周跑步或游泳 2 次 |
50% |
2% |
V |
■□□□ |
104 |
6 |
0.0576923076923077 |
0.00115384615384615 |
体重 80kg -> 70 kg |
50% |
2% |
V |
■■□□ |
10 |
5.1 |
0.51 |
0.0102 |
||||
高效率 |
50% |
工作日个人时间 > 1h 每周 3 次 |
10% |
1% |
V |
■□□□ |
156 |
45 |
0.288461538461538 |
0.00288461538461538 |
||
工作日工作时间 > 8h 每周 4 次 |
25% |
2.5% |
V |
■■□□ |
208 |
117 |
0.5625 |
0.0140625 |
||||
周末个人时间 > 3h 每周周 2 次 |
25% |
2.5% |
V |
■□□□ |
104 |
18 |
0.173076923076923 |
0.00432692307692308 |
||||
RT 值达标(80/60) 每周 6 次 |
40% |
4% |
V |
■■■□ |
312 |
246 |
0.788461538461538 |
0.0315384615384615 |
||||
读书 |
20% |
每月读书 4 本 |
100% |
4% |
V |
■□□□ |
48 |
15 |
0.3125 |
0.0125 |
||
计划和复盘 |
10% |
每周做早晨计划 6 次 |
30% |
0.6% |
V |
■■□□ |
312 |
233 |
0.746794871794872 |
0.00448076923076923 |
||
每日做习惯追踪 |
30% |
0.6% |
V |
■■□□ |
364 |
193 |
0.53021978021978 |
0.00318131868131868 |
||||
每周做 Review |
40% |
0.8% |
V |
■□□□ |
52 |
11 |
0.211538461538462 |
0.0016923076923077 |
||||
影响力 |
5% |
写作 |
100% |
写作 12 篇 |
100% |
5% |
V |
■□□□ |
12 |
1 |
0.0833333333333333 |
0.00416666666666667 |
家庭 |
25% |
旅行 |
10% |
出国旅行 1 次 |
50% |
1.25% |
|
■■■■ |
1 |
1 |
1 |
0.0125 |
出沪旅行 2 次 |
50% |
1.25% |
|
□□□□ |
2 |
0 |
0 |
0 |
||||
父母 |
30% |
回父母家 6 次 |
100% |
7.5% |
V |
■■■□ |
6 |
15 |
2.5 |
0.1875 |
||
子女 |
60% |
每周陪伴小孩 >3h 2 天 |
60% |
9% |
V |
■■□□ |
96 |
56 |
0.583333333333333 |
0.0525 |
||
制定并执行子女成长计划 |
20% |
3% |
V |
■□□□ |
12 |
1 |
0.0833333333333333 |
0.0025 |
||||
笔记 3 本教育书籍 |
20% |
3% |
V |
■□□□ |
3 |
1 |
0.333333333333333 |
0.00999999999999999 |
||||
基础技能 |
20% |
数学 |
30% |
看完「统计学习方法」 |
25% |
1.5% |
|
■□□□ |
1 |
0.3 |
0.3 |
0.0045 |
看完「什么是数学」 |
25% |
1.5% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
看完「怎样解题」 |
25% |
1.5% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
写一篇关于数学的文章 |
25% |
1.5% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
英文 |
30% |
扇贝背 GAE 词汇(剩余 3600) |
30% |
1.8% |
V |
□□□□ |
3600 |
0 |
0 |
0 |
||
懂你英语打卡两个月 30*2 |
30% |
1.8% |
V |
■□□□ |
60 |
5 |
0.0833333333333333 |
0.0015 |
||||
写 2 篇英文博客 |
20% |
1.2% |
|
□□□□ |
2 |
0 |
0 |
0 |
||||
读完「语法俱乐部」并做完习题 |
20% |
1.2% |
|
□□□□ |
13 |
0 |
0 |
0 |
||||
系统思考 |
20% |
笔记「认知科学」6 本书籍 |
100% |
4% |
|
■■□□ |
4 |
2 |
0.5 |
0.02 |
||
团队管理 |
20% |
笔记「创业和管理」4 本书籍 |
70% |
2.8% |
|
■■■□ |
4 |
3 |
0.75 |
0.021 |
||
写 2 篇相关文章 |
30% |
1.2% |
|
□□□□ |
2 |
0 |
0 |
0 |
||||
专业技能 |
15% |
综合技能 |
40% |
关注 3 个会议(InfoQ、AS、?) |
40% |
2.4% |
|
□□□□ |
3 |
0 |
0 |
0 |
云原生相关技术学习(待定) |
60% |
3.6% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
语言和基础 |
60% |
笔记「Java Concurrency in Practice」 |
30% |
2.7% |
|
■□□□ |
1 |
0.2 |
0.2 |
0.0054 |
||
读完「七周七并发」 |
10% |
0.9% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
笔记「架构思想」4 本书 |
30% |
2.7% |
|
□□□□ |
4 |
0 |
0 |
0 |
||||
读完「自己动手写Java虚拟机」或者类似书 |
30% |
2.7% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
面向未来技能 |
15% |
机器学习 |
60% |
学完 Coursera Machine Learning |
50% |
4.5% |
|
□□□□ |
1 |
0 |
0 |
0 |
学习 Google Machine Learning Crash |
30% |
2.7% |
|
□□□□ |
1 |
0.2 |
|
0% |
||||
学完「集体编程智慧」 |
20% |
1.8% |
|
■■■■ |
1 |
1 |
1 |
0.018 |
||||
数据分析 |
40% |
笔记「深入浅出数据分析」 |
50% |
3% |
|
■■■■ |
1 |
1 |
1 |
0.03 |
||
笔记「利用 Python 进行数据分析」 |
50% |
3% |
|
|
1 |
|
|
|
Direction |
D Rank |
Objectives |
O Rank |
Key Result |
KR Rank |
Rank Score |
Long Task |
Is Done |
Plan |
习惯养成 |
30% |
运动 |
20% |
每周跑步或游泳 2 次 |
100% |
6% |
V |
□□□□ |
104 |
高效率 |
50% |
工作日个人时间 > 1h 每周 3 次 |
10% |
1.5% |
V |
□□□□ |
156 |
||
工作日工作时间 > 8h 每周 4 次 |
25% |
3.75% |
V |
□□□□ |
208 |
||||
周末个人时间 > 3h 每周周 2 次 |
25% |
3.75% |
V |
□□□□ |
104 |
||||
RT 值达标(80/60) 每周 6 次 |
40% |
6% |
V |
□□□□ |
312 |
||||
计划和复盘 |
30% |
每周做早晨计划 6 次 |
30% |
2.7% |
V |
□□□□ |
312 |
||
每日做习惯追踪 |
30% |
2.7% |
V |
□□□□ |
364 |
||||
每周做 Review |
40% |
3.6% |
V |
□□□□ |
52 |
||||
家庭 |
20% |
旅行 |
10% |
出国旅行 1 次 |
50% |
1% |
|
□□□□ |
1 |
出沪旅行 2 次 |
50% |
1% |
|
□□□□ |
2 |
||||
父母 |
30% |
回父母家 12 次 |
100% |
6% |
V |
□□□□ |
12 |
||
子女 |
60% |
每周陪伴小孩 >3h 2 天 |
100% |
12% |
V |
□□□□ |
96 |
||
输入 |
25% |
读书 |
100% |
每月读书 4 本出笔记 |
100% |
25% |
|
□□□□ |
48 |
输出 |
25% |
写作 |
100% |
写作 24 篇 |
100% |
25% |
V |
□□□□ |
24 |
|
100% |
|
|
|
|
100% |
|
|
|
PS:文章成于 2020-01-19。
]]>在阿里,我们不得不承认一个事实:前端的确有价值,但放在全局来看,前端产生的价值并非核心价值。 在阿里,虽然前端的工作已 经不可或缺,但对大公司而言,不可或缺的岗位多了去呢,不可或缺不代表有核心价值,我就不说了。
- 玉伯 2013 年 阿里前端的困局与突围
image from Abbey Arches Architecture - Free photo on Pixabay
前几天和某大厂前端负责人 G 聊职业生涯发展,聊着聊着就谈到了前端和后端职业天花板。 我表达了自己观点:后端职业天花板更高,这是由职能细分决定。
后端(服务端)概念比较宽泛,常见分类可以有应用开发工程师、中间件工程师、甚至可以包含运维、数据工程师、算法工程师。 本文我只将后端工程师限定在应用开发工程师以及衍生的框架、库开发工程师。 前端这边由于引入大前端概念,概念也比较广,包含:Web 前端、移动端(iOS + Android 客户端)、桌面端(PC 端)。 我们也限定在这几个方向的应用开发。
有些同学可能不服气,现在基于 Node.js 也能写后端应用,并且已经有越来越多成熟产品。 单页应用推动了 React / React Native / Vue 等技术发展,这类前端框架也需要基于 MVC / MVVM 设计模式管理复杂数据流。 在端场景,Hybrid 应用愈发成熟,并且在一些特定领域比如 AI,iOS 也引入 Core ML 技术。 这样天花板还不够高么?
是的,尽管前端近年发展迅猛,探索出多种新技术,从更广泛端技术来看,Android / iOS 也迎来爆炸式发展, 几乎隐隐有势头盖过后端趋势。 随着业务复杂度提升, Web Frontend / Android / iOS 开发困难度愈发提升;随着科技普惠云计算发展, 技术门槛会进一步降低,当前前端工程师会有更宽阔空间。 但是仍然认为后端是更难掌握,职业天花板更高。
听我一一道来。
为了避免争执,我们先来看看如何评估一项技术复杂度,拎出三个衡量技术复杂度维度:
由于服务时效性是一个动态概念,先基于业务复杂度和数据量复杂度画个简图:
^
D | +-------------+ +-------------+
a | | | | |
t | | Big | | Complex |
a | | | | |
| +-------------+ +-------------+
s |
i |
z | +-------------+ +-------------+
e | | | | |
| | Simple | | Diversified |
| | | | |
| +-------------+ +-------------+
|
+---------------------------------------->
Business Logic
为什么后端更难挑战更大,有以下原因:
贴近业务。后端是业务逻辑忠实实现方和执行者,所有业务链路、业务分支、主流程和旁路都需要在后端有实现。 由于现在用户体验方面要求逐步提高,确实有不少业务链路和检查动作在前端呈现出来。 但这种链路即便有呈现,后端还是要进行建模、检查校验。 反观前端阿喀琉斯之踵,运行在端(浏览器 / 手机端),对用户透明,会面临安全问题。 从而导致数据无法持久化(即便持久化也是为了性能,这些数据不可信)。
可用性要求高。可用性体现在两个方面,实时可用性(也就是我们常说的 SLA)与面向未来设计(或者说向前兼容)。 前者要求系统是可以持续提供服务,这就带来了高可用、可扩容要求。这对整体系统设计带来了巨大挑战。 单点算力可用性增强的模式已经被证明不可行,分布式是被证明的可行道路。 后者对设计者提出了更高要求,系统需要兼容过去,还要给未来变化留下口子,当变化来临时候才可以低成本实现。 反观端上技术,本地无状态,无持久化数据,因此既没有实时可用性要求,也没有面向未来设计要求。
抽象程度高。抽象是为了解决业务复杂性和易变性,将具体业务以合理可扩展方式设计好。 这也是贴近业务的直接体现。 为了解决复杂业务下面抽象程度高问题,工程界提出了许多方法论。 设计层面有 DDD 领域驱动设计、微服务设计等;工程实施层面有各种设计模式、SOLID 开发模式、重构方法论等。 端上技术更多着眼在 UI 层面方法论:Reactive、Flux 数据流、动态热更新等。
上下游空间大。后端处于研发链路中间,前承端,后启运维数据算法,横接运营产品项管。 从我周围样本观察,当项目缺乏负责人时候,往往会从后端开发工程师挑选资深一员作为项目 Owner。 从人数上来看,后端往往也占据开发大的大多数。 由于上下游空间大,后端工程师职业天花板也会更开阔。转 DevOps / SRE / 算法 / 数据的后端开发工程师比比皆是。 这种转换非常自然,也提高了后端工程师的天花板。
贴近运行系统。当后端工程师部署他的项目,推动上线后,他往往需要对这个应用的运行环境有更多了解。 对于后端应用来说,生命周期可能是开发一两周,运行一两年。这种模式下面倒逼后端对 Linux 服务器环境有更多了解。 否则在生产环境运行时候,缺乏相关技能和工具掌握遇到问题就会束手无策。需要了解的还不仅仅是 Linux 部署环境, 还有相关的基础设施: RPC 系统、Queuing 队列系统、缓存系统、容器化环境等等基础设施。
我看到有不少前端工程师们已经开始尝试突破天花板,进行「升维攻击」。我将其总结为这么三条:
PS:前端还有一类发力方向 - 复杂 UI 产品,比如 Web Editor,Google Doc、Office Online。 随着大前端发展,这方面的空间已经大大扩充,Web / App 前端已经可以基于 Flutter / Electronic 等技术做 PC 端应用。 但这类应用数量较少,开发技能点和常见应用开发差异较大,不作为常规路线讨论。
我给前端同学的建议:
蚂蚁体验技术使用另外一种模式规避了这个问题,将前端概念从「端」扩展到「体验技术」。 格局一下子扩大,不再限于在用户浏览器运行,关注边界扩大到用户使用的方方面面。 他们推出的 语雀 Lark、AntDesign 以及背后支持的 Basement 开发者技术栈也确实给使用者带来更好体验, 加速了开发者速度,降低了前端工程师开发门槛,完整诠释了「体验技术」意义。 关于「体验技术部」故事,你可以看 那些年的体验技术部 - 知乎 了解更多。
后端开发工程师瓶颈是什么?我认为是业务理解,而业务理解抓手是数据理解。 最朴素业务理解是帮助产品经理梳理需求,将其所构想产品工程化实现出来。 但以更高要求来对待时候,单纯实现就远远不够了,要考虑成本和收益。 比如用户 Landing 页面优化,投入一周时间进行开发这是成本,那么期望到收益是什么? 更高的用户转化率。后端工程师是否有意识对这些数据进行建模、AB 测试、跟踪结果,进而帮助产品、运营进行决策?
除了完成功能,数据理解还有另外一个层面意义。在数据量规模到达一个量级时候,系统所追求不仅仅是可用、稳定, 还需要从沉淀数据中挖掘业务内在关系,将数据模型展现出来。这个工作内容已经超越了后端工程师职责, 属于数据工程师(还有一些叫法数仓管理员)职责。他们工作核心内容就是通过对业务数据挖掘,发现问题、解决问题,给予业务指导。 手段是建立体系化量化指标,将沉淀数据和日常业务关联。
后端是否可能被取代?我认为传统应用开发工程师被取代概率高,未来 10 年左右时间可以被取代。 为什么这么说,什么工种可以被取代?越标准化的工种越容易被取代。应用业务开发经过这些过程:
应用开发工程师参与了 2 3 4 5 6 部分, 每个部分产出物已经逐步呈现出标准化势态。比如需求分析文档+系统设计文档,已经具备标准化雏形(离岸外包也是基于这个来开发)。 进一步想,如果一门语言描述能力足够强,需求分析阶段即可将实现完成。4、5、6 也可以被自动化设施所取代。 应用开发工程师在整个工程师生态里面是手的存在,而非脑存在。手总是可以通过工具增强所替代。 我们设想一种场景,让业务方自己写 SQL,然后根据 SQL 生成标准化的 DAO 模块、Service 模块、View 模块、配套上合适的 UI 界面, 就可以拿出去直接用了。
后端如何才能不被取代呢? 在复杂度上面施力。复杂度可以往两个方向发展,对业务有更深刻理解成为业务专家。 支持大数据量和提供高可用性,成为基础设施专家,比如分布式存储、搜索、算法、芯片、网络、效能*。
另外一种升维办法是成为技术团队技术管理者,融合技术领导者和团队管理者两种技能。
后端天花板更高,但之所以高并非语言等技术因素带来的,本质原因是贴合业务更近、需要处理数据更多、有状态并且需要长期提供服务。 前端突破天花板就不是前端,后端突破天花板就不是后端,不要被角色限定自己学习内容,不要给自己划定边界。
最后提一个问题,现在越来越火的 Serverless,如果后端来建设该如何建设?如果前端来建设该如何建设?
]]>图片摄于北京圆明园
怎么描述 2018?我找了三个关键词。
我的 2018 年从北京拉开帷幕,1 月份时候团队几个小伙伴一起去给北京研发中心同学做了一次技术分享。 TL 余老师带我们三个架构师逛了圆明园,北京小伙伴热情招待我们吃了东来顺火锅。 在天津时候还第一次从海河冰面渡河。只是没想到,这次旅行成了我在沪江最后一次团建,
当时我已经有一些想法离开沪江,一些问题逐步暴露出来: 沪江工作舒适、做事情自由,但技术上缺乏量级挑战,制度上内部流程僵化,部门利益关系错综复杂。 我深信业务、技术是互相引领促进,在这种环境下无法全力施展拳脚。
恰好此接到蚂蚁金服 Sourcing 电话,于是开始走面试流程。事后证明,这是不靠谱沟通流程, 无准备状态的我让一些细节问题考倒(这也让我养成了 Sourcing 候选人时候都会问一句最近有没有做过面试准备)。 最终我还是走完了流程,以(我以为的) SRE 身份加入了蚂蚁。这里隐含一个巨大风险, 当时我对蚂蚁金服体系不够了解,这也给之后「拥抱变化」埋下了伏笔。
加入蚂蚁之后一个月后,第一个项目就让我大跌眼镜,我成为了一个增删改查工程师,而且是在技术债务较为严重项目上开展。 这个项目疲惫不堪,倒不是加班加点,而是在业务进度压迫之下必须囫囵吞枣实现功能, 不顾工程设计缺乏,框架使用方式不标准,带来大量低效劳动重复劳动。 期望成为 SRE 做 Infrastructure 的我一下子就蒙逼了,我先压下不满和震惊,抱着学习态度先投入完成交给自己的任务。
9 月儿子出生,我似乎做足了准备,但仍然发现准备不充分。 怀抱红彤彤的小精灵,我该如何做好父亲一角色? 这触发了我对家庭关系、父子关系的思考。我能给他提供什么,带来什么?我希望他成为什么样的人? 坎贝尔的千面英雄里提到的英雄之旅给我深刻印象。 人生历程是一个少年成为英雄的过程:懵懂、自我、挑战、出走、成长、灭龙、回归。 期望在他英雄之旅上,我能成为他的后盾,他的炮火,也能在他疲惫时候给他温暖归处。
陪产假很快结束,我重新回到工作中节奏仍然没有能控制起来,做了两个体量并不大的项目。 「拥抱变化」这个词我很喜欢,这是一种 VUCA 下面抵抗不确定性生存策略。 我不想让信任我的伙伴失望,也不愿意轻易放弃,持续想在业务场景中挖掘技术能够发挥价值。 18 年末我抓到一个机会。尽管这个想法还不够完善,但这个机会是我能够发挥价值重大突破口。
纵观 18 年,我职业生涯面临了巨大拐点,做了一个选择。 这也许不是最好,但是回头看 19 年沪江经历了一场巨大人事变动,至少这不是最差。
语言力量是软弱的,我直接用量化数字来回顾 2018。 很不幸,按照年初计划 OKR 来看,这一年我的评分特别打脸。 原因有如下几个:
量化表格附在本文最后。 完成度按照每季度如下:
第二季度是在沪江最后一段旅程,这段时间我个人时间充沛,做了不少事情。
完成了 ProgFun 学习,尽管狼狈仍然跑完了开智写作课。
第三季度开始我产能大幅降低,主要受家庭影响。
第四个季度我精力大量投入在工作上面,仅剩一些精力优先放到家庭,进一步导致个人事项度降低。
工作时间大幅增加,每周用 1 天时间陪伴家人,另外有一天时间用来个人事项处理。
个人时间由以前每个月 70((2*5+6*2)*4
)降低到 24(6*4
),几乎只有之前 1/3 时间。
接下来是 2019 年 OKR 安排。从个人发展方向上来说 18 年安排问题在于贪心。综合个人发展和精力分配, 以去年完成度为参考,新年任务安排需要调整为 1/4。
19 年 OKR 有这么几个原则:
2019 OKR 具体表格在附录。
有一点比较感慨,想做事情多,但是真正计算下来,尤其是加入阿里之后,个人能够分配时间真是少之又少。 真想好好珍惜学生时代以及以前工作不那么繁忙时光。
聊一些轻松一些话题,推荐一下我 2018 年使用的不错物品、阅读书籍和观影。
2019 已经过去一个季度了,我刚结束为期一个月 9116 项目冲刺。 终于可以缓口气,在四月初带小孩出行踏青看落樱。 尽管偶尔疲惫不堪,偶尔对自己工作不满,但仍然对未来充满信心和期待。 机会时代的智慧不仅仅只是等待和希望,更应该是探寻和希望。
Direction |
D Rank |
Objectives |
O Rank |
Key Result |
KR Rank |
Rank Score |
Long Task |
Is Done |
Plan |
Done |
Done Norm |
Score Total |
---|---|---|---|---|---|---|---|---|---|---|---|---|
掌握面向未来的技能 |
10% |
机器学习的基本技能 |
80% |
学完 Coursera Machine Learning |
80% |
6.4% |
|
□□□□ |
1 |
0 |
0 |
0 |
学完「集体编程智慧」 |
20% |
1.6% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
掌握区块链知识 |
20% |
Bitcoin / ethereum / ada / iots / eco |
50% |
1% |
|
■□□□ |
5 |
1 |
0.2 |
0.002 |
||
读 2 本区块链的书 |
50% |
1% |
|
■■□□ |
2 |
1 |
0.5 |
0.005 |
||||
掌握基础的技能 |
20% |
数学 |
15% |
看完「数学之美」 |
20% |
0.6% |
|
□□□□ |
1 |
0 |
0 |
0 |
看完「什么是数学」 |
20% |
0.6% |
|
■□□□ |
1 |
0.3 |
0.3 |
0.0018 |
||||
看完「如何解题」 |
20% |
0.6% |
|
■□□□ |
1 |
0.1 |
0.1 |
0.0006 |
||||
写一篇关于数学的文章 |
20% |
0.6% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
弄清楚概率论的研究对象、范畴、方法论 |
20% |
0.6% |
|
□□□□ |
3 |
0 |
0 |
0 |
||||
提升英文水平 |
20% |
扇贝背完 TOFEL 词汇 |
25% |
1% |
V |
■■■■ |
1 |
1 |
1 |
0.01 |
||
扇贝背 GAE 词汇 60 天 |
10% |
0.4% |
V |
■□□□ |
60 |
21 |
0.35 |
0.0014 |
||||
懂你英语打卡 30*4 |
30% |
1.2% |
V |
■□□□ |
120 |
23 |
0.191666666666667 |
0.0023 |
||||
写 2 篇英文博客 |
15% |
0.6% |
|
□□□□ |
2 |
0 |
0 |
0 |
||||
读完「语法俱乐部」并做完习题 |
20% |
0.8% |
|
■□□□ |
22 |
9 |
0.409090909090909 |
0.00327272727272727 |
||||
经济管理 |
15% |
读完「经济学原理」 |
20% |
0.6% |
|
□□□□ |
1 |
0 |
0 |
0 |
||
读完吴晓波的一系列书 |
30% |
0.9% |
|
□□□□ |
6 |
0 |
0 |
0 |
||||
读 3 本关于投资的书 |
30% |
0.9% |
|
□□□□ |
3 |
0 |
0 |
0 |
||||
通过投资获得 X 元收益 |
20% |
0.6% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
提升系统化思考能力 |
20% |
读 5 本相关书籍 |
40% |
1.6% |
|
□□□□ |
5 |
0 |
0 |
0 |
||
学完 Coursera Model Thinking 课程 |
60% |
2.4% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
团队管理 |
30% |
阅读团队管理相关 5 本书籍 |
20% |
1.2% |
|
■□□□ |
5 |
1 |
0.2 |
0.0024 |
||
产出 3 篇相关文章 |
20% |
1.2% |
|
■□□□ |
3 |
1 |
0.333333333333333 |
0.004 |
||||
X |
60% |
3.6% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
提升专业技能 |
30% |
Scala 语言深入 |
30% |
学完 Coursera Scala 课程 4*4 |
50% |
4.5% |
|
■□□□ |
16 |
2 |
0.125 |
0.005625 |
读完「Akka Cookbook」 |
25% |
2.25% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
读完「深入理解 Scala」 |
25% |
2.25% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
分布式系统深入 |
20% |
学习 MIT 6.824 课程 |
60% |
3.6% |
|
□□□□ |
1 |
0 |
0 |
0 |
||
研究 TiDB,产出文章 |
20% |
1.2% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
写一篇 CAP 文章 |
20% |
1.2% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
计算机语言和基础 |
50% |
读完「Java Concurrency in Practice」/ 「七周七并发」 |
20% |
3% |
|
□□□□ |
2 |
0 |
0 |
0 |
||
读完架构思想 4 本书:架构漫谈 / 程序原本 / 大道至简 / 我的架构思想 |
10% |
1.5% |
|
□□□□ |
4 |
0 |
0 |
0 |
||||
读完 Go 语言 4 本书: Go 入门指南 / Go 语言编程 / Go in Action / build web application with golang |
10% |
1.5% |
|
□□□□ |
4 |
0 |
0 |
0 |
||||
读完「算法新解」「图解算法」 |
10% |
1.5% |
|
■■■□ |
2 |
1.5 |
0.75 |
0.01125 |
||||
LeetCode 刷题 200 题 |
20% |
3% |
|
■□□□ |
200 |
26 |
0.13 |
0.0039 |
||||
读完「SICP」 |
30% |
4.5% |
|
□□□□ |
1 |
0 |
0 |
0 |
||||
提升家庭生活质量 |
10% |
旅行 |
10% |
出国旅行 1 次 |
50% |
0.5% |
|
□□□□ |
1 |
0 |
0 |
0 |
出江浙沪旅行 2 次 |
50% |
0.5% |
|
□□□□ |
2 |
0 |
0 |
0 |
||||
父母 |
15% |
回父母家 10 次 |
100% |
1.5% |
V |
■■□□ |
10 |
6 |
0.6 |
0.009 |
||
小孩 |
10% |
生 1 个 |
100% |
1% |
|
■■■■ |
1 |
1 |
1 |
0.01 |
||
照顾夫人 |
40% |
每周做饭 2 次 |
100% |
4% |
V |
■□□□ |
96 |
29 |
0.302083333333333 |
0.0120833333333333 |
||
家庭事务 |
20% |
每个月整理家庭一次 |
100% |
2% |
V |
■□□□ |
12 |
3 |
0.25 |
0.005 |
||
车 |
5% |
买 1 辆车 |
100% |
0.5% |
|
■■■■ |
1 |
1 |
1 |
0.005 |
||
养成习惯 |
20% |
运动 |
60% |
全年跑步或游泳 36 次 |
20% |
2.4% |
V |
■□□□ |
36 |
2 |
0.0555555555555556 |
0.00133333333333333 |
高效率 |
个人时间 > 2h 一周 4 次 |
40% |
4.8% |
V |
■□□□ |
192 |
70 |
0.364583333333333 |
0.0175 |
|||
高效率 |
工作时间 > 6h 一周 3 次 |
40% |
4.8% |
V |
■■□□ |
144 |
89 |
0.618055555555556 |
0.0296666666666667 |
|||
读书 |
10% |
每月读书 4 本 |
100% |
2% |
V |
■■■□ |
48 |
37 |
0.770833333333333 |
0.0154166666666667 |
||
计划和复盘 |
30% |
全年做早晨计划 264 次 |
30% |
1.8% |
V |
■■■□ |
264 |
240 |
0.909090909090909 |
0.0163636363636364 |
||
全年做习惯追踪 365 次 |
30% |
1.8% |
V |
■■□□ |
365 |
244 |
0.668493150684932 |
0.0120328767123288 |
||||
每周做 Review |
40% |
2.4% |
V |
■□□□ |
48 |
23 |
0.479166666666667 |
0.0115 |
||||
提升个人 PR |
10% |
结实外部朋友 |
20% |
参会 4 次 |
30% |
0.6% |
V |
□□□□ |
4 |
0 |
0 |
0 |
面向 100+ 人的外部做 1 次分享 |
70% |
1.4% |
|
■■□□ |
1 |
0.5 |
0.5 |
0.007 |
||||
外部代码贡献 |
30% |
参与 1 个开源项目,提交核心作用代码 |
100% |
3% |
V |
■■■□ |
1 |
0.75 |
0.75 |
0.0225 |
||
提升写作能力 |
50% |
全年写作 12 篇 |
30% |
1.5% |
V |
■■□□ |
12 |
7 |
0.583333333333333 |
0.00875 |
||
参加开智的课程,作业提交 8w |
70% |
3.5% |
V |
■■■■ |
8 |
8 |
1 |
0.035 |
||||
|
100% |
|
|
|
|
100% |
|
|
|
|
|
|
Score for Q |
|
|
|
|
|
|
|
|
|
|
|
|
Score 期望 |
|
|
|
|
|
|
|
|
|
|
|
|
Score 推测全年 |
|
|
|
|
|
|
|
|
|
|
|
|
Score Total |
|
|
|
|
|
|
|
|
|
|
|
27.1694240348692% |
Direction |
D Rank |
Objectives |
O Rank |
Key Result |
KR Rank |
---|---|---|---|---|---|
习惯养成 |
20% |
运动 |
20% |
每周跑步或游泳 2 次 |
50% |
运动 |
体重 80kg -> 70 kg |
50% |
|||
高效率 |
50% |
工作日个人时间 > 1h 每周 3 次 |
10% |
||
高效率 |
工作日工作时间 > 8h 每周 4 次 |
50% |
|||
高效率 |
周末个人时间 > 6h 每周周 1 次 |
40% |
|||
读书 |
20% |
每月读书 4 本 |
100% |
||
计划和复盘 |
10% |
每周做早晨计划 6 次 |
30% |
||
每日做习惯追踪 |
30% |
||||
每周做 Review |
40% |
||||
基础技能 |
20% |
数学 |
30% |
看完「统计学习方法」 |
25% |
看完「什么是数学」 |
25% |
||||
看完「怎样解题」 |
25% |
||||
写一篇关于数学的文章 |
25% |
||||
英文 |
30% |
扇贝背 GAE 词汇(剩余 3600) |
30% |
||
懂你英语打卡两个月 30*2 |
30% |
||||
写 2 篇英文博客 |
20% |
||||
读完「语法俱乐部」并做完习题 |
20% |
||||
系统思考 |
20% |
笔记「认知科学」6 本书籍 |
100% |
||
团队管理 |
20% |
笔记「创业和管理」4 本书籍 |
70% |
||
写 2 篇相关文章 |
30% |
||||
专业技能 |
15% |
综合技能 |
40% |
关注 3 个会议(InfoQ、AS、?) |
40% |
云原生相关技术学习(待定) |
60% |
||||
语言和基础 |
60% |
笔记「Java Concurrency in Practice」 |
30% |
||
读完「七周七并发」 |
10% |
||||
笔记「架构思想」4 本书 |
30% |
||||
读完「自己动手写Java虚拟机」或者类似书 |
30% |
||||
面向未来技能 |
15% |
机器学习 |
60% |
学完 Coursera Machine Learning |
50% |
学习 Google Machine Learning Crash |
30% |
||||
学完「集体编程智慧」 |
20% |
||||
数据分析 |
40% |
笔记「深入浅出数据分析」 |
50% |
||
笔记「利用 Python 进行数据分析」 |
50% |
||||
影响力 |
5% |
写作 |
100% |
写作 12 篇 |
100% |
家庭 |
25% |
旅行 |
10% |
出国旅行 1 次 |
50% |
出沪旅行 2 次 |
50% |
||||
父母 |
30% |
回父母家 6 次 |
100% |
||
子女 |
60% |
每周陪伴小孩 1 天 |
60% |
||
制定并执行子女成长计划 |
20% |
||||
笔记 3 本教育书籍 |
20% |
||||
|
100% |
|
|
|
|
最常见的误区:
让我一一给你讲解吧。
DevOps 是字面上 Dev 开发 / Ops 运维两者组合, 严格意义上 DevOps 如下(via DevOps - Wikipedia):
DevOps(Development 和 Operations 的组合词)是一种重视“软件开发人员(Dev) ”和“IT 运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。
SRE 全称是 Site Reliability Engineering,最早是由 Google 提出,并且在其工程实践中发扬光大。 他们还出了一本同名书籍「Site Reliability Engineering」, 让这个理念在互联网工程师圈子里广泛传播。
Google 对 SRE 解释是(via Site Reliability Engineering - Wikipedia):
Site reliability engineering (SRE) is a discipline that incorporates aspects of software engineering and applies that to operations whose goals are to create ultra-scalable and highly reliable software systems.
我将其翻译翻译为中文:
网站稳定性工程师是致力于打造「高扩展、高可用系统」,并将其贯彻为原则的软件工程师。
从定义来看,DevOps 是文化、运动和惯例,而 SRE 是有严格任职要求的职位。 文化是软性定义,文化有更多概念可以捏造,而 SRE 定义精准,就少了想象空间(也可能 SRE 门槛高 😄)。 按 Google 给出的说法是,SRE 工程师实践了 DevOps 文化。这个观点没错,但是国内的 DevOps 逐步独立出 DevOps 工程师, 所以在本文,我着重讨论的是 DevOps 工程师和 SRE 工程师两种职位对比。
互联网需求催生了 DevOps 。在最传统软件企业中,是只有 Dev 没有 Ops, 那时 Ops 可能还是只是技术支持人员。开发按照瀑布流:需求分析、系统设计、开发、测试、交付、运行, 传统软件发布是一个重量级操作。一旦发布,Dev 几乎不再直接操作。 80 后可能会记得 QQ 每年都会有一个大版本发布吧,QQ 2000 / 2003 / 2004 等等。 此时 Ops 不用和 Dev 直接高频接触,甚至针对一些纯离线业务,压根没有设立 Ops 这个岗位。
互联网浪潮之后,软件由传统意义上桌面软件演变为面向网站、手机应用。 这时候业务核心逻辑,比如交易,社交行为都不在用户桌面完成,而是在服务器后端完成。 这给互联网企业给予了极大操作空间:随时可以改变业务逻辑,这促进了业务快速迭代变更。 但即便这样,Dev 和 Ops 是极其分裂的两个环节。Ops 不关心代码是如何运作的,Dev 不知道代码如何运行在服务器上。
当业界还沉浸在可以每周发布版本喜悦中时,2009 年,Flicker 提出了每天发布 10+ 次概念,大大震撼了业界。 Flicker 提出了几个核心理念:
原文 SlideShare 在这 10+ Deploys Per Day: Dev and Ops Cooperation at Flickr
真是让人难以想象,今天各种培训公司和一些知名大 V 在呼唤这些 DevOps 理念, 竟然在 2009 年一份幻灯片中就展现淋漓尽致。经典总是不过时,在尘封下闪耀着智慧光芒。 有些人将 DevOps 和运维自动化等同,这是只看到表象。 DevOps 目标是提高业务系统交付速度,并为之提供相关工具、制度和服务。 一些个人或培训机构添油加醋和衍生含义,都是围绕这 DevOps 本质而发散。
接下来聊聊 SRE 历史, SRE 出现要晚一些。在 2003 年时候 Google 的 Ben Treynor 招募了几个软件工程师,这个团队设立目的是帮助 Google 生产环境服务运行更稳定、健壮、可靠。 不同于中小型规模公司,Google 服务于十几亿用户服务,短暂服务不可用会带来致命后果。 因此 Google 走在了时代最前面,SRE 产生了。
这个职位为大规模集群服务,小型团队不需要这样职位设定(可能也招不起真正 SRE 😊)。 Google 在探索若干年之后,SRE 团队开始将自己心得体会写在线上,并在 2016 年将此书出版。
DevOps 文化,那么就没有一个具象职能要求。现在不少公司将 DevOps 职能单独抽取出来,称之为 DevOps 工程师。 那让我们看看 DevOps 工程师关心什么:DevOps 文化目的是提交交付速度, DevOps 工程师就自然会关心软件 / 服务的整个生命周期。
一个简单的公式:速度 = 总量 / 时间
,添上工程行业术语,即 交付速度 = ((功能特性 * 工程质量) / 交付时间) * 交付风险
。
功能特性交给产品经理和项目经理管理,DevOps 工程师需要关心剩下几个因素:工程质量 / 交付时间 / 交付风险。 DevOps 工程师职能如下:
SRE 关键词是「高扩展性」「高可用性」。高扩展性是指当服务用户数量暴增时, 应用系统以及支撑其服务(服务器资源、网络系统、数据库资源)可以在不调整系统结构,不强化机器本身性能 ,仅仅增加实例数量方式进行扩容。高可用性是指,应用架构中任何环节出现不可用时,比如应用服务、网关、数据库 等系统挂掉,整个系统可以在可预见时间内恢复并重新提供服务。当然,既然是「高」可用, 那么这个时间一般期望在分钟级别。SRE 职能可以概括为以下:
职责不同导致两个职位工作内容也不尽相同,我将 DevOps 工程师和 SRE 工程师职能列举如下:
很有趣的对比,DevOps 和 SRE 都会关心应用生命周期,特别是生命周期里面中变更和故障。 但是 DevOps 工作内容是主要为开发链路服务,一个 DevOps Team 通常会提供一串工具链, 这其中会包括:开发工具、版本管理工具、CI 持续交付工具、CD 持续发布工具、报警工具、故障处理。 而 SRE Team 则关注更为关注变更、故障、性能、容量相关问题,会涉及具体业务,产出工具链会有: 容量测量工具、Logging 日志工具、Tracing 调用链路跟踪工具、Metrics 性能度量工具、监控报警工具等。
DevOps 首先是一种文化,后期逐渐独立成一个职位;SRE 一开始就明确是一个职位; 不少同学把 DevOps 和 SRE 搞混,是被两者表象锁迷惑,看上去这两者都有的工具属性、自动化要求也相似。 甚至有一些开发同学把这类运维工作都统一理解为:服务器 + 工具 + 自动化。这是盲人摸象,管中窥豹。
从技能上来说,两者都需要较强的运维技能。 在职业发展天花板上,DevOps 可能缺乏 SRE 在一些专业领域的技能: 计算机体系结构能力;高吞吐高并发优化能力;可扩展系统设计能力;复杂系统设计能力;业务系统排查能力。 两者都需要软实力,但是 SRE 面临复杂度更高,挑战更大,要求也更高:
DevOps 具有普遍意义,现代互联网公司都需要 DevOps,但是并非所有团队对高可用性、高扩展性存在需求,它们不需要 SRE。 DevOps 工程师掌握相关技能之后,也有机会可以发展为 SRE 工程师。 而一位合格 SRE 工程师,在有选择情况下面,我相信不会去转型为 DevOps 工程师。
从专业背景来看,无论是 DevOps 还是 SRE 工程师,都需要研发背景,前者需要开发工具链,后者需要有较强架构设计经验。 如果有运维工程师想转型成为 DevOps 或者 SRE,那么需要补上相关技术知识。 毕竟,不是会搭建一套 Jenkins + Kubernetes 就可以自称为 DevOps / SRE 工程师。
怎么样,有没有解开这几个常见误区呢?希望你看到这里可以豁然开朗,最后附上两个工程师的技能点, 期望有志成为这两种工程师的同学,加油努力。
DevOps:
SRE: