2024年3月1日星期五

如何扩大工作的 scope?

在美国工作的程序员都会遇到一个问题:想要晋升但经理说自己工作的 scope 不够大,又或者是影响力不够大。那要如何才能扩大自己工作的 scope 呢?

职业前期直系经理会负责为你找 scope,一步一步地给你更大的 scope,到了后面经理就会说「自己找 scope,这是晋升到下一个级别的要求,我给你 scope 就满足不了这个要求了。」既然不能依赖别人给你 scope,那要去哪里找扩大 scope 的机会呢?

这是很多人感觉到困扰的问题。假设自己在做的 scope 大小算作 1,把上下左右的外延做了也就是 1.1、1.2、1.3……的增长,这扩张速度太慢了。找一个跟自己已有 scope 相似但需要花同样功夫的 scope,那可以从 1 变成 2,但工作量跟着翻倍,拼命卷可以升一级,但 scope 大小从 2 到 3 是不可能的。况且职级线性上升时需要的 scope 是指数上升的,卷到三倍的工作量也没用,公司期望 scope 按照 1、2、4、8……的速度来增长。


在这篇文章里面,我们的关注点是如何找到更大的 scope,不是有了更大的 scope 后怎么做出来。怎么做出来当然是个难题,但很多人被卡在了找不到 scope,所以必须先解决这个问题。找不到 scope,不意味着找到了就有能力做出来,但找不到的时候人就会觉得自己有能力无处施展,这会导致很强的挫败感。

如何能找到更大的 scope?关键是要走出去跟更多人接触。技术再厉害的人,把自己关在小黑屋里面对着代码库发呆,是几乎不可能找到更大的 scope 的。你只能看到你已经知道的问题和机会在哪里,最多再看到一点外延,但指数级扩大的问题和机会很难闭关冥想出来。

我们可以在白纸上画三个从小到大的同心圆,分别代表:我能控制的事情、我能影响的事情、我能感知的事情。如果只考虑工作上的事情,这三个集合应该是两两子集的关系,「我能控制的事情」是「我能影响的事情」的子集,「我能影响的事情」是「我能感知的事情」的子集。

找不到更大的 scope,问题往往来自于「我能控制的事情」扩张到逼近「我能感知的事情」的边界了,然后就找不到空间继续扩张了。想要对着「我能控制的事情」大力出奇迹是很难有效果的,把工作量翻倍后 scope 依然上不去。真正能扩大 scope 的,是先把「我能感知的事情」扩张出去,有空间了再把「我能影响的事情」扩张出去,最后才能把「我能控制的事情」扩张出去。

如何能够把「我能感知的事情」扩张出去?我们必须走出去,接触更宽广的世界,接触更多各式各样的人。在一家大厂内部,这往往就是走出去跟别人聊天。我们对自己每天在做的项目、在解决的问题有太深的了解了,我们需要了解别人尝试解决什么问题、有什么问题解决不了,更大的 scope 就埋藏在那里。


如何走出去跟别人聊天?可以先从已经跟你有交集的人开始。肯定存在一些人,项目上跟你有合作,但有限的合作导致你们的交互很少,你不知道在合作之外他的主要工作是什么。你可以约他们喝咖啡,不要聊你已经知道的事情,问一下他在合作以外的主要工作是什么,以及他的工作有什么有意思的地方、有什么难题或者是不爽的地方。

关键是你要会聆听,他说的事情你要么理解了,要么通过更精准的问题追问。想象一下你跟他聊天后需要回来汇报跟他相关的所有信息,你自然会仔细听,甚至会做笔记,不会瞎扯一番大家爽了但谁也不记得聊了什么。这个对话应该像个自然的社交聊天,你不能如同审问对方一样获取信息。对方说的事情,你有共鸣的可以回应,你有不同观点的可以分享,这才是一个对话。

职级比较低的人,对于找职级比较高的人聊天可能存在心理上的障碍,觉得不应该浪费对方宝贵时间,错误假设自己懂的对方都懂了,所以对方不会从对话中获得任何价值。其实职级比你高的人也是人,你跟他们聊天可以提供情绪价值,有时候还能帮他们理清思路。如果长远来看你们会有更多合作,他们肯定乐意花时间跟你建立良好的信任关系,这包括花时间互相了解对方,尤其是对方的思维方式、思考问题的出发点。

刚刚开始跟几个人聊天时,你会获得大量碎片化的信息。聊的人多了之后,慢慢这些信息就会连接起来,呈现出公司内部更大的图谱。你需要在这个更大的图谱当中寻找当前运作得不够好的地方,例如说可靠性低或者是效率低的地方,然后想想你是否有想法和有能力去解决,这些都是你潜在的 scope。可能你没有信心直接玩起袖子开干,但你至少有素材跟你经理进行对话,说说你看到的机遇在哪里,让他就什么机遇更适合你提出他的看法。

2024年2月18日星期日

Vision Pro 使用体验(Part 2)

