阅读视图

发现新文章,点击刷新页面。

AI 和写作

Sam Altman 最近有一个关于 AI 和写作的访谈,让我开始思考 AI 辅助写作这个课题。

在辅助生产代码方面,Cursor 和 GitHub Copilot 已经证明了 AI 和人类在编程方面的协作非常有效。通过代码的上下文和注释,加上人类显示声明需求,AI 能很好地完成写代码任务。

我在思考在写作的时候,是否也能有一个 Copilot 辅助人类更好地写作。

大部分人不会进行深度写作,根据二八法则,80% 的人在消费 20% 的人生产的内容。这 80% 的人,偶尔进行浅度写作,例如写社交网络的动态、写用于工作中用于信息同步的文档。这类写作,我认为 AI 可以辅助的地方非常有限,用户基本不会主动为这样的场景寻找专门的写作工具或到 ChatGPT 这样的聊天窗口专门改写润色。这样的场景将来会被系统级的 AI (如 Apple Intelligent) 解决。

有的人希望写作,但不知道应该写什么。我曾经在书店看到过一本书,叫《642件可写的小事》,如标题所言,里面有 642 个开头,可以随便选一个续写。现在也有一些这样类型的 App. 这些书和 App 乍看起来很有意思,能解决不知道写什么的问题,但用起来我发现我根本不想写,因为我对里面的话题毫无兴趣。

我认为 AI 能很好地解决这个问题,因为我们可以在 prompt 中告诉 AI 自己的爱好和关注的领域,来定制化地生成自己会感兴趣的创意写作话题。

Notion 流行后,很多笔记工具开始标榜自己是「思维的工具」(Tools for thought). 其实写作这个行为本身就是「思维的工具」。Sam Altman 在访谈中说「写作是思考的外化」(writing is externalize thinking),而且「写作是模糊想法的放大器」。应该有不少人有同样的感觉,我们每天有很多想法和灵感,我们以为我们对这些想法非常清晰,但其实如果试着把想法写下来(或说出来),才发现很多思考在头脑内部其实处于非常混沌的状态。只有通过思考的外化(交流、写作),才会发现思维的漏洞、连结零散的思维。在修补思维漏洞的过程中,混沌的想法会逐渐变得健壮 (robust). 思考和灵感就像一颗种子,它非常迷人,但在播种前,它什么也不是。写作就是播种,认知是你的土壤,它会长出你意想不到的枝叶,然后又生长出新的枝叶,这就是为什么「写作是模糊想法的放大器」。

造成在头脑中的想法非常混沌的原因,是人脑无法承受过高的「认知负荷」 (John Sweller 提出的理论) 。借助书写可以把信息扩展到外部记忆,使得人在处理复杂信息时能更好地理解和思考。

很多人害怕写作,认为写作有门槛,但我不这么认为。我认为只要会思考,就会写作;只要能和人正常沟通,就会写作。写作只有把文字变成作品被品味被评价才是一种有门槛的艺术。我们受学校教育的影响,把写作完全视作一种文学艺术,才让人对写作感到害怕。却没有人教育我们,写作是一种思维的工具,写作可以帮助我们思考。我们其实可以只为自己写作,它是没有门槛的。把写作视作和自己对话,你就会发现写作也就是那么一回事儿。和自己对话需要遣詞造句吗?不需要。需要遵循什么文章结构吗?不需要。

把写作看作是思维的工具后,我对 AI 辅助写作有了全新的思考角度。从表面看,我们似乎需要的是辅助我写作的 Copilot, 但在本质上我们需要的是辅助思考的 Copilot.

现在很多文本编辑器都有 AI 功能:自动补全、自动总结、选中文本 Ask AI, 重写、润色等等。但我对这些功能在写作中的作用非常失望,真正在写作时,这些功能都是非常 annoying 的,而这不止我一个人有这样的感受:

这些功能,完全不能称作写作辅助,它们只不过是文本处理辅助罢了。它们对写作毫无用处,甚至有反作用。它们对思考也毫无用处,无法「放大」我的思考。

当写作作为一种思维工具时,我不需要 AI 辅助我自动补全,甚至有些可以生成一个段落的文字。这不是写作,这是在制造垃圾。

到底什么样的形式是一个好的写作 Copilot, 我也没有答案,但我认为它一定不能是侵入式的。它应该在一旁默默地观察我写出来的想法,然后在合适的时候告诉我它对此的观点。它有比我多得多的知识量,可以做到:

  • 在我提出一个观点时,找出其中可能的逻辑漏洞,帮助我更完整地思考。
  • 在我提出一个观点时,找出其在学术研究中对应的名词可以解释这个观点,甚至找出对应的科学实验、论文、现实中的案例。这在非虚构类写作中十分有帮助。我最近在读 Ali Abdaal 的 Feel good productivity 一书,里面十个观点有八个都能举出一个科学实验来论证,我基本可以单方面认为 Ali 一定是在用 GPT 来辅助他写这本书的。
  • 找出我在写作风格中的缺点,比如过于冗长,信息密度过低,句子难以理解等等。

也就是说,写作除了和自己对话以外,增加了和 AI 对话的一层。它可以提供灵感、帮助做 research, 写作风格指导等等的帮助。

虽然我希望有这么一个写作 Copilot 出现,但我觉得很难做出一个这样的 Copilot 出来。它的难点在于,不同类型的写作有着截然不同的需求,如果需要匹配不同的需求,最终还是会变成一个 Chat IM 的形态。上面提到的需求,其实直接使用对话也能解决,能进一步做的无非是在文本编辑器里加上选中文字展开对话,预设一些跟写作相关的 prompt 而已。

至于 AI 是否会扼杀写作,我不这么认为。Sam Altman 在访谈中也提到了这点:当你读到很好的作品,你会想去了解作者,这个作者经历的事情,他的思考方式,慢慢地和作者形成一个连结。这是 AI 做不到的。

「代码艺术家」不会被 AI 取代

最近大量地使用 Cursor 替代了 VS Code, 开始习惯直接在编辑器里告诉 AI 我的需求,让它来代替我写出代码段。

请注意,我用了「代码段」这个词,而不是「代码」,因为我想做一个区分 —— 按照我目前的经验来看,生成式 AI 非常擅长生成一段内聚的代码,而不是一整个应用程序。

在没有 AI 生成代码前,我写代码也是这样的一个流程:

  1. 思考整个应用的架构、模块
  2. 选择适合的技术栈
  3. 开始写代码,设计目录结构、抽象
  4. 真正开始实现实现

AI 也许能为第 1 和第 2 点提出建议,但我目前不需要。第 3 点我认为对于稍微复杂一点的生产级应用, AI 还做不到把这一块也做到。可能很多人看到现在 Claude 直接能写出一个全功能的 Todo List 就惊叹 AI 要取代程序员了,我觉得真正写过一个完整的给用户使用的「应用」的朋友对此都会很淡定。

对于我来说,只有在第 4 步(实现) 的阶段才真正能杠杆 AI 的能力。我会尽可能地描述清楚我的需求,让 AI 能理解我要做的任务,让它来生成满足我的抽象的代码,或修改现有的代码。

这里我提到的描述清楚我的需求是用 AI 生成代码中最重要的一点。所谓的「需求」不仅仅是描述这个函数需要做什么事情,还需要包含这个函数应该接收什么参数,返回的是什么数据结构。

例如,我在做的一个应用,其中我需要一个上传文件到 S3 的函数。在这个需求中,如果我单纯告诉 AI它要做什么,那我很有可能得到一个可以实现功能但不适合我调用的函数,因为 AI 没有上下文去确定我可以传哪些参数给它。

在深度和 AI 「结对编程」后,我对于「AI 是否能取代程序员」这个问题有了更深刻的思考。

有了 AI, 我现在写代码花的精力主要是在「设计」上,例如思考这个应用的交互设计,例如整个应用的架构设计。所谓的架构设计,一部分的工作是决定这个系统里要有什么模块,一部分的工作是决定这些模块如何串联在一起。而这些设计工作恰恰是我写代码的时候最喜欢做的,对我来说,写代码就应该是一个设计的过程,设计出优雅、易用、易扩展的接口是一件很有成就感的事。这也是我当初看 Head First Design Patter 这本书时的感受。
 如果写软件变成了一个只需要花精力在设计而不是实现上的过程,那么写软件的人就是「代码艺术家」了。我觉得「代码艺术家」是不会被 AI 取代的,因为设计的起点和终点都是人类,AI 可以给你 100 个设计上的答案,但只有人类最终能感知到现实和当下的环境和信息,创造出能触动另一群人类的产品。

如果你从现在开始,开始把 AI 当作是你的员工,就像某一天你突然只需要 $20 一个月就能招无数多愿意帮你打工的人,你很快就会发现,你最终会面临两种局面:

局面1:你将手足无措,你突然发现如果你不是实现函数的那个人,你就不知道你应该做什么了。从前你沾沾自喜的手写快排,手写红黑树突然变得一文不值,无处施展。

局面2:你将如虎添翼,你突然发现你曾经有很多想法没有精力和时间去实现,现在突然有这么多廉价劳动力将不厌其烦地帮你写代码,而你要做的只是设计好整个系统的结构,把具体实现外包给 AI. 然后把产品推出市场,去碰壁,去失败,去成功。显然,AI 不能替代你去碰壁,去失败,去成功,但真正让你变得强大的不是你手写快排有多烂熟于心,而是去碰壁之后学习到的东西

AI 不会替代「代码艺术家」,因为 AI 是「代码艺术家」的喷射机

读到这里,可能有人要说,Randy, 你飘了,你开始技术虚无主义了。在这里我要申明,这篇文章我是写给有一定经验的程序员看的。对于没有什么经验的程序员,多写点代码总是好的(至少目前来看)。AI 能力的上限是由用的人的上限决定的。无论是任何行业,充分掌握领域知识后配合 AI 才是最好的做法。

就像下面这个例子,我只要说一句 add tanstack query provider 就能让 AI 帮我把 @tanstack/query 加到我的程序里。我自己会写,但我自己写可能要花一两分钟,但 AI 一下子就好了。

但如果你没有任何代码经验,你连 tanstack query 是什么都不知道,也不知道要放在程序的哪个地方,那用 AI 还是有点困难。

写下这篇文章是因为最近用 Cursor 有感,加上刚好看到 Daniel Nguyen 发了一篇 Software is Art, 有感而发,不吐不快。在此粗浅翻译(非 GPT),作为结尾:

I realize the reason I like building is not just because I’m a builder.

我意识到我一直喜欢创造点东西的原因不只是因为我就是个创造者.

It’s because software products are how I express my creativity.

而是因为写软件产品是我表达我的创意的一种方式

It’s like a poem to a poet, a song to a songwriter, a painting to a painter…

就像诗人的诗,歌手的歌,画家的画

Software is my art form, my medium of expression.

软件是属于我的一种艺术形式,是我表达(创造力)的媒介。

不上班的第一年

2023 年的 5 月,我离开了微软,开始了自己做产品的旅程。到现在刚好满了一年,这一年发生了不少事情,有些在 2023 年终总结里提到过,这一篇我想更详细地列出在这一年我具体做了什么、对自由职业的思考、对做产品的一些思考等等。

首先是我的产出,这一年我的产出主要是两个产品、一个播客。

两个产品

Notepal

离职后第一个认真做的产品是 Notepal, 这是一个用于把微信读书笔记同步到各个笔记应用的插件。我在这篇文章详细写过这个产品的起源和在做这个产品的时候的一些思考。我在做这个产品的时候完全没有想到,这个只卖 50 块的插件,在这一年帮我付了我一整年的房租。

