2024年7月28日星期日

做给 GitHub Actions 开发者用的 Actions(Part 2)

在开发 GitHub Actions 时,有时候会遇到这样的问题:如果这个 Action 接受来自用户的 GitHub token,那该如何以这个 token 背后的身份完成所需的 git 操作?

我在上一篇文章里介绍了我做的 config-git-with-token-action,专门用来解决这个问题的,但这个 Action 在配置 git 的时候又是怎么获取对应的用户名和邮箱的呢?这是通过另外一个叫做 token-who-am-i-action 来解决的。

这个 Action 可以接受用户生成的 PAT (Personal Access Token),也可以接受 GitHub App 的 token。(默认的 GitHub Actions token 当然是接受的。)它会使用 Action 调用 GitHub API 查询关于 token 自己身份相关的信息,因为类似于 Linux 的 whoami 命令所以我把它叫做 token-who-am-i-action


这个 Action 能返回的信息当中,首先要看 typeUser 还是 Bot

  • 如果是 User 的话,它所返回的其他信息包括 nameemail。这两项都可能是 undefined,因为用户可以选择隐藏自己的名称和邮箱。
  • 如果是 Bot 的话,它必须返回 nameemailappSlug,全部都是字符串,不可能是 undefined。每一个 GitHub App 必须有一个用于显示的名字,然后由此生成对外公开的地址(格式为 https://github.com/apps/${appSlug})。GitHub App 其实并不存在邮箱,但使用特定格式(${id}+${login}@users.noreply.github.com)生成的邮箱会被正确识别并显示正确的头像,例如说 GitHub Actions 默认 token 的身份使用的邮箱是 41898282+github-actions[bot]@users.noreply.github.com

除此之外,无论是哪种类型的身份,这个 Action 还能返回 loginidglobalIdlogin 对于 User 来说,就是他的个人页面地址(https://github.com/${login})中的路径,有些文档也把这个叫做 username;对于 Bot 来说,这可以由 appSlug 通过特定格式(${appSlug}[bot])生成,例如 GitHub Actions 的就是 github-actions[bot]

至于 idglobalId 分别用于 REST API 和 GraphQL。这是 GitHub 数据存储很有意思的一个地方。对于每一种 REST API 的类型(如 user),它背后都是一张独立的关系型数据表,都有一个这种类型内部唯一的 id,但这个 id 可以跟其他类型冲突。在 GraphQL 里面,因为 id 可以用来查询任何类型,所以引入了 globalId 的概念,保证即使跨类型依然唯一。GraphQL 的某些 query/mutation 的 id 默认就是 globalId;但某些必须加上 X-GitHub-Next-Global-ID: 1 的 header 进行请求才是 globalId,否则就是跨类型不唯一的 id


那我们如何在编写自己的 Action 时调用 token-who-am-i-action 呢?如果你在编写的是 composite action,可以这样写:

runs:
  using: 'composite'
  steps:
    - uses: CatChen/token-who-am-i-action@v1
      id: token-who-am-i
      with:
        github-token: ${{ inputs.github-token }}

    - shell: bash
      env:
        LOGIN: ${{ steps.token-who-am-i.outputs.login }}
        GLOBAL_ID: ${{ steps.token-who-am-i.outputs.global-id }}
        ID: ${{ steps.token-who-am-i.outputs.id }}
        NAME: ${{ steps.token-who-am-i.outputs.name }}
        EMAIL: ${{ steps.token-who-am-i.outputs.email }}
        TYPE: ${{ steps.token-who-am-i.outputs.type }}
        APP_SLUG: ${{ steps.token-who-am-i.outputs.app-slug }}
      run: |
        echo "Login is $LOGIN"
        echo "Global id is $GLOBAL_ID"
        echo "Id is $ID"
        echo "Name is $NAME"
        echo "Email is $EMAIL"
        echo "Type is $TYPE"
        echo "App slug is $APP_SLUG"

如果你编写的是 JavaScript action 可以先从 NPM 安装同名的 token-who-am-i-action 包,然后再进行调用:

import { tokenWhoAmI } from 'token-who-am-i-action';

const me = await tokenWhoAmI(githubToken);

const {
  login,
  globalId,
  type,
} = me;

if (me.type === 'User') {
  const {
    id,
    name,
    email,
  } = me;
} else if (me.type === 'Bot') {
  const {
    appSlug,
    id,
    name,
    email,
  } = me;
}

希望这个 Action 对各位 GitHub Actions 开发者有用。非 Actions 开发者也可以直接在 Workflow 里面使用这个 Action,如果你的 Workflow 使用非默认的 GitHub Actions(机器人)身份进行 git 操作的话。大家在使用过程中遇到什么问题,或者是希望增加什么新功能,欢迎到项目的 GitHub 开 issue。

2024年6月30日星期日

做给 GitHub Actions 开发者用的 Actions(Part 1)

在开发 GitHub Actions 时,有时候会遇到这样的问题:如果这个 Action 接受来自用户的 GitHub token,那该如何以这个 token 背后的身份完成所需的 git 操作?

使用 token 操作 GitHub API 是很容易的,通过 @actions/github(或 @octokit/core)创建一个 Octokit 实例时把 token 传进去就可以了,之后通过这个实例进行的所有 API 调用(包括 REST 和 GraphQL)都会以这个 token 的身份进行。但命令行的 git 操作怎么办呢?如何让 git commit 的作者变成 token 背后的身份?如何让 git push 以 token 背后的身份进行提交?(这两者并不一定要用同一个身份。)我做了 config-git-with-token-action 就是用来解决这个问题的。

这个 Action 会对 ghgit 进行配置,让它们的身份信息变成 GitHub token 背后的身份信息。gh 的配置相对简单一些,把 GH_TOKEN 这一环境变量配置好就行了,然后执行 go auth status 就能打印出 gh 认为自己在使用的身份信息。对 git 进行配置稍微麻烦一些,gh auth setup-git 只能保证 git 在跟 GitHub 交互时从 gh 获得身份信息,但并不指明具体是哪一个身份。为了保证 git commit 使用正确的身份,需要通过 git config 来设置正确的用户名和邮箱。此外,为了保证 git push 使用正确的身份,需要通过 git remote set-url origin 来更新上游地址,在 https://github.com/… 的地址中注入用户名和 token,让它变成 https://username:token@github.com/…

这个 Action 假设用户在执行它之前已经执行过了 @actions/checkout,所以它不会尝试自行建立项目目录。大家最好使用同一个 token 进行 @actions/checkout,这样项目目录从一开始就是以 token 背后的身份创建的。以下是一个完整的用例:

runs:
  using: ‘composite’
  steps:
    - uses: actions/checkout@v4

    - uses: CatChen/config-git-with-token-action@v1
      with:
        github-token: ${{ inputs.github-token }}

    - shell: bash
      run: |
        echo “Set up git user name: $(git config —get user.name)”
        echo “Set up git user email: $(git config —get user.email)”
        echo “Set up git remote origin with login and token: $(git remote get-url origin)”

    - shell: bash
      run: |
        touch test_file
        git commit test_file -m ‘Created test file’
        git push

做为 GitHub Actions 开发者,如果你利用 JavaScript 而非 bash 进行开发,那上述 composite action 的用例并不适用,我们需要一个针对 JavaScript action 的用例。我自己有同样的需求,所以 config-git-with-token-action 同时还是一个 NPM 包,可以在 JavaScript 中进行调用获得同样的功能。(这个包具备完整的 TypeScript 类型信息。)安装好之后,通过 JavaScript 调用的用例如下:

import { configGitWithToken } from ‘config-git-with-token-action’;

await configGitWithToken(githubToken);

希望这个 Action 对各位 GitHub Actions 开发者有用。非 Actions 开发者也可以直接在 Workflow 里面使用这个 Action,如果你的 Workflow 使用非默认的 GitHub Actions(机器人)身份进行 git 操作的话。大家在使用过程中遇到什么问题,或者是希望增加什么新功能,欢迎到项目的 GitHub 开 issue。

至于这个 Action 是如何获取到 token 背后的身份信息的,那是这个系列的下一篇文章要介绍的下一个 Action 负责的。

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 这两家,里面有很多黑科技是外面大家根本不知道能做得这么好的,知道能做得这么好就是你最大的优势。(说得直白点,小厂是「贫穷限制了想象力」,而你见识过拥有几乎无限资源的大厂的想象力能去到的地方。)


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