Vision Pro 的「截屏」功能非常符合 iOS 和 watchOS 用户的习惯,把仅有的两个物理按键同时按下去就可以了。跟用 Vision Pro 拍摄 3D 视频没有取景框一样,截屏时你没办法知道截屏边界在哪里。Vision Pro 给你一个相当宽的可视角度,但实际截屏时它会截取一个 16:9 的区域然后保存为 1920x1080 的图片。

考虑到 Vision Pro 两块屏幕加起来像素超过 4K 屏幕,截屏 1920x1080 的分辨率实在是有点低。你可以截屏分享给别人,让别人看看你佩戴 Vision Pro 的体验是怎样的,但千万不要指望别人能够看清楚你第一人称视觉能看清楚的细节,更别期望别人能看清截屏上的小字。经过 3D 到 2D 的投射之后,截屏上偏小的文字是很难看清楚的,需要很用力地看才能看明白。

此外,我不知道 Vision Pro 截屏时使用的是左眼还是右眼的视觉,这值得研究一下。


把 Vision Pro 变成 Mac 外置屏幕的功能还不错。就算是 16“ MacBook Pro 的屏幕也只有 16”,但用 Vision Pro 打开瞬间可以变成 75" 的大屏幕,而且依旧看不到像素。这个屏幕可以用来玩游戏,游戏的计算应该是在 Mac 上进行的,Vision Pro 只是投屏而已。对于游戏来说,有一个巨大的投屏可比 MacBook Pro 的屏幕爽多了!而且这个投屏还不受物理空间限制,即使你的房间没那么大,放下 MacBook Pro 后就没多少空间了,你依然可以在 Vision Pro 里面放置一个突破房间墙壁限制的大屏幕。

比较遗憾的是缺乏一台 Mac 多屏幕投屏的支持。Vision Pro 只能显示 Mac 的主屏幕,不能选择增加屏幕。如果在投屏到 Vision Pro 之前 Mac 就已经连接了外置屏幕,投屏后所有外置屏幕都会熄灭。Mac 自身的主屏幕也会熄灭,既然投屏了就没必要在 Vision Pro 外面显示一模一样的内容了。这对于注重隐私的人来说会很有用,例如说在咖啡店或在飞机上用 Mac 加 Vision Pro 投屏,别人就看不到你的屏幕了。

想把 Mac 投屏到 Vision Pro 时,可以在 Vision Pro 里面抬头调出头顶上的 Control Center 然后选择连接附近的 Mac。更加神奇的连接方式是,在 Vision Pro 里面以穿透方式看着一台 Mac 的屏幕,Vision Pro 自动能够识别出这是哪台 Mac 然后在 Mac 的屏幕上方放置一个投屏按钮,点击按钮就会开始投屏。


反过来,你在 Vision Pro 里面看到的画面也可以投屏到 Mac 或 iPad 上。这很适合用来做演示,把自己在 Vision Pro 里面看到的内容分享给身边的人看。操作起来跟 iPhone 投屏到 Mac 上一模一样,在 Vision Pro 的 Control Center 选择 Screen Mirroring 就可以了。

访客模式是使用投屏的另外一个常见原因。visionOS 不像 macOS 那样存在多用户登录,更像是 iOS 那样只允许单一用户,只能够绑定单一 iCloud 帐号。如果想要把 Vision Pro 借给别人使用,就要开启访客模式,之后可以限制访客能够打开的应用(他会看到能打开的应用中你的所有数据)。为了更好的指导访客使用你的 Vision Pro,或者是为了更好地监控他在你的 Vision Pro 里面干什么,你可以在开启访客模式的同时投屏,那你就能看到它看到的画面了。


前面说到 Mac 投屏突破房间墙壁限制,我可以解释一下 visionOS 的 2D 窗口是如何跟现实世界的 3D 物品重叠的:应用的 2D 窗口永远会优先于现实世界的物品。举个例子,我可以在我和应用窗口之间放一个小盒子,理论上这个小盒子应该会遮挡窗口的一部分,而且 visionOS 确实能扫描到这个小盒子的存在。然而 visionOS 并不会让这个小盒子穿透显示到我的 Vision Pro 屏幕上,应用窗口即使在小盒子背后也会被优先显示出来,导致小盒子被隐藏起来。唯一例外的是我的双手,把手举起来放在 Vision Pro 和窗口之间,手是会被显示出来的,我可以看清楚手和窗口的互动操作。

Vision Pro 使用拇指和食指触碰一下表示单击,这是大家都在官方视频中看到的,但其实还有另外一种点击方式。只要把窗口拉到自己面前,手指可以直接点击窗口上的按钮,手指穿越 3D 空间中的 2D 窗口会被视为点击,手指穿越窗口后上下左右移动会被视为拖拽。这种设计使得 visionOS 里面显示的所有 2D 窗口名义上都是 iOS 一样的「触摸屏」,习惯触摸屏的用户会发现这非常符合直觉。


这次就写到这里吧,接下来想到有新内容再更新。这个系列的《Vision Pro 使用体验》,我准备想到哪里就写到哪里。不想错过接下来的内容的话,敬请关注和订阅。这篇文章首发于我的 Patreon,大家可以到 Patreon 上付费支持我写作。