EpubKit

EpubKit 是另外一个我投入比较多的产品。这是一个把网页转成 ePub 电子书的工具。正式上线不到一个月,收获了几十个付费用户。

一个播客

刚好今年的 5 月份也是我和 GeekPlux 一起做的播客节目《代码之外》 的一周年。起初我们只是玩票性地做闲聊节目,后来因为有了听众来信的栏目,我们慢慢会讲更多职业发展方向的主题,这些主题意外地无形中对很多听众有不少的帮助。后来还邀请我刚工作就很欣赏的前辈勾股作为来信栏目的常驻嘉宾,为节目带来了很多很好的观点。今年在参加一些线下活动时,有些听众会跟我说从节目中得到了启发。从统计数字上看,我们好像的确是做了一件影响力不算小的事:

  • 小宇宙订阅数 6000+, 总播放量 11万+
  • Bilibili 粉丝 6000+,总播放量 13万+
  • YouTube 订阅数 2500+, 总播放量 3万+

在这一年,我对于做产品和生活都有了很多思考。对于不上班这件事,有人羡慕,有人好奇,刚好在这篇总结里,我想跟大家分享不上班的好处和缺点。

不上班的好处

时间自由

不上班的好处对我来说最明显的是时间自由。我是一个喜欢晚睡晚起的人,所以对我来说,准时上班是一件很难的事。不上班可以让我睡到自然醒,有充足的睡眠时间。

认识不同的人

上班的时候基本只能认识职场环境里面的同事朋友,或者在网络上交流的朋友。但是在自己做产品后,我开始认识一些同样是在做产品的朋友,和这些朋友可以聊得更深入。这些人往往对很多事情都有自己独特的思考。我从他们身上学习到非常多。我经常觉得如果我只是和以前一样在公司上班,业余写写博客和开源,我可能很难和这些朋友有共同话题,最多也只是互相点赞之交。

赤裸地直面市场

不上班后只能完全依靠自己赚钱,这代表我需要很赤裸地直面市场。

第一层赤裸,指的是脱离公司,作为一个个体,我可以给消费者提供什么他们认为值得为我付费的价值?

第二层赤裸,指的是一个产品如果脱离大公司本来就有的入口红利,我靠什么给我的产品带来自然流量?

这些都是很难的问题,但一旦开始学习,收获都是巨大的,而且都是属于自己的。在公司里最可怕的地方在于,我们很容易把公司给的平台误以为是自己的能力,很容易把流量看成是理所当然会有的。

也正因为如此,在这个过程中,我可以学习到很多以前不会主动学习的技能。

学习到更多技能

自己做产品的时候,没有公司平台给的入口,首先需要学习的就是如何做 marketing. 也是只有在自己做产品的时候,才会发现要别人发现你的产品是一件多么难的事。回想以前在阿里做业务,我们写一个营销活动页面,只要把入口放到某个栏位,基本不需要担心没有流量。

为了获客,我学会了研究 SEO, 学会了怎么做小红书,读了很多关于 marketing 的书。这让我学习了很多技术之外的知识,我觉得这些知识是终生受用的,而且它们不仅可以在互联网行业受用。

不上班的缺点

没有固定收入

不上班最大的缺点就是没有固定收入,这是很现实的问题。坦率地说,我以前买东西基本不看价格,只要觉得有价值,我就会买。没有固定收入之后,我买东西变得更加谨慎了。比如最近我很想买一台 Studio Display, 换作以前,无需多想,直接下单。但是现在,我会想我要卖多少份软件才能 cover 这台显示器,想想还是算了。

有时候我的朋友说羡慕我不用上班,我就跟他们说我这一年的收入还没有你们一个月赚得多,他们心理也就平衡了🤣。

孤独

可能有些人就是喜欢不用和别人交流,但对我来说,我是喜欢社交的,我在适当的社交中可以获取能量。不上班的时候,大部份的时间都是在家里写代码,看书,在网上和朋友聊天。有时候一整天都不需要开口说一句话。到后来我有点受不了,开始到外面的咖啡厅办公,只要能见到旁边有活人,就能有所缓解这种孤独感。

不确定性

上班有上班的不确定性要面对,不上班也有不上班的不确定性要面对。你无法确定这种生活可以维持多久,你的产品是否能卖得出去,就算卖得出去,它是否能养活自己,这些都是要面对的问题。

这些就是我体会到的不上班的一些优缺点,接下来,我想讲讲这一年在做产品的过程中我的一些思考和感受。

思考和感受

独立开发?

「独立开发」这个词在这一年非常火,用来标签像我这样自己做产品的开发者。其实我一直不称自己为「独立开发者」,因为我根本不想「独立」开发。我想做 scope 更大的事情,只是现在还没有条件。如果可以,我更愿意和两三个志同道合的人一起做产品。就像 61 的谜底科技,像少楠的 flomo.

而且自己做产品这一年,让我深刻地意识到,真正要做「独立」开发是很难的,因为没有人能擅长所有技能

没有人擅长所有技能

曾经我觉得只要我愿意去学,我就能做好。比如设计。我很注重产品的 UI/UX 设计,但我一直没有经过认真的设计学习和训练。我读了些关于 UI/UX 的书,以为就可以成为一个设计还不错的程序员。后来发现我错了,理论和实践之间原来有一条很大的鸿沟我虽然知道很多设计和用户体验的理论,但是一旦真正动手做页面,这些理论完全无法转换成实际的设计。

这是因为设计和写代码一样,是需要长期积累的。优秀的设计师可能有审美天赋,但他们一定也是每天都观察大量的设计,在自己的脑中内化了很多设计的模式(我不知道有没有专业的术语来形容),这些积累使他们可以面对一个新的需求的时候,根据自己内化的东西产生新的设计,这是我这种只读过一些理论的人无法做到的。正如会写代码的设计师,可能可以写一点能跑的代码,但缺乏多年的代码实践经验,是不可能像真正的专业程序员一样根据经验做好技术选型和想应该用什么设计模式(design pattern)的。

不过,我还是学会了在看到一些设计的时候,比以往更深入地去思考这个设计,这些元素为什么会这么摆放,颜色是怎么运用的,等等。

人的时间和精力是有限的

Notepal 和 EpubKit 同时做,让我更能体会到人的时间和精力是有限的。把时间放在一个产品 4 小时,那么另外一个产品投入的时间就永远少了 4 小时。而且上下文切换(context switch)是非常耗精力的。比如你正在修产品 A 的 bug, 这时用户报了产品 B 的 bug, 要从产品 A 跳转到产品 B 的开发,是很难一下子切换过来的。

所以不是自己一个人做产品就不需要项目管理,还是得学会充当自己的 manager 管理自己。给项目列好 roadmap, 排好需求优先级。

关于这个问题我还专门请教了图拉鼎,他告诉我要把自己当成不同的角色来用,比如规定上午作为客服,专门处理用户反馈,下午是程序员,专注写代码。这个方法也让我很受用。

自己做产品,同时要充当开发、marketing、客服、产品设计,我觉得这是「独立」开发的最大挑战。

学会专注

开发完 Notepal 后,我曾经陷入了很长时间的 burnout. 眼下赚的钱也不多,但又不知道应该做什么。期间有一些零星的 idea, 开发完后也不了了之。后来开发了第一版 EpubKit, 有了一些用户,之后又 burnout 了,因为开始觉得它做得不好,也很小众,渐渐又想放弃,想做点别的试试。

后来一些朋友「批评」我说我不够专注,我也开始反思,其实自己做的东西不算很糟糕,只是自己太着急,没有把它们都做得极致就失去耐性。经过反思,我决定好好打磨自己现有的产品,才有了现在这一版自己比较满意的 EpubKit.

乔布斯说专注不是指只做最好的,而是对其它也很好的东西 say no.

另外,缺乏专注很有可能会消耗用户对你的信任。

警惕「快速试错」

「快速试错」、「快速验证想法」是很多人做产品的信条,我相信快速验证想法是重要的,但是更重要的是交给用户的这个产品不应该是一个半成品,它最好是一个 Finished software. 也就是说,即使你不再维护这个产品,它还依旧是个可用的软件。

我曾经也「快速试错」,发布了一些产品,最终不再维护,甚至还有几个用户付费了。我对这种「试错」是愧疚的,因为这很有可能伤害支持你的用户。因此我现在反而发布新产品会更加谨慎,要把用户的感受放在第一位。

这篇文章很好地表达了这个观点。

总结

以上就是不上班的一年我对自己的一些思考的总结,希望对读到的朋友有帮助。这一年我其实算是比较幸运的,虽然收入微薄,但做产品也算是能卖出去,能解决很多人的需求。但同时我还是处于迷茫的阶段,认为自己做的产品还是小打小闹,scope 太小。我还是希望将来能做出满足更大众需求的产品,覆盖面能更广。

我的这些观点不一定对,但也能让读者感受到一个程序员脱离公司的其中一种可能性。

在 Electron 中使用 SQLite 的最好方式

上周刚刚发布了一个用 Electron 的应用 EpubKit. EpubKit 是一个把网页制作成电子书的工具。在 EpubKit 里,我需要一个数据库来存储内容,最好的选择是 SQLite.

但是,由于 Electron 有 renderer 和 main 区分开的机制,所以在 Electron 中使用 SQLite 会非常麻烦 —— SQLite 的执行要在 Main process, 但调用要在 Renderer process. 在 Electron 里,Renderer 和 Main process 之间的通信是通过 IPC (Inter-Process Communication) 实现的。也就是说,我可能需要把每一个有关数据库操作的业务逻辑单独写成一个 IPC 通信的事件,然后在 Renderer 里调用这些事件。

我想要做到的是,我在 Renderer process 中直接调用 ORM, 但实际的执行是在 Main process 中。这样一来我就不需要单独地写很多个 IPC 事件了。

例如:

万幸的是 drizzle 居然有一个 HTTP Proxy 的机制。这个机制能让你所有的 ORM 操作都流到一个地方,在这个地方你能拿到最终生成的 sql 语句,然后你可以自己决定怎么执行这个 sql 语句。

也就是说,我可以在这个 proxy 里,把 sql 语句通过 IPC 发送到 Main process, 然后在 Main process 里执行这个 sql 语句。

接下来我会简单描述一下我在 EpubKit 里是怎么做的。

编写 Schema

在你的项目里找一个地方,把 drizzle schema 写下来:

import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";

export const posts = sqliteTable('posts', {
  id: int("id").primaryKey().default(0),
  title: text("title").notNull().default(""),
})

在 Renderer 里创建一个 drizzle database 实例

根据 文档, 在创建 drizzle db 实例的时候,可以传入一个函数,这就是 proxy 的本体。我们要做的是在这个 proxy 里,拿到 ORM 最终生成的 sql 语句、执行方法、变量,然后通过 IPC 发送到 Main process.

export const database = drizzle(async (...args) => {
  try {
    // 通过 IPC 把 SQL 发送到 Main process
    const result = await window.api.execute(...args)
    return {rows: result}
  } catch (e: any) {
    console.error('Error from sqlite proxy server: ', e.response.data)
    return { rows: [] }
  }
}, {
  schema: schema
})

这里有一个 window.api.execute(), 是怎么来的呢?其实是在 preload 进程里面定义然后暴露的, 它的作用就是通过 IPC 发送 sql 语句到 Main process:

// preload.ts

const api = {
  execute: (...args) => ipcRenderer.invoke('db:execute', ...args),
}

也就是说,实际上我们以上做的事情就是,通过 proxy, 把 SQL 语句通过 Main process 里的 db:execute handler 最终执行。

Main process

在 Main process, 我们创建一个 IPC handler:

// main.ts
ipcMain.handle('db:execute', execute)

这里的 execute 就是 Main process 里最终执行 SQL 语句的函数。

import { drizzle } from 'drizzle-orm/better-sqlite3'
import Database from 'better-sqlite3'
import { migrate } from 'drizzle-orm/better-sqlite3/migrator'
import * as schema from '../renderer/src/db/schema'
import fs from 'fs'
import { app } from 'electron'
import path from 'path'

// 初始化 sqlite
const dbPath = '../databse.db'

fs.mkdirSync(path.dirname(dbPath), { recursive: true })

const sqlite = new Database(
  dbPath
)

// 创建 drizzle 实例
export const db = drizzle(sqlite, { schema })

// 这里是 execute 方法
export const execute = async (e, sqlstr, params, method) => {
  // 得到执行需要的参数后,用 better-sqlite3 执行
  const result = sqlite.prepare(sqlstr)
  const ret = result[method](...params)
  return toDrizzleResult(ret)
}

function toDrizzleResult(row: Record<string, any>)
function toDrizzleResult(rows: Record<string, any> | Array<Record<string, any>>) {
   if (!rows) {
    return []
  }
  if (Array.isArray(rows)) {
    return rows.map((row) => {
      return Object.keys(row).map((key) => row[key])
    })
  } else {
    return Object.keys(rows).map((key) => rows[key])
  }
}

在上面的代码中,我额外实现了一个 toDrizzleResult 的方法,是为了把 better-sqlite3 的返回值按照 drizzle 需要的结构返回。

到这里,你就已经可以在 Renderer process 里直接用 drizzle 了:

function App(): JSX.Element {

  const [postList, setPosts] = useState([] as any[])

  useEffect(() => {
    database.query.posts.findMany().then(result => {
      setPosts(result)
    })
  }, [])

  return (
    <div>
      <div>
        <form onSubmit={async e => {
          e.preventDefault()

          const formData = new FormData(e.target as HTMLFormElement)
          const title = formData.get('title') as string
          if (title) {
            await database.insert(posts).values({
              id: Math.floor(Math.random() * 1000),
              title
            })

            // refetch
            const result = await database.query.posts.findMany()
            setPosts(result)
          }
        }}>
          <input name="title" type="text" placeholder="title" />
          <button>add</button>
        </form>
      </div>
      {postList.map(post => {
        return (
          <div key={post.id}>
            {post.title}
          </div>
        )
      })}
    </div>
  )
}

export default App

但这时候执行,会报错。原因是我们还没有初始化数据库。我们需要在 Main process 里初始化数据库。

首先需要用 drizzle-kit 生成 migration 文件。在 drizzle.config.ts 中指定了 migration 文件的地址:

// drizzle.config.ts
import type { Config } from 'drizzle-kit'

export default {
  schema: './src/db/schema.ts',
  out: './drizzle',
  driver: 'better-sqlite'
} satisfies Config

然后写一个 runMigrations 方法,用来初始化数据库:

export const runMigrate = async () => {
  migrate(db, {
    // 在 drizzle.config.ts 里指定的路径
    migrationsFolder: path.join(__dirname, '../../drizzle')
  })
}

这个方法需要在 Main process 启动时执行的:

async function createWindow() {
  // ...

  await runMigrate()
  createWindow()

  //...
}

实例源码

你可以在 这里 找到这个示例的完整源码。

特别感谢 EGOIST 提供灵感。

读 React 18 文档有感

昨天 Sixian 提到了 React 的新版官方文档,之前一直没有去读,今天抽空读了其中 Escape Hatches 的部分,有一些收获,也有一点感想,在这里简单分享一下。

声明:我经常使用 React, 但我不是 React 的专家,我也不再是一个对技术会进行非常深入探究的人了。所以对于 React 最新的一些 API, 可能对某些人来说是老生常谈,但对我来说是新鲜的。所以本文只是简单分享一下我看到的之前不知道的一些 API.

跑两次的 useEffect

开发环境 useEffect 跑两次是故意的,是为了帮助开发者在开发环境中发现你是否正确地给你的 effect 做了 teardown.

The right question isn’t “how to run an Effect once”, but “how to fix my Effect so that it works after remounting”.

如果你的代码因为 useEffect 跑两次所以出问题,很可能你用错了 useEffect.

讨论这样的特性是否是一种傲慢对我来说意义不大,重要的是我理解了其动机。

flushSync API

React 有一个 flushSync API, 可以强制 update DOM. 在以前我们一般是把希望在 DOM 更新后才执行的代码放在一个 setTimeout 里.

如果想重置一个 Component, 直接给它一个新的 key

如果希望某个 component 的 props 被改变的时候做一些重置,应该直接给这个 component 赋一个新的 key, 使 React 直接重建整个 DOM, 而不是用 useEffect 手动做重置工作。

其实以前我也经常这么干,比如 Modal 在关闭和打开后重置里面 Form 的值,我会直接给 Modal 一个新的 key. 一直不知道这是否是一个 best practice, 现在得到了官方认证。

useSyncExternalStore

这是一个我不知道的 Hook, 看起来很适合用于把别的库移植到 React 时使用,而且它对 SSR 非常友好。

一些感想

很多人觉得 React 繁琐,心智负担特别大,我也这么认为。但我一直觉得,这不是 React 本身的问题,而是 JavaScript 的问题。

React 是一个特别函数式编程思维的框架,但很可惜,JavaScript 只是一个半吊子的函数式编程语言,它只是在被设计的时候学了一点 Lisp 的皮毛,也提供了一点点函数式的 API, 但它没有很多真正的函数式编程语言应该有的基本特性,才导致了我们要写额外的代码来解决一些问题。

例如,Memoization 在一些函数式编程语言里是标配,但 JavaScript 里没有,所以你需要给函数手动套一层 React.useCallback.

函数式编程语言里, Immutable 也是标配,而在 JavaScript 里,需要用 Immer 之类的第三方库。

所以这就是为什么在多年前我很看好 ReasonML (现在叫 ReScript) 这个语言,因为它本身就是真正的函数式编程语言(它是基于 OCaml 的 JavaScript 方言),你会发现,用它来写 React 是多么舒服的一件事,因为语言本身就提供了你在 JavaScript 里需要调 API 才能实现的功能。有趣的是,React 在刚开始设计的时候用的就是 OCaml.

成也 JavaScript, 败也 JavaScript.

读《岩田先生:任天堂传奇社长如是说》

虽然我有 Switch 游戏机, 但我不是游戏的发烧友,对任天堂及其社长岩田聪更不甚了解。前些天听《半拿铁》的《任天堂往事》系列,对任天堂的发展有了一些了解,也听闻岩田聪同时是一个非常出色的程序员,所以开始对岩田聪产生了一些兴趣,于是找到了这本收集了岩田聪说过的话的书,希望能一睹这位天才程序员对管理企业和写程序有哪些独特的见解。

这本书不长,读完后我对岩田聪确实有了更多了解。首先是从他身上学到了一些管理企业的方法。

岩田聪的管理风格非常务实和「接地气」,他讲到他喜欢和员工一对一面谈:

我在与全体员工谈话的过程中,发现了许多“经过面谈才头一回意识到的事情”。就算对方是一向保持着沟通的人,也有在一对一的场合才讲得出口的话。这样说或许不太恰当,但我重新认识到:“假如不制造推心置腹的机会,人是不会敞开心扉的。”

在书的最后有宫本茂和糸井重里谈岩田先生的追忆文章,其中糸井重里回忆说:

虽然是第一次见面,但他的话语让人觉得值得信赖。岩田先生曾回忆说:“第一次见面时我也相当紧张。”但我一点也没看出来。“在已有基础上修改,还是从头做起?”这句话单从字面上看会让人觉得不够谦逊,但岩田先生完全没有居高临下的姿态,而是传递出十分看重对方自由选择的权利的感觉。怎么说呢,原本是我们请他来帮忙,但在技术问题之外,岩田先生工作方式的魅力也令我们深受感触,见面次数越多就越发信赖他。

结合我从前被管理的感受来看,这让我意识到,管理者能给被管理者足够的信任感是非常重要的,而信任感可以来自于谦逊和务实。

岩田聪还对公司的会议规定必须有一位「会议统筹」:

所谓“会议统筹”,就是让会议良性运转的人。如果会议中缺少创意,就负责添加创意;如果创意太多,导致过于发散,就负责归纳总结。简言之,就是会议的指挥。无论什么会议,都必须有一个决心“要在会议上得出答案”的统筹者。

我们在厂里工作过的都知道,很多时候会参加一些没有结论的无用会议,如果每次会议都能指派一个这样的「会议统筹」,相信大家的效率会提高很多。

从岩田聪的谈话中,我感觉岩田聪是一个非常能够洞悉到他人内心感受的人,所以在推进项目的时候,也会非常照顾同事的感受:

世界上许多改革都是运用否定现状的逻辑推行,但这样的做法会让很多人陷入不愉快。因为当下的现状正是许多人的心血、诚意、热情构成的。如果现状由不诚实的东西构成,去否定也无妨,但由诚实的产出构建的现状,不应该被否定。

也许正是这样的性格,才能让岩田聪清楚洞悉玩家的感受,从而做出改变世界的游戏。同时,也是因为他是真正有「给别人带来快乐」的强大 passion:

岩田先生发自内心地喜欢看到大家的笑容。他也将这个目标列入任天堂的经营理念。的确,他是一个希望给世界创造更多快乐的人。并且,他是一个为了实现这个目标不惜舍身奉献的人。他喜欢帮助他人,喜欢钻研各种事物,也喜欢为此而沟通交流的过程。所以说,每周一与宫本先生一起吃午饭的时间,凝聚着岩田先生所喜爱的一切。因为他们一起讨论着游戏创意,说着“我懂了”,试图给自己与玩家都带来笑容。

我一直觉得,能成大事的人,都是那些内心有一种很强大的 passion 的人。我虽然不是资深玩家,很难从游戏中感觉到快乐,但从岩田先生的谈话中,我还是为他的这种 passion 所感动。

如果你本身是一个玩家,可能会对这本书有更深刻的理解。

最后,用岩田先生的话结束这篇读后感:

在我的名片上,我是一名社长。在我的头脑中,我是一名游戏开发者。但在我心里,我是一个玩家。

On my business card,I am a corporate president.In my mind,I am a game developer.But in my heart,I am a gamer.

名刺のうえでは、わたしは社長です。頭のなかでは、わたしはゲーム開発者。しかし、こころのなかでは、わたしはゲーマーです。

2014 年终总结

2014 年因为有高考所以比往年过得快了些,大学的一个学期转眼也过了。有时候甚至缓不过神来,甚至让我常常有种错觉,2014 它早就过了,但 2015 它迟迟未来。

今年的 1 月我还在为化学烦恼,今年的 12 月现在的我已经过上了那时梦寐以求的自由的生活。但是生活哪有什么所谓的自由,高考只不过给我开了一个闸,从一个小笼子,滚进了一个更大的笼子而已。而这个更大的笼子比以前按部就班的生活更让我感到害怕,就像《海上钢琴师》里 1900 放弃离开轮船后 对 Max 说的一样,让我害怕的不是我看到的东西,而是我所无法看到的东西,这里什么都有,可是唯独没有尽头。