2024年2月9日星期五

Vision Pro 使用体验(Part 1)

在 Vision Pro 之前,我有 Oculus Rift 和 Quest 2,这两个都是纯 VR 设备(Quest 2 在接近障碍物时会触发黑白穿透视频)。Vision Pro 跟它们比,最大的优势是 AR 不会晕,我连续使用几个小时都没问题。之前 Quest 2 玩 Light Saber 一天最多玩 20 分钟,然后就开始感觉到头晕了。 虽然 Vision Pro 是有点重,但习惯后并不会觉得难受,当然脱下来之后脸上还是会有一圈的痕迹。我选择的是到 Apple Store 店里提货,顺便做 fit test,保证扫脸得到的尺寸合身。我觉得 fit test 还是挺重要的,扫脸归扫脸,但最终你需要找到一个几乎不漏光且重量均匀分布在脸上的尺寸。如果重量主要落在额头、脸颊或左右两侧,都会感觉到不舒服。

Apple Store 的店员有一套专门的「合身调试流程」。他们会先按照扫脸的结果给你对应的尺寸,然后问你哪里有漏光、哪里重量不均匀、哪里压得你不舒服,根据你的回答来找不同的尺寸给你试。当然,这暂时只能在美国体验到。在其它地方购买别人选好的尺寸,不太合身也只能将就了。


Vision Pro 的视力矫正镜片本质上跟眼镜是一样的,分为处方眼镜和老花眼镜两种。老花眼镜 $100 一对,只有固定的几个度数。处方眼镜就是要验光才能生产的眼镜,支持近视(据说能到 1100 度)、远视(据说能到 500 度)、散光等,$150 一对。之所以叫做处方眼镜,是因为在美国必须要有最近 6 个月的眼光处方才能配。镜片由蔡司生产,必须上传美国处方并由蔡司验证后才会进行生产和寄送。

镜片的使用体验很好,轻轻放在 Vision Pro 屏幕上面就会通过磁吸固定。跟我之前佩戴眼镜使用 Rift 和 Quest 2 对比,不需要配搭眼镜的体验好多了!不需要顾及眼镜如何塞进设备有限的空间里面,不需要小心穿戴设备,戴上不需要调整眼镜立即能用。购买时 Apple 会强烈建议你在镜片上刻字(只能使用大写字母进行拼写),保证你的镜片就算不慎跟别人的镜片放在一起了也能迅速区分开来。(左右两片镜片有「L」和「R」的标记进行区分。)

两个人共用一台 Vision Pro 的话,切换物理镜片很方便,唯一问题是镜片取下来后没有专门用来存放的盒子,只能放回原包装盒里。visionOS 能够知道镜片更换了,需要重新校准目光,否则 Vision Pro 没办法精确地根据你看着哪里来点亮哪里。这个过程就比较麻烦了,要连续三次地点亮六边形六个顶点。就算两个人都有 Vision Pro 的锁屏密码,不需要开启 Guest Mode,切换镜片依然麻烦。


Vision Pro 的操作系统叫做 visionOS。对于习惯了 iOS 和 tvOS 的用户来说,visionOS 会显得完成度不够高。这不会影响使用,但会在很多细节上体现出来:

  1. iMessage 尚不支持 Contact Key Verification。这在最新版 macOS 和 iOS 都已经支持的功能,在最新的 visionOS 1.0.2 竟然还不支持。(尚处于 beta 的 visionOS 1.1 我没安装。)如果你的 iCloud 帐号已经启用了这一功能,在 visionOS 激活 iMessage 时就会出错。你必须在另一个设备上关闭这一功能后,才能在 visionOS 上再次登录 iMessage。
  2. 在 iOS 上 FaceTime 和 iMessage 同时出现在系统设置里面,如果遇到上述 iMessage 激活问题你会想要在 visionOS 系统设置里面找 iMessage 重新激活,然后你会发现 visionOS 系统设置只有 FaceTime 没有 iMessage。visionOS 会在桌面 iMessage 图表旁边显示一个叹号,让你在打开它时重新登录。visionOS 把 iMessage 当作一个普通的应用把它的系统设置中藏起来了。
  3. 除了能够显示多语言以外,国际化设置基本上不存在。输入法只有英文和 Emoji,Siri 只有英语,日期和时间格式的选项是不存在的,公制单位还是英制单位的选项也是不存在的。(多语言的字典倒是存在的,所有 macOS 上有的字典 visionOS 都有。)
  4. Game Center 必须使用 iCloud 帐号,不能如同 iOS 一样注销后再登录另一个 Apple ID。

Vision Pro 天然支持 3D 视频,因为左右两眼是两个独立的屏幕,可以显示不同的内容。Disney+ 的应用在 Vision Pro 里面会专门显示一个 3D 影片的分类,很多 Marvel 电影和 Pixar 动画片都有 3D 的版本。我打开了《Avengers: End Game》看了个开头,发现 3D 电影的效果很好。前景的人物和物件显然是立体的,背景的投射会随着你头部移动而进行轻微的调节,使得背景看起来有深度。这是 Vision Pro 跟 3D 电视不一样的地方,后者并不会跟踪你的头部移动。

Vision Pro 支持播放 iPhone 15 Pro 和 iPhone 15 Pro Max 拍摄的 3D 视频,同时也能进行 3D 视频拍摄。用 iPhone 拍摄的话,视频必须用横屏模式拍摄,因为这样 iPhone 才能使用两个摄像头从左右两个角度进行拍摄。拍摄好 3D 视频后,在 Vision Pro 里面打开照片应用就能看到(假设 iCloud 已经设置好,图片视频已经自动同步),其中专门有一个 3D 视频的分类帮你把 3D 视频从所有视频中筛选出来。

在 Vision Pro 里面播放自己录制的 3D 视频,感觉就像在眼前打开了一个传送门,看到传送门另一端 3D 的景象。平面视频会让你觉得原本 3D 的世界已经被强行投射到了一个平面上,只是这个平面上的画面在动。但 3D 视频看起来就像一个传送门,能够看到立体的人物和物件在动。3D 视频的播放器还有一个「全屏模式」,或者说是「沉浸模式」,把视频播放器的边框虚化为云雾一般,然后嵌入到你身处的场景当中去。

同样是拍摄 3D 视频,用 Vision Pro 的话没有取景框,而且拍出来的视频长宽比是 1:1,也就是正方形的。没有取景框的体验有点奇怪,你没办法确定什么被拍进去了、什么被剪裁掉了。此外 Vision Pro 拍摄视频的稳定性没有 iPhone 好,人带着 Vision Pro 拍摄视频时难免会随着目光转移而轻微移动头部,拍出来的视频就会有对应的轻微抖动,观看时就更有可能觉得不适。iPhone 拍视频时已经智能对视频做了稳定化,即使有一点点手抖拍出来的视频依然是非常稳的。

在可以选择的情况下,我会建议使用 iPhone 拍摄 3D 视频。iPhone 的相机默认是没有开启 3D 视频拍摄的按钮的,第一次使用之前必须先去系统设置打开,然后在拍摄视频的界面就会出现一个 Vision Pro 外圈形状的图标,用以开启 3D 视频拍摄。具体怎么操作可以看 Apple 官方的帮助文档


这个系列的《Vision Pro 使用体验》,我准备想到哪里就写到哪里。这次先写这么多,接下来想到有新内容再更新。不想错过接下来的内容的话,敬请关注和订阅。这篇文章首发于我的 Patreon,大家可以到 Patreon 上付费支持我写作。

2023年10月22日星期日

2023 年邀请链接(referral links)

我在 2018 年和 2019 年夏天都分享里一批我觉得值得推荐的服务和产品。疫情开始之后我就忘记了这回事,好多年都没再分享过。(不过我们《牛油果烤面包》还是每年年底做一期《好物推荐》的。)现在想起来,就写一篇 2023 年的吧。

我今年推荐的服务和产品包括:

  1. Neon
  2. Interviewing.io
  3. One Medical

以下是它们的详细介绍。

Neon

Neon 是一个三藩市湾区的亚洲菜外卖服务。跟其他随时点随时送的外卖不一样,Neon 是需要预订的,饭店会提前做好交给 Neon,Neon 每天下午派送。派送时食物是冰着的,重新加热一下就行。这些都是有门店的饭店,不是中央厨房,出品也是在门店能点到的菜色。

他们有 Koi Palace(鲤鱼门)的焗葡挞,一份 8 只或 24 只,我买回来用空气炸锅三分钟加热一下就很好吃。饭后作为甜品吃一个,一份可以吃一星期。(没有空气炸锅的话,烤箱也可以但就是慢。)两磅一份的咖喱牛筋腩也不错,完全冰冻的,重新蒸热了就能吃。牛筋这种在家里要煮很久才能煮烂的食材就很适合买别人煮好的。

他们有 Shanghai Dumpling King(包饺店)的小笼包,一份 10 只或 30 只。我买了之后冰起来作为备用的主食,什么时候需要了就拿 10 个出来,蒸 10 分钟就能吃了。他们还有 Harborview Restaurant & Bar(凯悦汇)的贵妃黄毛鸡和明炉烧鸭,这两个我也经常点。除此之外,他们还有其他东南亚菜系,都是由三藩市不同的饭店做的。

住在湾区,有时候不想做饭也不想出去吃的话,就会叫外卖。但是等饿了再叫的话,外卖又不知道要等多久才来。如果能够提前计划一下这周吃是什么的话,订 Neon 是很可靠的,通常下午 2:00 到 4:00 送到。因为 Neon 的食物需要重新加热,所以是当作食材来卖的,没有消费税。因为司机一天下午把整个湾区都送了,所以也没有转门的送餐费用,消费就随意了。

如果你使用我的邀请链接的话:

  • 你得到的好处:$15 的 Neon credit。
  • 我得到的好处:$15 的 Neon credit。

邀请链接:https://chen.cat/neon-referral

Interviewing.io