还好上了大学以后,能遇到几个让我又有了寄托的朋友,我和他们一起做事,非常地快乐。

技术方面,哈哈,就不说了,我仍然迫切的希望能用技术给这个社会带来一点好的改变。

遗憾的事是好像很难再找到能聊聊天的朋友,而且好像变得不太爱说话了,所以有时候我还是挺怀念高一时候的我。最遗憾的事是 Google 还没有解封,当我以为『她』正在慢慢变好的时候,却再一次让我失望了。

新年愿望是,1,所有人都好 2,能继续写代码 3,生活的这片土地能再进步一些,我知道这的确需要一个过程,但我想自己,和我身边的人,还有未来的儿子和女儿,都能在这里活得快乐些。

最后附上 1900 对 Max 说的那段话,说的真好:

All that city. You just couldn’t see the end to it. The end? Please? You please just show me where it ends? It was all very fine on that gangway. And I was grand too, in my overcoat. I cut quite a figure. And I was getting off. Guaranteed. There was no problem. It wasn’t what I saw that stopped me, Max. It was what I didn’t see. You understand that? What I didn’t see. In all that sprawling city there was everything except an end. There was no end. What I did not see was where the whole thing came to an end. The end of the world…

2016 年终总结

博客篇

开篇讲讲这个博客这一年的「成绩」:

访问量

因为今年年中进行过博客迁移,从 github pages 迁到了阿里云,还换了整个 blog platform. 所以新旧博客的统计要结合起来看。

旧博客统计:

新博客统计:

总计: PV 49,406,UV 15,800

访问设备

iPhone 用户仍然排第一,第二是小米。

来源浏览器

文章排行

上半年一个 Vue 和 Webpack 系列访问量比较大。下半年给自己的博客定位不是单纯的技术博客,访问量比较大的是关于退学的两篇文章。

坚持做独立博客很难,坚持写博客更难。我没有开公众号,因为开公众号会让我感到有压力,更会让我「为了推送而写文章」。也是因为赞同陈皓的观点

技术篇

今年因为工作原因,把大部分的精力花在了 React 上,Vue 反倒没什么机会写了。写 React 的过程中对 Thinking in React 有了不同的看法。我从前不喜欢 JSX, 不喜欢 setState, 但是慢慢地开始思考 Functional Programming, Reactive Programming. 后来发现,React 是不是 React 已经不重要了,因为 React 只是实现它思想的手段,更重要的是在其背后的,UI Development 的观念 —— (state) => View。无论如何争论 Functional Programming,它的的确确改变了我对程序开发的想法,我开始追求 Pure Function,开始讲究函数的 side-effect. 我想下一年我还会对 FRP 做更深入的研究。关于这方面的感悟,我会单独写成文章。

比较快乐的是在一些项目里使用了 TypeScript. 自己也写了文章,录了视频, 来表达我对这个语言的看法。

这一年参加了两个 Talk, 一个是珠三角技术沙龙,讲的是 Vue 和 Native. 另一个是 Node 地下铁,讲了 TypeScript. 关于技术分享,我在知乎有一篇回答 讲了我对国内技术分享会议的看法。希望我自己在接下来的一年能有更大的长进,然后用自己的行动去告诉大家技术分享应该怎么做。

比较遗憾的是工作之后减少了写开源项目的时间,今年对开源社区最大的贡献就只有给一本TypeScript 书 贡献了些内容。

工作篇

从 3 月份入职,完成了一个项目的重构,帮助了一些还有些迷茫的朋友,用自己的热情感染了团队的技术氛围,是在这一年工作上让我自己感到满足的事情。

读书篇

Just for Fun: The Story of an Accidental Revolutionary

Linux 创始人 Linus 的自传,记录了 Linus 的少年时期和 Linux 的诞生,之中还夹杂一些对开放源代码的观念。读完以后很受鼓舞,能像 Linus 一样是我做软件开发的终极目标。

Soft Skills: The software developer’s life manual

中文名叫《软技能:代码之外的生存指南》。我很不喜欢这类教别人做人的书,但是受人推荐,还是读完了。

书里有几个章节我印象比较深刻。比如谈到大公司、中等规模公司、创业公司之前的区别:

在大公司工作令人沮丧,因为他们感到他们个人的贡献无足轻重

为大公司工作的一个显而易见的事情就是成长机会

结合自身条件和自己喜欢的工作环境进行职业选择,是需要深思熟虑的事情。

书中还谈了个人品牌的打造,学历问题等等。都值得一读。

The Art of UNIX Programming

中文名是《UNIX 编程艺术》。万幸我用 macOS 也算是 UNIX 环境的重度用户,所以在读这本书的时候不会感到吃力。这本书实际上和 UNIX 源代码没有什么关系,讲的是 UNIX 下的程序(比如 grep),这些程序的设计哲学让我对软件有了新的思考。

编写复杂软件又不至于把自己搞混乱的方法是降低软件整体的复杂度。软件本身的复杂度不会因为实现方式和代码组织的优秀而降低,但是这能使整体复杂度降低。降低整体复杂度的方法是用清晰的接口把复杂的软件分解成若干个简单的模块

每把剃刀都自有其哲学,更何况是软件开发呢。即使是开发一个小函数,它的输入和输出也是需要讲究的。

Becomming Steve Jobs

我没有读《乔布斯传》,而是选了这本《成为乔布斯》,是听说这本传记记录的乔布斯要更真实。事实上通过很多途径都已经宏观上对乔布斯有了很大程度上的了解,所以读传记的时候已经没有对某些事件产生触动。反而触动我的是一些小细节上,比如书中提到乔布斯父亲的话:

对于一个橱柜来说,别人看不到的底面与表面的抛光一样重要;对于一辆雪佛兰汽车来说,别人看不到的刹车片和汽车的油漆一样重要。

容忍与自由

我求学时期读了很多民国作家的书,唯独胡适先生的书读得不多。今年读了这本文集,意犹未尽,还想读他的《中国的哲学》,但看来要等下一年了。

Elon Musk

硅谷「钢铁侠」Elon Musk 的传记。十分羡慕财富自由又有想法的人。

A brief history of humankind

推荐这本有趣的《人类简史》,看人类是如何从原始人进化过来的。

游戏篇

今年买了 PS4, 人生第一台游戏机。玩了几款大作,《最后的生还者》、《神秘海域》、《GTA5》、《看门狗》。印象较深的是《最后的生还者》,没有 PS4 的也建议视频通关。

下半年换了 Macbook Pro 也入了 Steam 的坑,Steam 的游戏和 PS4 比简直就是白菜价。沉迷了一段时间 Don’t Starve. 通关了 Firewatch.

自我总结

这一年的成长比较横向,不仅仅在技术,还学习了音乐,健身等等。遗憾是改不掉一直以来喜欢「急」的缺点,急于完成事情,不多加考虑。甚至容易使自己在快要完成一件事情的时候,容易由于太急,最后烂尾,比如

2017 年终总结

Preface

我的 2017 过得很平淡,可能是因为出来工作已经是第二年了。

今年比较特别的是身边的朋友都大四了,参加了一些毕业拍照。大家都陆陆续续地开始找工作,面试。偶尔在朋友圈看到朋友拿到 offer, 也会替他们感到开心。

有很多朋友开始请教我一些出来工作的经验,面试的技巧、offer 怎么选择、租房的经验等等。被问到这些问题的时候我也会很开心。因为以前上学的时候,学业成绩不好,我在班上除了搞搞笑,对身边的同学来说并没有多大的「用处」。

4 月份的时候上台拿了 16 年的优秀新人奖,但是 17 年整一年没有做出很出色的成绩。慢慢失落地发现在很多事情上,技术并不是那么重要,它只不过是一种手段而已。

在年底,和 @EGOIST 一起创办了 StickerOverflow. 成功地让国内的开发者能买到高质量的技术贴纸。

今年想得比较多的是应该如何用自己的能力去帮助更多的人。想过去做培训,想过回大学给以前班的人开个交流会,想过写书。最后都搁置了,因为我后来发现,「助人」有时是一件很一厢情愿的事。

除了技术外,这一年有很多新的尝试,上台唱歌纪念张国荣,参加了唱歌的比赛,做了一期音乐电台节目。在新的一年,还想尝试去经营一个音乐博客,让更多人发现被忽略的优秀粤语流行音乐和歌手。

博客成绩

今年博客的 PV 和 UV 都小幅度降低了,原因是今年下半年文章写得很少。因为花了不少时间在比写文章更有趣的事情上。

博客没有广告,但还是会有人因为读文章而受到启发给我赞赏,今年一共收到 ¥136 的赞赏。更开心的是收到了一些真诚的交流邮件。

PV/UV

PV: 33,247 (去年 49,406), UV: 12,172 (去年 15,800)

操作系统

来源

用户访问的时段

设备

消费

电子产品

iPhone 8

把很喜欢的 iPhone SE 退役了,因为跑 iOS11 耗电快,性能不够。

SONY 黑卡 1

为了方便写数码产品评测,入手了黑卡 1 代。

DS216j/DS218+

上半年用 DS216j,因为性能满足不了,年底换成了 DS218+. 关于 NAS 的使用,可以参考这篇文章.

Yamaha P115 / Yamaha NP12

因为要学习 Keyboard,入手了 Yamaha P115 电钢来入门。P115 是全配重的,手感很好,但是重量大,不便携。为了可以携带出去户外表演,于是又入了轻一半重量的 61 键的 NP12。

Nintendo Switch

买了 Switch, 圆了掌机梦,玩了《塞尔达传说》和《奥德赛》。因为便携,吃灰率要比 PS4 低。

DJI Spark

新年计划

2018 年的计划是多写写代码,多玩玩音乐,读更多书,换一间大些的房子,买一辆车。

有次和朋友聊天,她说,你知道自己想要做什么,又能把它做到,还能靠它养活自己。你千万不能变得世俗啊,如果你也变得世俗,我会觉得这个世界,真的就是那样了。

2018 香港流行音乐推介

「香港乐坛已死」已经不是什么新鲜的论调,这句话也经常被用于伪装自己对当代流行音乐颇有研究。作为一个港乐爱好者,我对此一直持相反的意见。港乐没死,但它的确从大众变成小众了。

流行文化产品是时代气息的显影」,显然,当代粤语片区大多数人们变得不再需要粤语流行歌曲,他们比以前有更多选择,例如综艺、韩国流行文化等等。当然也和当代人接触信息的媒介有很大的关系,毕竟如果消费者可以满足于连「俗气芭乐」都不如的《学猫叫》,那么又何必费钱费心思去做更具音乐性的音乐作品呢。

当粤语片区的年轻一代人「抛弃」了电视机,加上国内串流音乐服务的版权问题,粤语流行文化在国内就很难传播进来了。如果不是主动接触它,关注它,粤语流行歌曲几乎没有机会进入你的耳朵。唯一的机会是香港艺人参加国内的音乐类综艺节目,但为了照顾观众,唱起了国语,发行的也是国语唱片。

不发国语唱片的新生代香港音乐人自然更难被发现,但港乐从旋律、编曲、填词都不输中国大陆(以及台湾地区)的流行音乐。下面是一个歌单,这些歌是我心目中 2018 年发行的粤语歌曲中最好的。听完这些歌,你就可以大概清楚目前港乐的发展状况。它是真的不如其它的流行音乐吗?