这是一个针对特定大厂(Facebook、Microsoft、Google、Amazon、Apple)匿名模拟面试(mock interview)服务。匿名是它最大的特点,你和面试官之间只有语音和代码,没有视频。面试官都是来自大厂的面试官,他们会用公司的面试流程和标准来面试你,如果你对某一家大厂的面试方式一无所知的话可以通过模拟面试来了解。面试结束后,你可以问匿名的面试官要反馈,也可以问他更多关于他公司的面试信息。

这些匿名的面试官会评价你的面试,如果他们觉得你通过了面试就会在 Interviewing.io 的系统里提交相关信息。如果系统发现你能稳定通过某一家大厂的模拟面试,它就会把你推荐给那家大厂进行 on-site 面试。你能跳过 technical phone screen,因为你已经通过了的 Interviewing.io 匿名面试就当作是 technical phone screen 了。

跟真实但匿名的大厂面试官进行模拟面试是要花钱的,具体价格由面试类型(coding、system design、behavior 等等)决定。除此之外,Interviewing.io 还提供免费的 peer-to-peer 面试预约,但面试官不是后台认证的大厂面试官所以质量就很难说了。如果只是想要多找人互相模拟面试,这是一个不错的选择。

如果你使用我的邀请链接的话:

  • 你得到的好处:首次消费时 $100 的折扣。
  • 我得到的好处:一次免费的算法模拟面试(必须在你消费之后)。

邀请链接:https://chen.cat/iio-referral

One Medical

今年 2 月份被 Amazon 收购的 One Medical 是一个连锁的诊所服务。有一些互联网大厂提供免费或打折的 One Medical 订阅服务,但就算没有公司打折的话也值得考虑全额自费购买。

One Medical 在美国各大城市(都市圈)都有诊所。在手机上预约医生面对面的门诊很方便,通常能约到一个星期内的。预约医生(可能包括 Nurse Practitioner)视频门诊的话,通常能约到一个小时之内的。疫情期间我每次出行回来都会用 One Medical 来约一次核算,流感等疫苗有时候我也会选择预约 One Medical 的(虽然 Walgreens 和 CVS 通常也有)。

总的来说 One Medical 提供了便利,不再需要自己去使用难用的医疗保险网站搜索医生,只要医疗保险能覆盖到 One Medical,就能直接在上面搜索附近的地点然后选一个最快能约到的医生。对于我这种以前在 Facebook 时习惯了园区内就有医生和牙医的懒人来说,非常方便。

如果你使用我的邀请链接的话:

  • 你得到的好处:第一年年费省 $50。
  • 我得到的好处:没有。

邀请链接:https://chen.cat/one-medical-referral

实体商品

我推荐的实体商品都会放在我的 Amazon Storefront 上面,今年我觉得值得推荐的是 Insta360 Link 摄像头第二代的 AirPods Pro(USB-C 版本)

Insta360 Link 是 Insta360 把便携智能云台用电脑摄像头上的结果,它能够通过人脸识别找到我,然后自动跟随我,还能根据我离摄像头的距离来调整变焦。作为 4K 摄像头,它的视频质量是没问题的。更重要的是,我不需要在视频会议前刻意调整我的摄像头角度了,Insta360 Link 一启动就会找我的脸。

AirPods Pro 应该不需要介绍了,大家都看过 Apple 官方的宣传片,降噪效果确实非常好。值得一提的是,此前有一个 Lightning 版本的我不小心连盒子扔进洗衣机洗了之后,拿出来干了竟然还能用。(偶尔有点小问题,但能用。)

2023年3月30日星期四

熟练使用 Google 或 Facebook 内部工具毫无用处?

有一部分 Google 和 Facebook 员工总是在担心一件事情:公司内部使用的所有工具都不是来源的,离开了公司就不会再用到,花那么多年把这些工具用到熟手了,一旦离职就会变成毫无用处的沉没成本。这种忧虑显得非常围城:在大厂外面的人觉得自己使用的工具不够好,想要进大厂看看到底人家有什么黑科技;在大厂里面的人觉得自己只用闭源工具,担心因为自己不懂其它公司都在用的来源工具而失去竞争力,想要花时间学习开源工具但又没有机会。

担心这种事情并非毫无道理,但我觉得这不是一个真正值得解决的问题,而且不是一个解决后就一了百了的问题。如果此时此刻在大厂,还不如专心做好眼前的工作,等到真正换工作的时候再根据工作需要现学。真正重要的能力,不是你现在懂什么,而是需要在未知环境中摸索时你能够学得有多快。(学校里建立激励机制是错的。根据你此时此刻懂多少来决定给予多少正向或反向激励,一旦你把这内化了就会造成职业上长远的负面影响。)


为什么说「不懂外部开源工具」这个问题不可能被彻底解决?这本质上还是因为技术发展的速度太快,如果一门技术此时此刻用不上,你学了之后就会逐渐贬值。

很多人只看到了现在成功的独角兽在用什么技术,但现在已经成功了的独角兽都是 10 后那一代创业公司了,它们创业时赶上了 AWS 的浪潮,但那时候大家用的 AWS 跟今时今日 AWS 提供的服务根本不是同一回事。那时候大家还是用 EC2 这样赤裸裸的机器,运维还是工作的一大部分,大家尝试做运维自动化但成熟度跟今时今日比简直就是玩具。

随着这一批 10 后创业公司的成长,它们对 AWS 的用量越来越大,遇到的运维问题越来越复杂,然后才出现了现在常见的这一些解决方案:用 AWS 管理界面太手工了,所以用 Terraform 来统一管理需要用到的 AWS 资源。自己在 EC2 上安装和维护软件太麻烦,关键的是缺乏一致性,公司内部做一套统一的容器镜像吧,把所有常用的东西打包进去。很多这些解决方案都是为大规模 AWS 运维设计出来的。

然而如果你看一下 20 后新生代的创业公司在用什么,你会发现你学会前面的一切都没用。20 后创业公司一上来就用 Firebase。EC2 是什么?虚拟机是什么?容器是什么?统统不知道,也不需要知道,赶紧把东西做出来,获得用户和拿到融资最重要。Firebase 当然有它的弱点和限制,大多是 20 后创业公司在扩大规模时都会遇到 Firestore 无索引筛选慢的问题,然后要把数据迁移到 GCP 或干脆把服务迁移走。

现实情况就是这样子,如果你离开大厂加入一家 10 后创业成功的独角兽,你就必须接受过去 10 年大家在 AWS 上建立起来的复杂生态环境,但即使你学会了你依然摆脱不了原来的忧虑:这些技术仅适用于这一批公司。你跳回去大厂,这些技术就没用了。你跳到 20 后早期创业公司,这些技术也没用。等它们 10 年后成长为独角兽时,你需要为它们解决新一代技术带来的问题,而不是去纠结上一代技术已经把上一代的问题解决得有多好。


你的职业发展如何,受你控制且最重要的部分是你能够为别人创造多大的价值。(这也是学校激励机制从来没有帮助你内化的东西。)从懂什么技术出发思考如何优化职业发展,这本身就是错误的方向。你必须从如何创造价值出发进行思考,如果没有充足的信息进行思考那就先尽心观察收集数据。

每一家公司要解决的问题都不一样,具体的某一种技术可能为解决特定的某一个问题提供了所需的手段,但越是通用的技术越需要针对特定问题通过定制化来落地。我见过很多人把「这在我上一家公司非常成功地解决了这个问题」带到新公司,尝试重复一边解决同样的问题,最终发现没有两个问题是一样的。在新公司不接地气,过去成功过的解决方案在新公司不能落地,最终都会失败。

我观察到一些人的成功路线,是借助在上一家公司打开的眼界,在下一家公司结合实际地解决问题,然后迅速地成长。例如说,在大厂当个 L5 接手维护一个曾经非常有开创性的系统,保证它的服务质量同时在上面添加功能。没错,这开创性的工作是轮不到你做了,几年前做这件事情的人升了 L6,但这样的机会只有一次,你不是第一个把路从无到有地踩出来的人,你就没机会升 L6。但你可以把这个系统和它解决的问题搞明白,接着去一家尚未解决过这个问题的独角兽,帮助他们解决这个问题。

对你来说真正有价值的是你见过这件事情能做成的视野。一件探索性的事情知道它能做成,你就已经成功了一半。(有很多探索性的事情,最终是做不成的,至少是在你有生之年人类无法达成。)但你不能把大厂的方案直接抄一遍,你需要理解在这个相似的领域这家公司独有的问题是怎样的,然后定制一个能在这家公司落地的方案。在一家独角兽迅速成长的那几年里,很多问题都是由于规模迅速扩张而造成的,曾经帮助这家公司成功的那批人对这些问题一无所知,而你在大厂见过规模成功扩张后的方案,所以他们会寄望于你来帮助他们解决问题。因为这件事情在这家公司还没有发生过,这个问题也没有被解决过,你在这家公司做出来就算是开创性的了,于是你可以在这里升 L6。

等你升完 L6,环视四周,发现升 L7 的好机会也都被别人挖掘完了,接下来在这家公司升 L7 会变得越来越难。没关系,你需要做的是把前面这个过程再重复一遍,这家公司已经被人利用过的 L7 机会,总有下一家还在路上的公司还没有遇到过相关的问题,等着你去解决。闭源的大厂,尤其是 Google 和 Facebook 这两家,里面有很多黑科技是外面大家根本不知道能做得这么好的,知道能做得这么好就是你最大的优势。(说得直白点,小厂是「贫穷限制了想象力」,而你见识过拥有几乎无限资源的大厂的想象力能去到的地方。)


回到文章的主题上来,熟练使用内部工具本身没有什么特别的价值,但知道这样的工具能做出来(而别人不知道这样的工具竟然能做出来)就很有价值,深入理解这些工具是如何被设计出来的以及它们被设计出来时的历史背景也很有价值。

2023年3月26日星期日

从人工编辑到算法排序