(如果你用 PC 访问这篇文章,在这里会有一个 Spotify 播放器,播放这一整个歌单)

《心之科学》 容祖儿 作曲:Howie@DearJane & 林家謙 / 填词:黃偉文

《小问题》 AGA 作曲:AGA / 填词:陳詠謙

《未来见》 RubberBand 作曲:RubberBand / 填词:Tim Lui

《恐怖情人》 许廷铿 作曲:雷頌德 / 填词:林夕

《无期》 AGA 作曲:AGA / 填词:林夕

《哪儿》 小尘埃

《仙乐处处飘》 小尘埃

《挥挥手》 JW 作曲:Eye Fung / 填词:陳詠謙

《荣辱战争》 林奕匡 作曲:林奕匡 / 填词:陳詠謙

《人妻的伪术》 谢安琪 作曲:雷頌德 / 填词:林夕

《睡前服》 小肥 作曲:小肥 / 填词:Tim Lui

《天才儿童 1985》 张敬轩 作曲:伍樂城 / 填词:黃偉文

《重阳》 邓小巧 作曲:林家謙、謝國維 / 填词:黃偉文

《百年树木》 张敬轩 作曲:伍卓賢 / 填词:林若寧

《风尘三侠》 小肥/侧田/6号@RubberBand 作曲:李偉@RubberBand / 填词:林寶

《如何从夏天活过来》 黄妍 作曲:黄妍 / 填词:黄妍

2018 年终总结

大人热爱数字。如果你跟他们说你认识了新朋友,他们从来不会问你重要的事情。他们从来不会说:“他的声音听起来怎么样?他最喜欢什么游戏?他收集蝴蝶吗?”

他们会问:“他多少岁?有多少个兄弟?他有多重?他父亲赚多少钱?”只有这样他们才会觉得他们了解了他。

如果你对大人说:“我看到一座漂亮红砖房,窗台上摆着几盆天竺葵,屋顶有许多鸽子……”那他们想象不出这座房子是什么样子的。你必须说:“我看到一座价值十万法郎的房子。”他们就会惊叫:“哇,多漂亮的房子啊!”

—— 《小王子》

2018 对我自己来说,是灰心的一年。这一年除了和往年一样的焦虑以外,也没有做成什么我认为了不起的事。很大的原因是我对技术失去了信心。

小时候,我最大的理想是「用技术改变世界」,但我早在我的 2017 年终总结 里提到:「技术并不是那么重要,它只不过是一种手段而已」。今年我更加确信这一点,我们做技术的人,永远只能间接地改变世界。一个可以改变人们生活方式的产品,技术虽是不可缺少的部份,但也不是起决定性作用的部分。

于我而言这是灾难性的信仰崩塌。

有时夜里我也会反思,是不是对自己的要求太高了,又或者是,我太想向别人证明自己,害怕自己变得平庸。而在今年,我意识到自己真的变得平庸了,所以我把他称为灰心的一年。

而今年最主要的心态变化,则是随着年龄的增长和财富的积累,我不免也会想到未来的现实生活。婚姻、家庭、置业,等等。我觉得这些东西不能带给我快乐,它们不是我想要的。但是为什么每一个人都要「善意」地提醒我,你应该这样,应该那样呢。我不买房,便错么?成年人的眼里,为什么都只有所谓的「保值」、「升值」呢。

我们总说大人们不关心孩子们快不快乐,只关心孩子们的成绩。原来大人们也不关心大人们快不快乐啊。

工作

今年除了支撑了几个业务的后台管理系统以外,主要在思考的东西是如何利用 GraphQL 帮助开发者更轻松地应对中后台管理系统的开发。

在内部做了很多基建以后我有一个感悟,我们做基建的初衷是提高开发者的效率,但是实际上,影响开发者效率的因素,很大程度是开发者本身。基建能做的很有限。举例来说,Redux 这么一个「简单」的库,却有很多人用不好。在项目里,很多可以简单实现的地方,由于编程水平、经验、对库/框架的了解程度等等条件的不足,开发者就把简单的问题复杂地解决了。

即使给人一台最好的单反,也不一定拍得出最好的照片——因为重点还是镜头背后的那颗脑袋。

所以在新的一年,我的目标是多输出一些理论层面的东西。

消费

2018 年依然是没有节制地消费,不过买的东西已经不多了(该买的都买过了)

  • 把 Yamaha 的电子琴换成了 Korg Kross1
  • 搬到了一个 43 平米的一房一厅
  • 买了 TASCAM 录音笔
  • 买了 Scarlett 2i2 音频介面 (用来录些 cover)

(不得不提欧德堡全脂牛奶是大概一周一箱…)

输出

今年尝试性地做了一集技术的短视频内容:《解读 The State of JavaScript 2018》

生活

2018 年我做了决定,我的生活不再是 80% 的 coding time 了,我把更多的时间,花在了别的地方——音乐、健身、读书。这是一个巨大的变化,因为在以前,代码几乎是我生活的全部。我意识到,如果我稍微放下一点点的代码时间,生活里还可以拥有更多有趣的东西。

健身

健身可以说是 2018 年唯一一件做成了的事情。我从 2018 年 1 月份开始健身,每周训练 3-4 天,控制饮食。直到现在刚好整整一年,达到了从 46kg 到 54 kg 的变化。

如果问我是怎么坚持下来的,我认为无论是坚持什么,都是:因为相信,所以咬着牙坚持试试,坚持了一段时间后,它就成了你的一种生活方式。当它成为了你的一种生活方式的时候,就无所谓坚持和不坚持了。就像你不会去问别人是怎么「坚持」看美剧的一样。

关于更多我健身的经验,我会在之后单独写一篇文章分享。

德州扑克

今年学会了德州扑克,和朋友打了很多场。我非常喜欢这个扑克游戏,他让我更了解我的牌友,更重要的是,在牌局里,我意识到了自己的缺点:冒进、喜欢承受高风险高回报、贪婪、充满侥幸心理。每次打完,通过回顾自己的打法,我更加了解我自己,我会反思:

  • 是不是贪婪让我失去了我的筹码?
  • 下一次我再遇到相同的情况时,我如何控制自己的欲望?
  • 我应该付出多少的筹码,才是一次价值下注,才能击败我的对手?
  • 面对失败时,我是否能做到及时止损,调整心态?
  • 当我读到对方的牌有 80% 的概率是比我强的时候,我是否可以克服自己的侥幸心理而不去跟注?

德州扑克结合了运气、心理、概率、演技,它不仅是人与人之间的博弈,也能让你有不妥协于运气的机会——你拿一手烂牌,仍然能打好(当然,运气有时候也会打败你,本来在转牌时胜利在望,河牌却是别人的翻身之牌)。

我还转载排版了世界扑克巡回赛(WPT)总决赛冠军老邱的小传记 《赌士列传: 老邱传奇》。相信你可以通过这篇传记感受到德州扑克的魅力。

博客成绩

在这一年收到了挺多的读者来信,向我询问建议,我都尽量抽时间一一回复。还有一些文章收到的打赏留言里提到的因为我的文章而有所收获,让我知道我写的博客的确有他的价值所在。

关于 2019

希望把生活过得更有趣一些。

2019 年终总结:慢慢变成别人眼中「食古不化」的「怪人」

总结

2019 年 3 月我来到杭州,在蚂蚁金服开始了新的工作。这是我第一次在广东省外的地方生活。

新的环境,新的工作,对我来说是一个很大的挑战。挑战不在于如何适应一个新的环境,而在于在一个技术基建完备的环境里,如果业务难关不是技术可以解决的,那我的价值在哪里?在这个方面,我让很多对我抱有期望的人失望了。

「我的价值是什么」是我不断寻找答案的命题,我不满足只作为一个消费者活在世上。只有作为生产者创造自己独特的价值,才让我觉得自己是这个世界的一员。

我创造了很多,但好像只有写博客算是对别人来说稍微有价值的东西。现代的人又不怎么喜欢认真地看文字了,写好一篇文章的阅读量也不比同样内容的一个视频高。就连我一个关注了很久的独立博客博主,也转了型做视频。

「写博客」似乎已经是一件看上去「食古不化」的事,而我竟然越来越「食古不化」了。有时候到一家餐厅,被告知没有餐牌,只能扫码点餐,我会反问,「如果我没有手机呢?」。

我想,如果旁边正好有一位比我年轻十岁的客人听到我这样问,他心中也许会暗想,「x, 这个食古不化的傻x」。

今年下半年我又因为不能忍受国内社交网络的反智内容和评论,微信和微博都变成了只写的状态,除非收到消息,微信几乎不怎么打开。此举极大地改善了我的心理健康。但在别人眼里,就是一个不合群的怪人。

告别 2019,我就 25 岁了。这是一个不算年轻,也不算不年轻的岁数。如果开始「食古不化」是变老的征兆,那么我已经开始变老了。但如果不懒惰、不犬儒、不圆滑、不反智就是年轻,那么我将会永远年轻下去。

数据

博客

今年博客的 UV 比上一年多了 10, 000. PV 比上一年多了 40, 000. 《健身一年》 问率最高。

博客没有广告,所有收入来源于读者自发赞赏,今年收到的赞赏总共有约 200 元人民币。

共收到 11 封咨询信和很有心的感谢信。

音乐

今年做了 6 首翻唱,上传在 YouTubeBilibili:

播放量最高的是《奉献》和《黄昏》。

阅读

今年读的书里有以下几本想推荐给大家:

《学会提问:批判式思维指南》(豆瓣 | 购买

这本书提到了几个常见的逻辑谬误,以及如何去看待别人的观点,如何得出自己思考过的结论。这本书没有教你应该相信谁,而是不应该相信谁。

《非暴力沟通》(豆瓣 | 购买

我的书评

《颓废与沉默》(豆瓣 | 购买

这本书结合中国发生的实例解释了中国存在的「犬儒主义」,和它产生的原因和影响。

消费

  • Kindle Oasis ( 购买 ) 替换了我的 Kindle Paperwhite. 手感很好,放裤袋很轻松。是我今年买得最值的产品。
  • 黑卡 3 ( 购买 ) 替换了我的黑卡 1, 有可以旋转的屏幕,自己拍视频的时候轻松多了。
  • iPad Pro ( 我的评测 ) 替换了我的 iPad mini 2. 主要用来看微信读书,睡前浏览各个咨询网站。最后悔是没有买蜂窝版。

每天都看的内容

放空的时候看的内容

关于 2020

创造更多价值吧。

2020 年终总结

COVID-19 疫情最严重的时候我在广州,记得我 12 月底从杭州飞回广州的时候,根本没有想到事情会发展得和 SARS 一样严重。印象中那个时候的关键词是:安静,冷空气,未知。不知道这样的情况会持续多久,每天关注着 Telegram 的 Broadcast 看新增的感染有多少。同时还远程办公着,做着「花呗来电」的需求。没错,就是那个让你付钱还要每月打电话提醒你还花呗的服务。

没想到多年前我们都在探索的远程办公,会因为一场传染病让更多人体验到了。但显然,我体验到的只是畸形的远程办公,他披上了远程办公的外衣,让我们每个人变成了 On call 24 小时 —— 比如我甚至在炒菜的时候还要接入电话会议。

我思考了很多,在 4 月份决定离开阿里巴巴。后来我面试了一些公司,那些你能说得出名字的公司我应该面了有一半。我很奇怪现在的「大厂」招聘到底是什么标准,问的问题到底有什么意义,让我一度严重怀疑我的能力。当然,有些面试者可能也觉得奇怪,为什么这样一个在博客上侃侃而谈的人,连浏览器如何验证 HTTPS 证书的都不知道。

后来落实了现在的工作,不过我现阶段不打算过多的提及我的新工作。但在这个新的环境,遇到的新的人们,对我一些固有的想法有了不少的冲击,让我有了一些新的想法和反思:

  • 优秀的程序员和普通的程序员之间很多时候只隔了一层好奇心,对「有没有更好,更有效率,更聪明的做法」的好奇心。在这个时代,「代码」是一种生产资料的,而大量的程序员们却自己忽略了这一点,或者自愿放弃了这点
  • 把技术作为兴趣的人是少数,只是互联网让我们有机会聚集到了一起,这让我很长一段时间误以为这就是程序员的常态。现在每当我因一个程序员缺乏专业素养而内心愤怒的时候, 我都会提醒自己,不是每个人都像我一样愿意(或者有条件)把技术带到日常生活中去。 我每天吸收的资讯、读的书大部分都和技术有关,这只是因为我的兴趣和职业都恰好是技术,但别人不是。
  • 我在很多人的眼里是幸运的 —— 我有条件在很小的时候接触计算机,我「幸运地」在退学以后还能进「大厂」,等等等等。我承认我是幸运的,但我一直在思考,我所享受到的幸运,可以如何为那些不那么「幸运」的人带去一些有用的东西呢?博客是我一直在做的,也能稍微达到目的,但这远远不够。

技术

  • 在一个内部平台用了 hapi, 我非常喜欢 hapi/boom 的设计。hapi 是我目前用 Node 写 API Server 的首选。
  • 学了一段时间 Go, 写了 snp. 目前还没有场景用到 Go 的更高阶的特性。以后我会尽量把和前端无关的工具用 Go 写。
  • 我仍然一直在关注 ReScript (也就是 ReasonML), 我认为 ReScript 是所有编译到 JavaScript 的方言里最优雅的语言。她的特性弥补了用 ECMAScript 写 React 的缺点。

我今年最喜欢的技术

Best buy

(*排名分先后)

我也对我买过的数码产品进行了反思,觉得有些对我来说是性能过剩的:

iPad Pro

如果现在让我选,我肯定会选更便宜且支持二代 Apple Pencil 的 iPad Air. 我目前对 iPad 的需求只有:

  • 支持二代 Apple Pencil
  • 支持蜂窝

MacBook Pro 32GB 内存

我在买这台 MacBook 的时候特意把内存加到了 32GB, 但这些年用下来,我发现我对 laptop 的需求瓶颈不在内存,而是 CPU, 运算速度,续航和发热量,而这些东西在这台 MacBook Pro 都没有做得好。

我可能会在一年后换成 M2 的 MacBook Air 取代这台 MacBook Pro.

Apple Watch

Apple Watch 对于有氧训练来说确实非常好用,但对于我大多数情况下都是力量训练的人来说并没有太大的用处,唯一的用处只有一些健身记录的 App 可以通过 Apple Watch 在训练的时候进行记录,或者组间计时。而我更愿意用小本子来记(因为记录一些训练感受的备注更方便),我的卡西欧手表计时也足够方便。

我也用 AutoSleep 记录过一段时间的睡眠时间和质量,但我本身睡眠就没有什么问题,所以这些数据对我没有什么帮助。

工具

今年用到了两个新的工具,值得分享给大家。

Roam Research

我在试用了 30 天 Roam Research 以后就发现我离不开它,所以咬着牙订阅了它。我记的想法或者笔记通常是零散的,没有组织的,但在 Roam Research 里可以通过 tag 和 backlink 把这些零散的记录关联到一起,可以很好地辅助我的写作。

比如在我的 Roam Research 里有提到关于 Roam Research 的记录都能直观地看到:

有时候我在写作的时候会惊喜地发现有些有关联的笔记我自己都已经忘了,如果不是 backlink 我可能永远都不会想起它。所以它就像是我第二大脑 :我们虽然暂时还无法复制我们自己的大脑,但是我们可以通过文字笔记的方式创造一个我们大脑的子集。它记忆了自己曾经学习过的、思考过的东西,永远不会忘记。在需要的时候,随时可以翻查这个第二大脑的记忆,而且他是网状的,拥有一定程度上的「联想」。

我之后会详细地分享我是如何用 Roam Research 的。

Cubox

https://cubox.pro

我以前不用书签管理工具,有些曾经读过的文章和工具,在之后想找也找不到了。在用了 Roam Research 之后,感受到了 tag 的强大力量,所以我开始用书签管理工具, 给每一个书签加上标签,日后我可以通过 tag 的组合找到忘掉的东西。

  • Cubox 也替代了我其它的 Read it later 工具,我现在看到无法短时间消化的文章,都可以收藏到 Read it later 的文件夹里,到晚上统一处理。
  • 在移动端用 share sheet 也可以很方便把移动端上看到的页面收藏到 Cubox

阅读

《精通正则表达式》

我是在一次面试的时候被问到过一个用正则表达式提取一个固定语法的句子里的信息的问题,之后我就读到了这本《精通正则表达式》,前三章已经让我收获非常大。这个收获不是那些正则表达式的基本语法,而是这本书想要传达给读者的思想 —— 把正则表达式作为一种工具去运用它。

我在读了这本书之后再也没有了以前对正则表达式的那种恐惧,而且还因为正则表达式学习了很多关于 Parser 的知识,也在日常开发中惊喜地发现有很多问题可以用正则的方式解决。

《精通正则表达式》是我今年读的最有价值的一本书。

How to make smart notes

因为用 Roam Research 所以读的一本书,讲的是做笔记的方法论。

Even the best tool will not improve your productivity considerably if you don’t change your daily routines the tool is embedded in, just as the fastest car won’t help you much if you don’t have proper roads to drive it on. Like every change in behaviour, a change in working habits means going through a phase where you are drawn back to your old ways.

《整洁架构之道》

我最早的职业规划是成为一个架构师,在工作以来也用自己的方法「设计」过很多系统和工具,这本书是讲真正的软件架构,里面解答了很多我想要知道的答案,软件架构的目标是什么,如何做软件架构,如何做技术选型。还有一些真实的架构案例可以参考。

这本书我要再读一两遍才能详细地和大家分享。

新的一年想做的

如果有机会,我希望可以在今年做一个小而美且有人愿意购买的产品,她会是好用的、体验好的、克制的、能带给用户更深层次的价值的。就像印刷机,他表面上看上去只是一个工具,但他对于人类知识文化的传播是功不可没的

2022 年终总结

今年的年终总结写得有点晚,2021 年觉得自己一事无成,迟迟没有动笔。如今 2022 年也已经过去了,虽然同样觉得自己在今年一事无成,但转念又想自己应该给自己一个回顾的机会,终于还是动笔了。

琐碎的生活变化

2022 年因为工作的变化,主要都生活在苏州。苏州是一个很「慢」的城市,我算不上特别喜欢,但我很喜欢苏州的湖。我在 ByteTalk 的这期播客节目 里提到过一些我对苏州的看法。

苏州的一些区域可以骑摩托车上路,我喜欢摩托,所以买了一辆摩托通勤,周末偶尔也会到不同的湖边溜溜车,还认识了一些摩友。

意外的插曲

我从多年前开始受情绪问题的困扰,在 2021 年开始变得更严重,到了 2022 年,它变得更加凶猛。

那根一直在紧绷的弦,最终还是断了。我在年中因为焦虑导致了惊恐发作, 这是一种焦虑的躯体化症状。由于没有相关经验,发作时我误以为心脏出现了问题,于是拨打了 120. 这也是我第一次坐上救护车。那一周我住进了医院,做了身体检查,排除了器官问题。

我的情绪问题导致了我在 2021 和 2022 都没有写什么工作以外的代码,我的内心一直在希望自己放松和希望自己多做点事情两者之间不断摇摆。我曾经认为自己是一个很清楚自己想要什么,也很有目标的人,但最近两年,我越来越不知道自己在做什么,要做什么。

零碎的产出

Logseq 分享

我在一个晚上非常即兴地录了一个关于我如何使用 Logseq 的视频,没想到会有这么多的播放量,在 YouTube 上也收到了不少的感谢和鼓励。

CodeSpeedy

发布了一个我自己个人在用的 Code snippet 管理工具 CodeSpeedy. 是我第一个发布的用 Tauri 写的程序。

AMAzingTalk

因为看到 Twitter 上有不少人提供付费咨询服务,所以做了一个页面收录了一些不错的提供咨询的人。目前只是一个简单的静态网页,但我有计划把它做成一个产品。

《兰迪和他的朋友们》

我身边有不少不同领域的有意思的朋友,很想通过访谈和交流的形式把他们各自领域的哲学分享给更多人。于是我开了一个不定期更新的 podcast 《兰迪和他的朋友们》。目前只出了两期,分别是和我的健身教练聊了健身,还有和我的同事兼攀岩岩友聊了攀岩。

新的一年我还计划做一个和香港流行音乐相关的节目,感兴趣的朋友欢迎 Email 和我交流。

攀岩

今年在我的同事 sixian 的带领下接触了攀岩,我也意外地爱上了这个运动。喜欢和岩友一起交流线路的不同爬法和技巧,喜欢用出优雅的脚法的感觉,喜欢到不同的城市拜访当地的岩馆 —— 例如我和 sixian 到 上海攀岩工厂探店 (Bilibili).

攀岩是一个力量和技巧结合的运动,我因为有力量训练的基础,让我入门这个运动非常快。但我喜欢学习和应用技巧的部分,我对攀岩的兴趣很大程度上来自于观看了 Jain Kim 的优雅脚法.

感染 Omicron

在 2022 年的尾声感染了 Omicron, 反复发烧 2 天多,咳嗽了近一周。我在开放了一周后才打了第一针科兴。我的症状比较轻,没有失去味觉和嗅觉,也没有所谓的「刀片噪」,只有轻微的扁桃体发炎类似的感觉。

最后

2023 年我就 28 岁了,有时候填表格看到年龄的一项写着 27, 会让我有些感觉陌生。我总是觉得自己距离踏出学校的那天还不是很远,在写做了 6 年程序员,我学到的 10 条经验 时我才发现自己已经工作了 6 年多了。我也偶尔看回 我 21 生日时写的博客, 很多问题在我内心缠绕:我还能做些什么?我想要的到底是什么?我的价值是什么?我接受了自己只是个普通人吗?

我没有答案。

2023 年终总结: 和自己对话

可在小宇宙收听本文音频版

变化

2023 年初,持续了 3 年的疫情封控结束了,整个社会迎来了一个转变的开端,没人知道会变好还是变更糟糕。随后 3 月,我决定离开微软,我自己也迎来了一个转变的开端,我也不知道会变好还是更糟糕。

我财务自由了吗?如果财务自由有一个绝对值,那么无论是多少,我肯定都没有达到。离职后有很多朋友问过我这个问题,现在刚好可以在博客也跟我的博客读者分享:当我在考虑不工作时,我在思考什么?

1. 如果继续现在的状态,那么 3-5 年内我会有什么不同?

这是我在考虑是否入职微软之前用的同一个方法,是我朋友教我的。当年我觉得如果不入职微软见识见识,我一定会后悔,留在原来的公司也不会有太大的变化。今天即使我已经离职了,但我很感谢我有选择微软,我的收获非常多。所以我运用同一种决策方式来思考,即使我在微软继续工作 10 年,很大可能我得到的只是 title 和薪水的不同,这和我的目标背道而驰。这也引出了我的第二层思考:

2. 我正在做的事和我的长远目标是否对齐?

过去我会担忧和焦虑我达不到目标,后来我醒悟:没有人可以控制结果,只要我正在做的事是朝向我的长远目标就可以了。继续工作显然不是。

3. 没有固定收入怎么生活?

我有一点积蓄,虽然不多。很多人存了钱,买了房子。而我用我的积蓄买一段可以不工作的时间,我觉得还是很值得的。而且我不工作也不是天天躺着什么都不做,只要朝着我的目标在努力,这也算是一笔投资。

幸福之道

离开公司后,我的心情很复杂。我觉得我一直想寻找一个答案,但实际上我连问题是什么都不知道。我想起了年轻时读的《刀锋》,应该像书中的拉里一样去寻找,于是 7 月份我去了泰国,得到了一段出乎意料的佛教哲学之旅

创造

今年的输出比我预期的要更丰富一些。

视频创作

继 2022 年的 Logseq 分享视频,今年又做出了一个超 50,000 播放量的视频《我如何做笔记》

《代码之外 Beyond Code》播客

今年终于和 GeekPlux 一起把盘算了多年的项目真正做了出来,意外地得到了很不错的反响。甚至能在线下聚会的时候听到一些我们的听众表达的感谢。

其实节目的制作形式受到了很多我喜欢的不同的播客节目,我由此也更深刻地感觉到,一个人能做出什么样的东西,很大程度上取决于其接触、输入的东西。只有吸收优秀的东西,才有机会做出优秀的东西。

这跟做产品也很像,如果一个人从来没有体验过真正优秀的产品,那就很难做出优秀的产品。所以我总是鼓励一些朋友,不要因为一个产品要花钱所以不去用它,就算只买一个月,也要去体验一下。

Randynamic Studio

离职后我就计划把我之后做的所有小工具统一到 Randynamic Studio 的名下. 很幸运,它的真正意义上的第一个产品 Notepal 算是非常成功。它没有为我赚巨额的钱,但它解决了很多人的需求,并且这些人愿意为它付费,这是我写在博客首页很多年的一个目标,在今年终于实现了。

多年来我把 Y Combinator 的 slogan 一直放在心中,有很长一段时间我每天睡醒后心里就会自动播放这句话:

Make something people want.

很幸运我朝着这一个目标又迈进了一步。

线下聚会

解除封控后,终于可以参加一些技术圈子的线下聚会了,也有机会见到一些相识多年但从未谋面的网友。

先是参加了佐玩开发者交流会,见到了很多很年轻的后辈,更惊喜的是很多是《代码之外》的听众,他们都说在节目中收获到了很多。在那一瞬间我觉得我做的很多事情都是有意义的。

然后我到杭州找了图拉鼎,见识了数字游民的聚合地「玉鸟集」。在那里还认识了非常多的独立开发者和做产品的人,大家都很有自己的想法,对自己做的事情有自己的理解,有自己的观点,自己的品味。这种类型的朋友是很难在工作的时候遇到的。

总结

2023 是我和自已对话的一年,我开始了解自己想要的是什么,了解自己的情绪,了解自己擅长的是什么,不擅长的是什么,了解自己追求的是什么,等等。在清迈,我学会了倾听内心的声音,我也意识到,在真正了解自己的情况下,做决定似乎没有那么难了。

展望

我其实很希望在 2024 年能做一些 scope 更大的事情,同时也需要找到和我有相同 passion 的人。我一直不称自己为「独立开发者」,一是我觉得我还没达到,二是这也不是我的目标。我的目标一直是两三个有同样追求的人组成一个小 team 做一个美且在商业上可行的产品。

希望自己能往目标再迈进一大步。

在清迈冥想学习 7 天后,我不再「追求」幸福

在清迈结束了一周的禅修学习,想把自己的体验和感悟写下来,才发现这些我感觉不可思议的体验,用文字和图片表达出来是多么的苍白。

事缘起年初意识到自己的心理健康状态不再适合继续工作,遂辞职休息。刚好看到早见小姐分享了她在巴厘岛冥想的点滴,才发现泰国清迈有寺庙提供冥想学习。于是当晚就找好了寺庙,订下去泰国的机票。

我在出发前一直在想这次远行学习到底能不能符合我的期望学习到「真正的」冥想技术。一周下来,我的感觉是这一周学习到的和体验到的远远超出我的期望。我不仅在老师的带领下练习冥想,还对佛教有了新的认识。

通往幸福的道路在哪里

我到的第一家寺庙是 Wat Umong, 中文应该译作乌蒙寺。我在这里待了五天四夜。Umong 基本是靠自己练习,没有系统的教学。这家寺庙在丛林里,所以特别贴近自然。

寺庙虽然没有强制不使用手机,但我在第一天开始就尽量不用手机了,只限制自己一些时间查看是否有重要信息,其余时间都是飞行模式,把手机当作相机用。

当身边没有任何可以让你转移注意力的介质(手机、电脑、互联网)的时候,内心就像被放了一个扩音器,一点微小的想法都会被放大和延伸。这让我真正地了解了自己内心的声音。

在离开 Umong 的前一晚,我和另一个中国人和一个俄罗斯人一起在黑夜中喝茶。这位俄罗斯朋友带了普洱和茶壶,跟我们两个中国人科普茶道。我们从 9 点聊聊到凌晨一点,聊了关于冥想的体验,我们各自的烦恼。我说当我在冥想的时候,我总是会想到未来的事,在这几天的冥想练习,我才发现原来我的内心一直纠结在我想像的未来,我一直找不到我的快乐和幸福在哪里,于是我很痛苦。

俄罗斯朋友(大意是)说:Don’t think too much. Focus on where you are, what you have, and what you can do with what you have.

我从他的回答得到了很大的启发。我一直在寻找快乐和幸福,以为它们在前方,我需要走一些路去找到它们。但我忽略了「当下」,在我已经拥有的东西,和当下我做出来的事情中感受它们。我也终于理解了寺庙里其中一棵树上挂的一句话的含义:There is no path to happiness, happiness is the path.

那晚我们还聊了很多,那一刻我希望时间可以过得更慢一点,可以在这样宁静的夜晚多待一下。萤火虫在我们身边飞来飞去,远处还有虫鸣,还有壁虎的叫声(我第一次听到壁虎也有叫声)。

第二天早上我们一起到寺庙的洞穴里面,那时还没有游客进来,我在洞穴里冥想了一会儿。然后俄罗斯朋友带我们去了附近的一个动物园,他说那里有一只会讲泰文的鸟。我不知道这种鸟叫什么(应该不是鹦鹉),我们听到它竟然会讲几句不同的话,还会唱一段相同的旋律。

临走前,俄罗斯朋友竟然送了我一个茶杯。

下午我就离开了 Wat Umong, 出发到 Wat Suan Dok.

介于哲学和信仰之间的佛教

到达 Wat Suan Dok 的 Monk chat office, 首先是我们接下来三天课程的老师 Phra K.K. 跟我们教授关于冥想和佛教的基本知识。K.K. 讲了差不多一个半小时,我学到非常多。后来在去冥想中心路上我和其它学员聊天,我们都一致认为我们学到了超出了我们预期的知识。这也让我们对接下来三天的学习很有信心。我们这一期一共有 15 个学员,来自世界各个地方,英国、美国、中国、西班牙、印度、新加坡、韩国等等。

在冥想中心,我们被要求一切都要慢下来。走路要慢,即使敲钟集合,一定不能急着过来,必须慢慢走。吃饭的时候,需要 eating mindfully (中文应该就是「正念饮食」),慢慢地把食物送到嘴里,慢慢地咀嚼,感受食物。

这是个小小的改变,但是三天下来,我的内心状态因此有了翻天覆地的变化。远离社交网络、做事慢下来,让我得到了内心的平静。我们从小就被鞭策一切都要快,要「抓紧时间」—— 上课铃响,要赶紧跑回教室;在学校吃饭,每次都要被老师催要吃快点回宿舍休息;走在路上,明明没有很赶时间,步伐也要很快。

当我开始把步伐慢下来的时候,就发现肢体上的缓慢,会使得内心变得平静。反过来,当内心开始胡思乱想,肢体动作就会开始不自觉地变快。有好几次,我在缓慢行走时开始分神,回过神来发现自己走得越来越快。

在第二天的讨论环节,K.K. 解答了我们很多问题,正是这些讨论,让我对佛教有了全新的理解。

我以前认为佛教是一种迷信,因为身边接触到的跟佛相关的东西,无非是「求神拜佛」。现在我才知道,佛教反对迷信。佛教不解释人是如何被创造的,没有像上帝这样创世的神。佛教是教人如何摆脱痛苦的。佛教让你相信你自己,而不是相信别人,也不是相信佛。只有你自己可以帮你自己摆脱痛苦,而不是相信一个神可以帮你。只有你自己去体验过,你才能去相信。

老师说,每个人都可以顿悟,只要持续练习(冥想)。佛陀叫作 Buddha, 意思是觉醒的人(the awakened one),每个人都能是 Buddha.

佛教是介于哲学和信仰之间的东西。我被其哲学的部分深深吸引。

知名投资人 Naval 把自己的生活哲学称为「理性佛教」,这也是我今后打算不断探索的一块。我想学习佛教中对人生、痛苦的理解,形成自己的生活方向盘。

对我来说,理性佛教意味着理解佛教所倡导的内在修行,以此让自己变得更快乐、更富有、更能活在当下、更能控制自己的情绪——成为一个更好的人。

所以,我的人生哲学就包含这两个方面:一方面是进化论,进化论是约束性原则,因为它解释了关于人类的诸多问题;另一方面是佛教,佛教是关于我们每个人内心状态的精神哲学,是最古老、最经得起时间考验的哲学。我认为这两点并不矛盾,可以相辅相成,互为补充。

—— Naval

出家的佛教徒需要遵循很多戒律,因为佛教认为痛苦来源于欲望。你的欲望越多,你将拥有的痛苦就越多(The more you desire, the more you suffer)。今天你买卡西欧,明天你就想要劳力士,于是你痛苦。有一天你得到了劳力士,就会发现不过如此,你的欲望让你还想要别的,于是你痛苦。

当然我只是一个普通人,一个带点野心活着的普通人,我不会遵守严苛的戒律,但通过冥想练习,我似乎更有能力从客观的角度观察(aware of)到我此刻的冲动的来源,从而冷静地思考是否要做出下一步的行为。

智慧是一种知道个人行为的长期后果的思维能力。 —— Naval

使人直面内心的冥想

在冥想的一周里,我基本没有怎么说话。但当我在进行冥想练习时,我实则和自己的内心对话了数千次。冥想能锻炼我们对情绪的觉察能力。我在头几天的冥想练习里,头一回通过观察我走神时不自觉思考的内容,清晰地观察到我的内心世界。在第三天,我就在我的日记本中写下了一句:「我太纠结于我想象中的未来」。

我和一起学习的一些学员交流我们的冥想体会,有的人会想起很多过去的事,而我会想到很多还没发生的,我想象的事:我接下来应该做点什么?别人会如何评价我的行为?我接下来要读什么书?要学点什么?等等等等。

佛教也认为痛苦来自对过去和未来的纠结,过去的已经过去,未来的还没发生,我们拥有的只有当下,我们应该在享受当下的幸福。而我意识到,我一直在未来寻找幸福 —— 读这些书会幸福、买这些东西会幸福、写这些东西会幸福。但是,一旦真正实现,我还是觉得不幸福,因为未来已来的时候,我又从未来的未来寻找幸福在哪里。我一直忽略了感受当下。

人们总是用一生来等待开始新的生活,这是很常见的现象。等待是一种思维状态,意味着你需要未来,而不要现在;你不要你所拥有的,而要你所没有的。任何一种形式的等待,都让你无意识地在你的此时此刻创造了一种内心的冲突:你不要此时此刻,你把希望寄托于未来。丧失对当下时刻的意识,会大大降低你的生命质量。

设定目标并努力去实现目标本身并没有错,错误的是你将它看成是你对生命和对本体的感受的替代品。

—— 《当下的力量》

在这一周的冥想,我只记得就只有短暂的 2 秒钟,我仿佛真正地感受到了当下。我不再想过去,也不再想未来,我只感受到当下,听到鸟叫,虫鸣,风吹过脸上。有一瞬间,我感觉被一个不知名的气泡笼罩,在这个气泡里有很大的安全感,我感觉我在此刻是无敌的,因为过去已经过去,未来的我不再去想,当下我可以做任何事。

你还可以通过将你的注意力集中在当下这一刻,从而在思维中创造那种空白。就是全神贯注于当下的时刻。这是一件非常值得去做的事情。通过这种方式,你可以将意识从思维活动中引开,并创造一种无思维的空白。在这种空白中,你高度警惕,注意力高度集中,但是你没有在思考。这就是冥想的本质。

—— 《当下的力量》

最后

一周的时间很短暂,但我想我这辈子都会记得这一周。离开寺庙,我还是比以往更慢地做一切事情。慢慢走、慢慢吃,没有事情是那么的紧急,手机并没有那么着急去看。不必用尽一切去填满我的时间,吃饭不看手机并不会损失任何东西。保持冥想练习,保持对自身情绪的觉察力。

最后三天我只用手机在离开前和我的老师拍了张合照。K.K. 是个非常棒的老师,跟我们讲了很多佛学知识,带我们做冥想练习,在傍晚带我们在外面欣赏晚霞和自然的宁静,在大雨的清晨 5 点多带我们听着雨声和鸟叫冥想,最后一天还送给我们每人一串带上他的祝福的珠链。

他说,monks get no salary in Thailand, I’m teaching you for free. 离开前我把我身上所有现金都 donate 给了他。

这是他在 YouTube 上曾经接受过的采访:https://www.youtube.com/watch?v=3g7TUT1kjC8

FAQ

教学用的语言是什么?

英语。我认为高中英语成绩不太差的水平就能听懂。

在哪里可以报名?

Wat Umong 不需要提前报名,每天早上 8 点半前报到既可。

Wat Suan Dok 的 monkchat 在官网上可以预约。3 days 的每月都有一次。虽然有 2 days, 但我建议 3 days.

为你写的一首诗

我喜欢你

从那年的某天

我突然遇到你

但他们说

不要谈论你

因为她不喜欢你

所以你是禁忌 你是克星

你只会带来麻烦

他们请我不要谈论你

我说你没有错 喜欢你也没有错

错在她不喜欢你

他们劝我私奔吧

去一个大家都喜欢你的地方

但她是我妈妈

我如何离开她

他们劝我离开你吧

即使没有你 生活如常

多年后

我偶尔提起你

他们请我不要谈论你

国内自建图床指南

我的博客很长一段时间在使用新浪微博作为图床,自从新浪微博开始防外链,我博客文章很多配图丢失了。我意识到我需要一个稳定可靠的图床,所以开始用阿里云自建一个我自己的图床,目前已经稳定使用了大半年。

我起初以为很难,而且费用不底。但是在这半年,我每个月的 CDN 费用不高(当然这也取决于访问量)。我自诩自己的博客不至于荒废或者没什么人访问,所以对于那些和我的博客规模差不多的独立博客博主,本篇应该算得上是一个十分贴切的参考。

当然,虽然我用的是阿里云,但套用到其它云服务都是一样的,读者可以读完后价比三家再作选择。

声明:本文和阿里云没有任何利益关系。

本文的目标读者

  • 有自建图床的需求,且对国内访问速度有要求的。例如独立博客、独立摄影站,甚至独立播客主于用存放音频文件。

云服务做图床的原理

云服务产品有很多,搭建图床只需要关注 OSS 和 CDN. OSS 是对象存储服务,通俗来说就是用来存文件的。OSS 都有对应的域名,文件保存在 OSS 后,可以通过 URL 下载它。

但是直接通过 OSS 下载的成本很高,价格十分昂贵,所以我们需要 CDN 来分发,节约成本。在阿里云,可以把 CDN 绑定到 OSS, 通过 CDN 去访问这个文件时,如果是首次访问,CDN 会从 OSS 取得这个文件,这个过程叫「回源」。之后再访问会直接从 CDN 读取。

步骤详解

因为我自己用的是阿里云,所以以阿里云为例(假设你已经注册好帐号)。

创建一个 OSS Bucket

一个 Bucket 相当于 OSS 中的一个存储空间,在 OSS 控制台 点击创建 Bucket:

填好 Bucket 名称和区域,其它选项按照默认即可。

创建成功后,在 Bucket 的文件管理可以上传文件:

查看上传文件的信息,你可以看到文件有 URL, 但由于在创建 Bucket 的时候,为了防止盗用,我们选的 Bucket 权限为私有,所以从 URL 其实无法访问这个文件:

创建 CDN 配置

CDN 控制台 进入域名管理,就可以开始配置 CDN 域名。

所以,在创建 CDN 前,你需要买一个域名。这个域名可以随便买个便宜的不主流的,因为没人在意一个图床的域名。

创建域名后,有一个要注意的地方,就是如果你需要国内加速,你的域名必须备案。备案其实是整个自建图床成本最高的一个环节。如果你的博客或者网站域名已经备案,那么可以直接用这个域名分配一个二级域名给 CDN 用。省去再备案的麻烦。

比如你的域名是 blabla.com, 那么你的加速域名可以是 static.blabla.com.

源站信息选「OSS域名」,选中之后会出现一个下拉选择,可以选中刚刚创建的 Bucket 源站:

如果你的网站用 https, 端口选 443.

如果你域名已经备案,就选全球或中国大陆。

设置域名的 CNAME

创建完后,你需要把你域名的 CNAME 指定为提供的值。如果你域名解析也是用阿里云,可以查看 这篇文档

开启 HTTPS

开启私有 Bucket 回源

因为前面在创建 Bucket 的时候权限设置为私有,所以需要给 CDN 开启私有 Bucket 回源的权限。

配置 Refer 防盗链

CDN 防盗是有必要的,如果你的图片被别处盗用,会增加不必要的流量。所以推荐设置 Refer 防盗白名单,只对允许指定的域名访问。例如我设置了除了我自己博客以外的一些 RSS Reader 以及 V2EX 可以访问:

使用 uPic 方便上传图片

以上的准备都做完后,你已经拥有了一个图床。现在就需要一个方便的工具把图片上传到图床。如果你用 macOS, 我推荐开源的 uPic

配置 uPic

添加阿里云 OSS 配置:

这里需要填 AccessKey 和 SecretKey:

你可以在 RAM 控制台 创建一个用户,然后创建 AccessKey.

创建后给这个 AccessKey 授 AliyunOSSFullAccess 这个权限:

我博客的 CDN 用量

从 2019 年 10 月 1 号至 2020 年 3 月 7 号总计 11.28GB. 按流量计费,每 GB 0.24 元,也才几块钱。

相关链接

如果你觉得本指南受用,可以通过 此链接 注册阿里云。

Serlina: 渐进式的 React 服务器渲染框架

副标题: 《可能是最适合 Egg 的 React Serverside-rendering 方案》

上一周周末我花了些时间来完成了一个 React serverside-rendering 框架——Serlina. 在此想通过这篇文章讲讲 Serlina 框架本身,以及我为什么要开发她。

(下文中 React Serverside-rendering 均简称为 “SSR”)

起因

最直接的起因是我们在内部有一个 React base 的项目的首页希望做服务器渲染,我参考了一些方案,如 Next.js, Fusion.js 等等。我很喜欢 Next.js, 我从他刚发布的时候就在持续关注,我认为他已经是最完美的 SSR 方案。

但是当我试图把 Next.js 接入到我们的服务器端 (Egg.js base) 时,我发现 由于 Next.js 需要控制 http context, 导致无法兼容 Egg 程序。

我认为 Next.js 的核心应该可以脱离 http context. 只需要完成构建配置、renderToString 这些脏活,然后把渲染后的 HTML String 返回即可。于是我浏览了 Next.js 的代码,试图寻找类似 nextjs/core 的东西,然而并没有。Next.js 是一个完整的 Web Framework.

于是我开始设计一个理念是脱离服务器实现的 SSR 框架,并取名为 Serlina. 她和 Next.js 拥有同样友好的开发体验,唯一不同之处是,她不关心服务器实现。

最简单的例子

安装依赖

npm i serlina react react-dom --save

创建一个应用目录

├── index.js
├── page
│   └── page1.js

编写一个 React 页面

// page/page1.js

export default () => {
  return <div>Hello Serlina!</div>
}

最后是服务器的实现

// index.js

const { Serlina } = require('serlina')
const path = require('path')

const http = require('http')

// 初始化 Serlina
const serlina = new Serlina({
  baseDir: path.resolve(__dirname, './')
})

serlina.prepare()
  .then(() => {
    http.createServer(async (req, res) => {
        res.writeHead(200, { 'Content-Type': 'text/html' })
        if (req.url === '/page1') {
          // 渲染页面
          const rendered = await serlina.render('page1')
          res.write(rendered.string)
        } else {
          res.write('works!')
        }
        res.end()
    }).listen(8090)
  })
  .catch(console.error)

通过以上的例子,Serlina 有两个最主要的 API:

  • prepare() 用于做构建准备
  • render() 用于渲染 React 页面, 得到 HTML string.

以上的例子也表达了 Serlina 的核心思想——她处理了 React 服务器渲染的一切脏活,然后把处理好的东西交给你自己去渲染到客户端。

这就是「渐进式」的意思:你可以在某些地方用她,也可以在某些地方不用她。你可以只在某个路由里面使用 serlina.render() 去渲染。这有点像是一个模板引擎。

在 Egg 中使用

egg-serlina

我之所以认为 Serlina 是最适合 Egg 的 SSR 方案,是因为我认为 Next.js 是最好的 SSR 方案。而 Serlina 把 Next.js 的体验带到了 Egg, 那么她应该就是最适合 Egg 的 SSR 方案。

以下内容非 Egg 用户可以跳过。

npm i egg-serlina react react-dom --save
exports.serlina = {
  map: {
    '/page1': 'page1'
  }
}

配置了用 Serlina 渲染的页面后,页面会在 getInitialProps 里得到 egg 的 ctx:

// {app_root}/client/page/page1.js

export default class Page1 extends React.Component {

  static async getInitialProps({ ctx }) {
    // ctx is egg `ctx`
    return {
      data: await ctx.service.getData()
    }
  }

  render () {
    return (
      <div>{this.props.data}</div>
    )
  }
}

常被问到的问题

和 Next.js 有什么区别

关于这个问题,上文已经说得很清楚了。另外,Serlina 并不是要取代 Next.js, 而是希望在某些场景,能成为一种合适的选择。

❌