为什么作为企业内部沟通工具,Slack 在公司还小的时候那么好用,但在公司变得越来越大之后 Slack 会变得如此低效?这个问题我思考有一段时间里,我觉得这是一个数据规模的问题,在规模还小的时候 Slack 是一个很好的解决方案,但一旦规模大到一定程度 Slack 就不再有效率。

如果要跟 Slack 做对比,在 Facebook 内部使用 Facebook Workplace 的效率就很高。Workplace 有它自己的问题,例如说导致员工不停地在刷 newsfeed,因为总是害怕自己错过了什么重要的信息,所以一有空停下来就要刷。除此之外,在 Facebook 内部使用 Workplace 的体验很好,重要的信息总是会在 newsfeed 上出现。因为 Facebook 知道你是谁、你跟哪些同事的交互比较多、他们又跟哪些内容交互比较多,所以 Workplace 的排序算法能够把对你重要的内容有限显示出来。

为什么 Slack 和 Facebook Workplace 存在这样的区别?我思考的结果是,Slack跟 Workplace 的区别本质上很像 Yahoo 作为一个门户网站跟 Google 的区别。前者依赖于人工编辑,而后者使用算法排序。在数据规模有限时,人工编辑能够应付得过来而且效果可以非常好,毕竟这是让人亲自审阅这些内容然后决定什么重要、什么不重要、如何归类等等。随着数据规模变得越来越大,人工编辑最终会应付不过来,这就是 Yahoo 作为门户网站最终会输给 Google 的原因,同时这也是 Slack 不好用的原因。


面对大量没经过整理的信息,我们有两个非常不一样的选择:

  1. 尝试控制这些信息的产生和流动。
  2. 放弃对信息产生和流动进行控制。

这两个不同的方向,导致了截然不同的产品设置。

门户网站和搜索引擎的区别能够很好地解释这两个方向的差异有多大。门户网站相信自己可以人工整理和索引世界上所有的网站,或者至少是重要的网站。因此如果你要找某个网站,在门户网站上你要么能找到这个网站(有索引)要么不能找到(没索引)。这就好比图书馆,你不知道是否存在一本某个题材的书,但你可以根据图书索引找到这个题材的书所在的书架,浏览完书架上所有的书你就知道你想要的这本书是否存在了。

搜索引擎做了一个本质上截然不同的假设。沿用图书馆的例子,因为数据规模如此之大,你就算根据图书索引找到了你想要的书所存在的书架,你也会发现符合这一分类的书架如此之多你根本浏览不过来。在一个小图书馆里,可能计算机科学只是一个书架。在人类知识的图书馆里,计算机科学的书架区域可能有好几平方公里大。使用搜索引擎的前提,就是你选择了主动放弃对信息的控制,你让图书馆管理员帮你挑 10 本跟你想要题材最相关的书,他选好后拿过来给你浏览,你从中挑一本你觉得最合适的拿去读。

使用 Google 进行搜索时,虽然 Google 会告诉你总共有多少条结果、分开多少页显示,但大多数人根本就不会在乎,因为前几条有你想要的信息就是有,没有的话再翻几页也不太可能有。这就是我所定义的放弃控制:选择相信算法把对你重要的信息放在前面,放弃排在后面的信息。无论你从哪一个点开始选择不再看排在后面的信息,你都可以相信你已经浏览了相关性高的信息,而你不看的信息的相关性肯定不如你已经看过的高。


Slack、邮件以及公司内部人工编辑的门户网站,其实都属于第一种选择,相信人能够控制信息。Slack 可以通过 channel 控制信息分类,可以通过加入和退出 channel 来控制你具体接收的信息。邮件无论使用 Gmail 还是 Outlook 接收,都可以设置复杂的规则对邮件进行控制,例如说什么邮件不看,什么邮件不那么频繁地看。公司的内部门户网站也一样,编辑往往站在公司的立场来思考公司想要让什么员工获取什么信息,然后编写文章并发布到对应的频道。

Facebook Workplace 属于第二种选择。使用 Workplace 意味着你放弃了手工(包括通过设置规则)判断什么信息重要、什么信息不重要,你选择相信 Facebook 的排序算法帮你把重要的信息排在前面,因此无论你刷 newsfeed 刷到哪里停下来你都可以相信你已经阅读了比较重要的信息,排在后面你不去阅读的信息一定没有前面的那么重要。正是因为 Workplace 的这种特性,在 Facebook 这种规模已经非常大的公司内部 Workplace 会显得很好用。

今时今日已经没有人使用门户网站了,大家都使用搜索引擎,这是因为全球公开的信息的规模已经非常之大。然而企业面对的情况不一样,每一家企业都是从小开始做大的,在它们还小的时候 Slack 就会显得非常好用。(我自己经营一家几个人的公司,一个 Facebook Messenger 的群聊就够了,甚至不需要分多个 channel。所有人知道公司内部所有正在进行的讨论,因为根本就没有多少讨论。)然而当企业做大之后,切换到 Workplace 就很难。因为信息架构的本质区别,Slack 里面的信息不可能导入到 Workplace 里面,这就使得 Slack 拥有巨大的粘性。

Workplace 有些很成功的跨国公司客户,例如说 Walmart 和 Starbucks。对于这种规模的公司来说,使用 Workplace 非常合适。但创业公司上了 Slack 的船后就很难下来了,这就导致了今时今日很多创业公司有一定规模后需要想办法应对 Slack 带来的各种负面影响。我当然希望能够直接从 Slack 切换到 Workplace,但实际上这对于任何一家这种规模的公司来说这都是一个痛苦的迁移。

2023年3月19日星期日

在 React Native WebView 中使用自定义字体

在 React Native 中使用自定义字体(自己提供 woff2 或其他格式的字体文件)很常见,官方文档也说得很清楚。在 WebView 中使用自定义字体也是有文档可以参考的。但在 React Native 里面使用 WebView 并且要在 React Native WebView 里面使用自定义字体,我搜索了一下没找到现成的文档,只好自己根据 iOS 和 Android 原生 WebView 的文档研究对应的 React Native 方法。

这里说的是在 React Native WebView 中嵌入构建时已经打包进去的本地自定义字体文件。如果使用网络上的自定义字体文件的话,标准的 CSS @font-face 就能解决,唯一需要注意的是网络上的自定义字体文件是否跨源(cross origin)。跨源在 WebView 里也不是解决不了的问题,可以通过改变页面的 baseUrl 把本地生成的 HTML 页面变成同源。也可以把字体部署到自己控制的服务器上,把 Access-Control-Allow-Origin header 设置好。

iOS

React Native WebView 在 iOS 上使用的是 WkWebView,React Native 中的组件 API 跟原生 WkWebView 的很相似。原生有一个这样的 API 用来加载一个 HTML 页面:

func loadHTMLString(
    _ string: String,
    baseURL: URL?
) -> WKNavigation?

原生应用可以通过 Bundle.main.bundleURL 来获得应用目录的绝对路径 URL,也就是以 file:// 开头的一个 URL。在 loadHTMLString 时把 baseURL 指向这个 URL 那打开页面的绝对路径也就是应用目录的绝对路径了。假设打包时已经把 custom-font.woff2 文件当作资源打包进去放在应用根目录了,那在 CSS 中就可以以相对路径的方式指向这个文件了:

@font-face {
  font-family: "Custom Font";
  src: url("custom-font.woff2") format("woff2");
}

在这里 custom-font.woff2 等同于 ./custom-font.woff2,也就是在应用根目录的同名文件。这在原生很容易解决的问题,在 React Native 中的主要障碍是缺乏一个 JavaScript 可以直接读取的 Bundle.main.bundleURL。为此我们要自己写一个简单调用 Bundle.main.bundleURLNative Module

// WebViewBaseUrl.m

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(WebViewBaseUrlModule, NSObject)
RCT_EXTERN_METHOD(getBaseUrl:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
+ (BOOL)requiresMainQueueSetup
{
  return YES;
}
@end
// WebViewBaseUrl.swift

import Foundation
@objc(WebViewBaseUrlModule)
class WebViewBaseUrlModule: NSObject {
  @objc
  func getBaseUrl(_ resolve: @escaping RCTPromiseResolveBlock,
                  rejecter reject: @escaping RCTPromiseRejectBlock) {
    resolve(Bundle.main.bundleURL.absoluteString)
  }
}

获取到应用路径后,把它用于 React Native WebView 的 baseUrl,本质上跟 iOS 原生的 baseURL 没什么区别,只不过前者是 string 后者是 NSURL

const baseUrl = await NativeModules.WebViewBaseURL.getBaseUrl();
return (
  <WebView
    source={{
      html,
      baseUrl,
    }}
  />
);

(考虑到 macOS 的相似性,这个方法估计在 macOS 上也有效,但我没有测试过。)

Android

Android 的应用根目录路径跟 iOS 不一样,此外 Android 给 WebView 提供了一个神奇的但仅限于 WebView 的应用根目录路径,那就是 file:///android_asset/。在 Android 的原生代码里面是不能使用 file:///android_asset/ 访问应用内的文件的,但在 WebView 里面却可以使用这个神奇的绝对路径。

有了这个神奇的路径后,Android 上需要做的事情就很简单了。我们不需要从原生代码获取应用路径,只需要使用准确的相对路径就可以了。假设 custom-font.woff2 文件正确地放置到了 assets/fonts/custom-font.woff2 目录,便宜打包后这个文件就可以在 WebView 里面通过 file:///android_asset/fonts/custom-font.woff2 访问。为了跟 iOS 使用相似的 CSS 相对路径,我们可以把 baseUrl 指向 file:///android_asset/fonts/(假设 WebView 不需要用到其他本地编译时打包好的资源的话)。

let baseUrl;
if (Platform.os === 'ios') {
  baseUrl = await NativeModules.WebViewBaseURL.getBaseUrl();
} else {
  baseUrl = 'file:///android_asset/fonts/';
}
return (
  <WebView
    source={{
      html,
      baseUrl,
    }}
  />
);

这样就可以同时覆盖 iOS 和 Android 的场景了。React Native WebView 其实还支持 Windows,但我不需要支持 Windows 所以也没去研究。