普通视图

发现新文章,点击刷新页面。
昨天以前Cat in Chinese

DEI 是不应该有配额的

作者 Cat Chen
2025年4月19日 11:24

我还记得在 Facebook 参加「反歧视」培训时,我举手提了一个问题:「如果我们不是歧视少数的一方,而是歧视多数的一方,这是否合法?」我得到的答案是「逆向歧视也是一种歧视」。这意味着不歧视的话,招聘招到什么样的人就是什么样的人,不可能给少数群体安排一个配额。(除非招聘总人数没有上限,在少数群体满足配额之前无论招到什么人都继续招聘。)

Facebook 在这方面是说到也做到的,不存在任何针对少数群体的配额。针对少数群体的弱势地位,有几件事情可以做:

  1. 可以放宽招聘的入口。如果招聘职位招到的都是男性,那考虑一下是不是招聘广告在女性面前的暴光率就不够,让更多的女性进入招聘的漏斗是可以的,但进入了漏斗之后的层层筛选就不能有歧视了。
  2. 可以在人远远没到招聘之前提供帮助。例如说那些针对中学生的编程课程,有女生想要加入就应该欢迎她们加入。如果有女生因为社会刻板印象而怀疑自己是否应该加入,可以鼓励她们撇开社会刻板印象从自身的兴趣出发考虑要不要深入学习编程。
  3. 可以对员工提供「无意识歧视(unconscious bias)」的培训。有时候员工并没有主动歧视某个群体,但是社会刻板印象会导致他们无意识地就在歧视某个群体,例如说有出差的机会时就默认怀孕的员工不会想要参加。这种培训可以让大家留意到自己日常工作中的一些无意识行为,从而避免因此引入的歧视。

我之前听说 Google 的招聘存在针对少数群体的配额,如果属实的话这其实也是一种歧视。


为什么有些政府或企业会设立 DEI 配额?因为这样子才能显示出自己真的有贡献,产生了影响(impact)。我上面所说的 Facebook 的做法,往往是无法在数字上体现成果的,一测量就会发现数值没产生足够可信的变化,数值浮动可能纯粹是噪声。这样很容易引起别人的质疑,「投入这么多资源去改善少数群体的处境,到底有没有效果?」

设立少数群体的招聘配额或者是晋升配额,通过逆向歧视完成配额,至少避免了上述的质疑,因为少数群体的实际招聘和晋升人数或比例每一年都在涨。这时候政府和企业就可以大谈自己在 DEI 上面的贡献有多少,成果有多出色。然而正是这种急功近利的做法,导致了最近反 DEI 的现象。

Donald Trump 反对 DEI,并且说要创造一个「colorblind and merit-based」的社会,然而真正符合 DEI 精神的做法本身就应该是「colorblind and merit-based」的。他和他的支持者实际反对的是逆向歧视,这是一个合理的诉求,毕竟逆向歧视也是歧视,只不过受到伤害的是多数群体而已。但是这种做法把 DEI 的名字搞臭了,现在不再有一个能代表真正「colorblind and merit-based」的词汇。


真正做 DEI 必须要沉得住气,在数据长期不动的前提下坚持做自己认为正确的事情。我在 Facebook 内部接触到各种帮助员工提升的群体,大多数帮助的结果都无法量化,但如果你相信你帮到了别人你就应该继续做下去。

我曾经跟负责 Facebook 女工程师成长的资深工程师聊过,她说她们组织资深的女工程师对其他女工程师提供帮助,结果就是无法量化。无论你如何分析晋升和绩效数据,都没有可量化的结果。有时候做好事和面子工程是不可兼得的。她最后的结论是,「如果你认定事情是对的,你就应该坚持去做,无论最好是否有可量化的结果」。

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

作者 Cat Chen
2024年7月29日 12:37

在开发 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。

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

作者 Cat Chen
2024年7月1日 13:30

在开发 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 负责的。

Vision Pro 使用体验(Part 2)

作者 Cat Chen
2024年2月19日 13:26

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 上付费支持我写作。

帮助别人的正确姿势

作者 Cat Chen
2022年9月26日 06:01

大家晋升到一定级别后就会被要求带新人,怎样带新人才算是成功?绩效评价时是只看有还是没有带人,还是要看带人具体的效果?如果要看效果的话那如何衡量效果?这是我在 career coaching 的过程中时不时会遇到的问题,我觉得可以把我的框架写下来分享一下。

首先,带新人以及更广泛的 mentoring 和 coaching 肯定是可以根据结果来衡量做得好还是不好的,但刚刚开始上手这项工作时更应该看重的是效率而不是效果。这不是说完全不看结果,而是说在合理的难度上能及格地完成工作但不追求更好的效果。

这有点像程序员刚刚开始学习编程时写代码的目标:要能够通过编译,执行结果要符合预期,但不考虑执行性能,也不考虑代码是否优雅。首先要会写代码,接着要多写,写得越快就能在同样的时间里写得越多。练习多了以后,再考虑如何提升围绕着编程的其它指标。如果一开始写不出正确的代码,或者练习的数量不够,其它指标均无法优化。


在有机会带人之后,首先要保证别人确实能够从你这里得到帮助。简单地问一句「你觉得这对你有帮助吗?」就够了,绝大多数时候别人都会说「有帮助」。如果你发现对方迟疑一下才告诉你「有帮助」,你可以再追问一下「有哪些方面可以改进吗?」可能你分享了很多信息,但没有回答到点上;可能你提供了很多理论,但对方还是不知道接下来该做什么。多尝试直接或间接地获取反馈,做到对别人提供有效帮助并不难。

在确认别人能够获得帮助后,接着就需要提高效率。我们可以用一个数值来衡量效率:杠杆比例——如果你花 1 倍时间来帮助别人就能让别人省 n 倍时间的话,这个 n 是多少。效率最低时 n=1,这意味着对方需要获取帮助的事情你帮他做了,而且你做得还不比他快多少。(如果 n<1 你要认真考虑一下你是否应该带人。)带新人的话往往一开始就能做到 n=2 甚至 n=3,毕竟你比新人要更熟悉整个技术栈,就算你帮他把他的工作做了,你也应该比他亲自做要快不少。

我通常建议的目标是 n=8,也就是说如果一天工作 8 小时,那你花 1 个小时就能帮助别人省 1 天的时间。从 n=2 到 n=8 有一段距离,没有人可以瞬间做到 n=8,但要把这做为一个长远的目标,不停地想办法优化以提高 n 的数值。这时候有一种意识很重要:只帮对方必须要获得外界帮助的东西,他自己能够缓慢学习提高的东西不需要帮。

打一个比喻:想象一个倾斜的平面上有一个因为摩擦力而没有向低处滑动的物体。因为静摩擦比动摩擦要大很多,重力无法抵消静摩擦所以物体不会运动,但有可能重力比动摩擦要大,只要运动起来就可以加速。你需要做的就是提供一开始克服静摩擦的那个力。如果没有这个力,这个物体可以相对平面永久地静止,但一旦它运动起来了,无论初速度有多低都能逐渐加速。

这跟「授人以渔」是不冲突的。你教会一个人基本的捕鱼技巧之后你就可以让他自己通过训练来提高捕鱼的效率,他应该自己想要捕更多鱼,这样子他才能从饿肚子变成能吃饱,再到能够卖鱼赚钱。他开头的阶段体验可能不太好,自己捕的鱼自己都吃不饱,饿着肚子又要去捕鱼了。但你效率也很重要,你效率越高就可以越大规模地帮助其他还完全不会捕鱼的人,这些人因为完全不会捕鱼都快要饿死了。(前提是确实有很多人能从你的帮助获得价值,都等着从你那里获得帮助。)

为了实现这个目标,在帮助别人之后最好自己复盘一下,想想自己有哪里的效率做得不够高,如果再遇到类似情况的话怎样能够提高性价比。


效率上去之后,你就可以开始关注难题了。就好像你在能够熟练编程以后,你就会想要找难题来解决一样。难题之所以难,是因为你不能通过提供信息(包括知识、数据等)来解决问题,真正的障碍往往来自于你想要帮助的人的意识。可能在他价值观深处藏着一些他自己也不知道的东西,阻止了他达成他的目标。可能你能看出来他的目标在南面,但他的价值观会引导他向北走。

有时候你提供信息(一张地图)给他就行了,他能够从逻辑上分析出来:要么调整价值观,使得价值观能够允许自己往南走;要么放弃这个目标。有时候你会遇到很顽固的人,他觉得只要往北走足够远就能绕地球一圈抵达目标。你需要挖掘他此前怎样的人生经历塑造了他这样的价值观,然后才能确定你是否能做点什么。这些难题的共同点只存在于这个层面:你不可能用你的知识和逻辑去帮助他,你需要搞明白这个人是经历了什么才成为了今天这样子。


这篇文章首发于我的 Patreon,对同类文章感兴趣的话可以考虑在 Patreon 上支持我,付费订户可以提前至少一周阅读到我的最新文章。同时也欢迎大家通过邮件RSS/Atom 免费订阅我的博客。

面试题:开发新功能和重构老代码之间怎么选?

作者 Cat Chen
2022年8月14日 08:00


最近面试的多家公司当中,有至少一家尝试在行为(Behavior Questions,或 BQ)面试中问我这样的问题。尽管可以去猜这家公司到底更重视迭代速度还是代码质量然后从对应的角度去回答,但正确的答案只有一个,那就是不要回答这个问题。


第一原理(英语:First principle),哲学与逻辑名词,是一个最基本的命题或假设,不能被省略或删除,也不能被违反。第一原理相当于是在数学中的公理。最早由亚里士多德提出。——Wikipedia

对于所有商业公司来说,第一原理永远是商业,不是产品也不是技术。(「商业」和「业务」在英文里都是 business,在这里不针对中文进行区分。)产品的存在是为了服务于商业,技术的运用是为了服务于产品和商业。脱离商业探讨技术上的取舍毫无意义,因此如果被问到先开发新功能还是先重构老代码,必须要从商业的角度进行回答。

开发新功能对产品和商业有什么影响?重构老代码又有什么影响?这是你必须和面试官探讨的问题。面试官可以会提供更多具体的场景信息,也可能让你陈述不同场景下不同的取舍。我可以提供一些不同场景的例子。


如果我们是一个创业公司,或者是大公司的内部创业,正在做一个探索新市场的新产品。大家对这个新市场不是特别有信心,希望尽快通过产品验证市场是否存在,不存在的话就换方向或者撤销团队。这时候重构代码的优先级就很低了。或许几个月后发现假象中的新市场并不存在,现在的代码写得再烂,几个月后都随着产品下线而全面删除,谁在乎它烂呢?

这背后是有真实的故事的。Facebook 曾经做过一个针对拉美的应用,叫做 Flash,跟 SnapChat 很类似但这是在 Facebook 主应用之外的一个独立应用。Flash 花了 3 个月就发布了第一个版本,我们团队跟 Flash 团队合作,基于他们的代码进行二次开发,有时候会因为代码质量踩坑,我们非常希望 Flash 团队维持很高的代码质量。但他们的目标就是快速验证拉美是否存在一个低端用户的 SnapChat 市场,所以他们就一直往前冲。又过去了三个月,他们团队进行了一次内部讨论,到底接下来应该继续往前冲,还是先重构一下代码以便将来跑得更快,最后他们选择了继续往前冲。

我们团队虽然不喜欢他们这个决定,但这是一个正确的决定。做决定之后的三个月,他们做了最后的冲锋,把值得做的事情都尝试了一遍,最后的结论就是这个市场不存在或者说不值得做。Flash 应用下线,代码全部删除,团队解散。重构并不会改变这个结果,之后稍微推迟这个结果。从商业的角度来说,重构导致了近期成本,但不会带来长期收益,所以不应该进行重构。


如果我们是一个做券商的金融创业公司,那取舍的方向就可能很不一样。我们的首要目标是评估代码出现逻辑错误导致的财务风险。如果我们做的只是现金业务,财务风险是有上限的,也就是所有用户存放在我们这里的现金总量。在最坏的情况下,我们可以把所有用户存放在我们这里的现金都归还给用户,然后可能再承担对应的监管机构罚款和诉讼赔偿,损失的总额是可以进行估算的。如果我们涉及到证券业务,财务风险可能就是不封顶的,因为证券交易可能涉及杠杆和做空。

在进行这种讨论时,需要注重两方面的思维能力:

第一,对商业运作涉及的风险有基本的认识。可以不是很懂面试这家公司的具体商业模式,但要有商业常识和词汇,能够跟面试官进行这方面的沟通,然后才能进行商业上的趋势。

第二,坚持第一原理思维。不要为了控制商业风险就进行重构,而要思考控制商业风险的最佳手段是什么,用这来判断重构是不是合适的技术手段。商业风险有很多非技术的控制手段,例如说买保险。就算是技术手段,也不一定需要是重构,可能提高测试覆盖比例的性价比更高。总之不要为了合理化重构而找一个商业上的理由,那如同拿着锤子找钉子,本末倒置。


从这一道面试题可以推导出来一个更加广泛适用的思维模式:不要别人问什么你就答什么。考试(包括 LeetCode 形式的上机考试)往往把人训练为问什么就答什么,因为问题是没有协商空间的。但在面试的时候,一个大活人随时准备好跟你对话,你必须先对问题进行协商。

这是我在做 career coaching 中时常会提醒客户的事情。尤其是面试 senior 或以上职位时,搞清楚面试题的第一原理很重要,你找到了第一原理然后才能围绕第一原理进行解题。至于引出问题的说辞,例如「开发新功能和重构代码之间怎么选」,这其实不是重点。有时候这个说辞可能是个「X-Y 问题」,但我们期望资深的程序员有侦测和解决 X-Y 问题的意识。

Facebook 工程师文化独特之处

作者 Cat Chen
2022年2月17日 15:07

我在 Facebook 工作了 7 年,结合 Facebook 之前和之后的其它公司的经验,我觉得 Facebook 的文化有些独特的地方值得分享一下。尽管我说了「独特」,这不代表其它公司绝对不会这样做,有些公司有相似的文化,有时候相似的文化用力程度不一样得到的结果也不一样。

工程师对产品结果负责任

我见过很多互联网公司只看工程师产出的技术成果,实际产品结果或商业结果在考评中占的比例很大。Facebook 从高级工程师开始,考评主要看对产品结果的产出,而且有时候非常数据驱动。

考评只看技术的公司,或者是 Facebook 没到高级工程师的级别,只要把技术做极致了就行,产品得不到提醒,公司的商业没有变得更成功,工程师都不用负责任,锅可以甩给产品经理。一款新产品一个新功能做出来,代码可靠从不崩溃,性能优化做足从来不卡顿,界面跟设计师出的图完美吻合,等等等等,工程师就算是优秀的工程师了。如果团队的目标是提升用户留存率,这个技术上完美的新功能发布后留存率不仅仅没有上升还下降了,工程师不需要负责任,考评受罚的只有产品经理。

这种模式的问题是工程师和产品经理目标不一致,更容易产生利益冲突。工程师说「我就是要做这个技术上这么复杂的项目,否则晋升委员会觉得我技术不够不让我晋升」,产品经理说「这个项目消耗你很多时间还不太可能提升产品留存率,同样的时间你可以做好几个有可能提升留存率的功能了。」在极端情况下,这会让双方对立。我在百度时就见过一个例子,只是把工程师换成设计师:设计师说这样设计好,产品经理说那样设计好,双方都只是在利用主观判断说服对方,最后产品经理说「如果做你的设计,KPI 掉了你负责任;如果做我的设计,KPI 掉了我负责任。你想要对 KPI 负责任吗?」设计师立即闭嘴。

考评主要看产品结果的公司,把技术做到极致没用,产品完成指定目标才有用。如果团队的目标还是提升用户留存率,留存率提升了,达到预订目标了,工程师和产品经理(以及其它角色)都能考评过关,远超过预订目标的话还能获得奖励;留存率没有提升,或者是提升不够,工程师和产品经理的考评都会受罚。

工程师不能甩锅给产品经理,两者的目标高度重合。工程师知道,「产品经理指错路」不是借口,产品达不到预订的目标就要受罚。因此 Facebook 的工程师往往非常了解自己在做的产品的指标和数据,他们会花时间去看分析产品数据、阅读用户调研报告,而不是等待产品经理搞明白要做什么了再分配任务。如果工程师坚信产品经理指错路,那就必须自己找到正确的方向然后把结果做出来。没有结果就要受罚,可能是不及格的绩效,可能被炒掉,Facebook 不接受任何解释只看结果。

这种模式可以驱动一个产品团队坐下来好好合作,想方设法利用每一个成员的能力,因为只有这样才能达成目标。如果工程师觉得自己擅长做技术且只喜欢做技术,在这种环境下迟早被炒。所有人都在一条船上,要死一起死。「喜欢做什么」有时候就要给「什么必须要做成」让步,因为做不成船就沉了,大家一起死。

这种模式在自顶而下划分任务的公司不可能成功,只有鼓励自底而上解决问题的公司才能成功。自顶而下的公司,就如同只有船长有权向下发布命令,而且很多时候不需要解释命令的意图,所有船员都觉得自己只要执行命令就好,不需要关注其它人在做什么,不需要理解整艘船是如何运作的。如果船快要撞上冰山了,没有船员会觉得自己有能力和责任阻止船真的撞上去,因为只有船长知道船是怎么运作的,其它人只能无奈的等船沉没。

Facebook 的各级经理都鼓励下属自行定义「什么叫做成功」,如果经理觉得成功定义得合理,就放手让下属自己想办法把事情做成,不需要告诉下属「做什么才能成功」、「如何做才能成功」。如果产品团队主动提出「留存率增长 10% 叫做成功」,只要经理觉得这是对公司合理的贡献,产品团队自己想办法把留存里提升 10%。这样做才能要求产品经理和工程师一起对产品结果负责任,因为「当初是谁定义成功就是留存率增长 10% 的?」

基础架构被视为内部产品

有一些公司会强行在内部推广自己的基础架构。基础架构要进行不向下兼容的升级时,所有内部用户必须派人负责升级。据我所知 Google 就是这样做的,例如说 Google 的某个基础架构服务要从 1.0 升级到 2.0,但它们的 API 是互补兼容的,那用到这个服务的所有团队都必须安排工程师负责更新自己的代码,改为调用 2.0 的 API,否则 1.0 一旦下线这个团队就无法使用这个服务。

Facebook 正好是相反的,基础架构在一定程度是也被看作是产品,只是用户是内部用户而已。那作为产品,当然自己要想办法找到自己的用户,自己要想办法把用户留住,自己想办法扩大用户基数,等等等等。一个新的基础架构出来,没有名气,没有成功案例,必须自己想办法推广和招揽用户。潜在的客户可能说,「你这东西看起来不错,但我不是很确定用了是不是真的对完成我的目标有帮助,我也没空学习和使用你的东西」。遇到这种情况,Facebook 的基础架构团队就跟外面的 SaaS 服务提供商一样,要想办法讨好用户,例如说「没关系,我们团队派专人来帮助你们学习和使用我们的东西,觉得好用的话你们接着自己维护,不好用的话也不浪费你们资源」。

当初 React、React Native 等技术出来时也经历同样的过程,在没有名气之前作者就要想办法推广、说服别人来用自己的东西。外面只看到它们发布时的光环,没看到早期推广的各种困难。2016 年我曾经负责调研 React Native 是否适合用户增长部门使用,因为用户增长主要靠做实验,实验周期受客户端升级周期限制,React Native OTA 升级可以突破客户端升级周期限制。在我做完调研后,我给了大大的一个「NO」,因为 Facebook 主要的用户增长来源自 Android 而当时 React Native 的 Android 版性能(加载速度和内存消耗)实在是不行,和内部其它服务的整合也满地是坑。

基础架构不仅仅新品需要推广,升级也需要推广。「你们出个 2.0 跟 1.0 不兼容?我们一个高级工程师需要花三个月时间改写我们的代码才能兼容 2.0?那我们不升级了。你们准备准备暂停 1.0 的服务?没关系,我们自己出机器,我们自己跑一个 1.0 的实例,反正公司内所有组可以访问所有代码。你们准备把 1.0 代码改得面目全非?我们现在立即 fork 你们的代码!之后我们自行维护一个 1.0 的 fork。」

不向下兼容的基础架构升级往往还是要想方设法讨好已有用户。「你们可以继续用我们的 1.0 服务,但 2.0 性能更好哦。你们组的目标不是转化率吗?你们之前也做过实验,证明了转化率和性能高度相关,提升性能就能提升转化率。我们来算一下,提升这么多性能就能提升这么多的转化率,这绝对值得你们一个高级工程师花三个月来做啦。」工程师既要做销售又要做客服,实在不容易。

既然基础架构被视为内部产品,那产品有的压力基础架构也有。新的基础架构如果在一定时间内无法获得充足的用户,那就很可能被要求终止。已经垄断内部市场的基础架构也不能松懈,如果服务不稳定,内部用户不开心,他们会用脚投票。这些用户团队可能会自己做一个仅适用于他们的服务来取代你的基础架构,也可能有其它人做一个跟你竞争的基础架构然后趁机挖你的用户。

救火比防火更容易获得回报

Facebook 的文化有优点也有缺点。因为公司非常数据驱动,考评倾向于用数据说话,没有数据等于没有干活,完全不看你有没有苦劳,这导致防御性措施很难吸引人去做,因为成功阻止了坏事发生时你没办法收集数据说你成功阻止了多少件坏事、它们可能有多坏。

喜欢写测试的工程师进入 Facebook 往往会因为多写测试而受到惩罚。「你能证明这些测试实际带来的好处吗?你能量化这些好处吗?不能的话你还不如去做新功能,至少新功能能帮助提升产品指标。」不能量化的事情在 Facebook 是很难吸引人去做的。很多工程师技术非常好,但用数据讲故事的能力不行,这种事情就没办法做。

懂得用数据讲故事的话,好像测试这种不能直接量化的东西,可以找参考数据间接量化。「我们分析了去年的每一次事故并且确定其中 n 次是可以被测试防范的,考虑到今年我们团队多了 50% 的人,如果有测试的话我们今年可以防范了 1.5n 次事故。」如果经理认同这种推理的话,你就可以安心写测试了。(如果真的有人明年回来分析今年的事故的话,你最好能证明其中没有一次事故能被测试防范。」)

这种间接量化还是必须等坏事发生过了才能使用,如果你在坏事尚未发生之前就成功阻止了这一类坏事的发生,你没办法证明你工作的价值。这就导致了一个很不好的现象,我往往用以下的比喻来解释:

假想你是一个小镇的消防队队长,你四处去检查镇上有没有违反消防规范的地方,有你就叫负责人整改。这个小镇上所有人都狠你,见到你来检查消防就叫你滚,因为你给大家制造麻烦而没人觉得这些麻烦带来了任何好处。小镇的商户联合起来说每年这百分之几的成本都是你造成的,没有你的话就能有更高的利润。

在消防队队长这个职位上,你只能默默地等待火灾发生。一个房子烧着了你还不要着急去救,救了大家就会说「火灾的损失也很有限嘛,不知道花那么高的代价去防火」。你利用这个时间着手准备灭一场大火。等房子烧了一片了,小镇居民在路上奔跑呼喊了,你就去救火吧。(当然这一切的前提都是你真的有能力救火。)之后你要颁布什么新的消防规范,所有居民都会主动配合。

这就是为什么 Facebook 内部那么多问题处于起火状态,因为不起火就没有救火英雄。

互联网选大厂还是小厂:读了本书还是写了本书

作者 Cat Chen
2021年12月26日 14:20

这个系列上次写到《互联网选大厂还是小厂:数据中心里有朵云》,这次简短地写一个小一点的区别。在小公司,你更容易遇到同事说「我读了本书,书上说的这个事情能解决我们遇到的问题。我把书上说的事情投入实际应用后,得到了这么好的效果。」在大公司,你更有可能遇到同事说「我是这本书的作者,有问题欢迎来问我,我也可以定期组织相关的研讨。」

有资格写书的大神门往往聚集在大厂中,小厂很厉害的话或许能抢到一个这样的大神。这些人已经工作很多年了,积累也很深了,有心情就把知识整理出来写本书。科技理论的前沿往往发生在学术界,但科技落地的前沿是在大厂。大厂把学术界早就搞明白的事情拿过来,然后进行从来没人实践过的落地。在这方面,大厂提供了更多的机会去做探索。因为前人没做过,就算理论摆在那里,也不可能有书把这件事情描述清楚。

小厂正好是反面,走的路往往是无数前人已经走过的路,成长过程中遇到的问题很多其它公司早就遇到过,已经有人把解决问题的方法抽象出来写成书。这即是优点又是缺点,要看你站在什么角度去看。从创造影响力的角度来看,这是优点,一个应届生新人,不需要很强的原创性,不需要很好的研究能力,读本书就有机会为公司解决大问题,然后得到升职加薪之类的回报。在大厂,这是不可能的,写书的作者都在这里了,如果有利用这本书上的知识就能创造影响力的机会,早就已经做了,怎么可能轮到你呢?但在大厂里,你可以直接接触到作者哦,你可以跟他一起工作,直接从他身上学习,这是小厂没办法提供的。

面小厂就比面大厂更容易吗?

作者 Cat Chen
2021年9月19日 14:39

在我做 career coaching 的过程中,我发现不少人觉得「如果进不了大厂的话,那就降低一点目标,总能进小厂吧」,然后他们发现小厂 offer 也拿不到,于是怀疑自己能力是不是如此之差。大家从升学考试的经验出发,「进不了这一档次的学校就尝试下一档次的学校吧」,把这种逻辑推广到「进不了大厂就尝试小厂吧」看起来很合理,但很不幸人才和公司的匹配并不适用这种模式。


我时常用这样一个比喻来描述大厂和小厂招聘的本质区别:

大厂招聘,就如同 Walmart(沃尔玛)去采购两吨苹果用来上架。作为一家超市,Walmart 肯定有一套经过长期优化的采购标准,一个苹果只要满足采购标准 Walmart 就愿意要。Walmart 不在乎这些满足标准的苹果之间有什么个体差异,因为绝大部分都会被顾客买走,一小部分会卖不掉然后烂掉,但这早已在营收当中考虑到,Walmart 并不会亏钱。

小厂招聘,就如同个人消费者去买两个苹果。无论你个人对挑选苹果有什么准则,你肯定会按照你的准则挑选两个最好的。每个人的挑选准则都不太一样,如果有足够多的消费者,绝大部分的苹果都会被买走。

应聘时你就如同是一个苹果。如果你想被 Walmart 采购,你就要满足 Walmart 的采购标准。但因为 Walmart 是一个非常成熟的超市,所以采购标准相对稳定和可预测,反而更容易有针对性地做优化。如果你想被个人消费者挑选,反而很难抓住具体的某一个消费者让他挑你。每一个个人消费者的挑选准则都不太一样,你猜不到当前在挑苹果的这一位消费者是怎么想的,如果他不挑你你也没有办法,再等下一位消费者吧。这个过程比较看缘分。


为什么大厂和小厂的招聘模式会截然不同?

小厂招聘往往是针对一个或几个很具体的职位做的。举个例子,公司创业一开始还没找到产品方向,想要招一两个什么都懂但不用太精通的全栈工程师,创始人想要尝试做什么产品,熬个夜第二天就把原型做出来了。尝试了多个产品方向后,公司终于找到了一个靠谱的方向,开始缓慢地有用户积累,顺便招个前端工程师来打磨用户体验。用户增长持续加速,当初熬夜写出来那个单体后端已经支撑不住了,于是要招个有 micro-service 经验的后端工程师来重新设计后端架构,把后端拆成多个服务,并且为将来 sharding 做准备。服务拆了之后,单体数据库通过 replica 还撑了一段时间,终于也要不行了,需要赶紧招一个精通数据库优化的程序员。小厂每时每刻面临的问题都不一样,用固定的标准招通才并不能有效解决问题。

大厂可以用固定的标准招通才,尤其是 Facebook 和 Google 这种全公司统一招聘的。虽然大厂内的每一个团队都好像小厂一样,拥有此时此刻特定的招聘需求,但因为团队数量足够多,所以在统计学上大厂可以无视这些差异按照一个标准来招人。由于大厂的招聘标准如此的稳定和可预测,针对大厂的招聘标准做准备反而更容易。你可以尝试搞清楚大厂的招聘标准,搞清楚后这套标准不会变来变去。一旦你满足了这套标准,你不需要太过关心个体差异。

如果你达不到大厂的标准,要参与小厂的招聘,那马上就会变成跟上述苹果比喻一样看缘分。你不知道具体哪个小厂正好需要你,你要明白到大部分的小厂当前的需求都跟你不匹配,从统计学的角度来说你必须通过大量的小厂去找一个匹配的。这时候你的体验会变得完全不一样,从只需要针对几个大厂做准备变成需要跟很多小厂打交道。


从升学考试经验推导而来的期望在这时候完全不成立。如果你高考差几分上不了北大清华,你很可能还是能去非常好的学校。但在找工作时,差一点进不了大厂并不意味着马上有小厂意识到你的价值把你招走,你需要在茫茫大海中搜索跟你匹配的小厂。这时候设置正确的期望很重要,大量小厂跟你擦肩而过,但这不代表你能力有问题,你需要坚持下去。

小厂的招聘流程并不像大厂那样标准化和可预测,那意味着他们扔掉你简历时并不一定是你简历有问题;他们面试时不像大厂一样按套路出题,他们会针对特定的招聘需求来出题,所以你答不上来不一定是你能力有问题;经验有限导致他们的面试并不一定能有效挑选人才,你觉得自己面得很好但他们不要你,同样不一定是你能力有问题。这个过程的负反馈可能比应聘大厂难受很多,你唯一能选择的就是坚持住继续尝试。

AlgoTogether 算法面试小班(信息更新)

作者 Cat Chen
2020年10月30日 08:17

我们三个月前发布的招生信息仍然有效,现在再补充一些新信息。

免费学习材料

我们不提供试听课,但我们免费提供一部分的学习材料,方便大家来了解我们的教学风格。这里面包括两段教学视频,和两道 LeetCode 中等难度题目的解题思路文档。大家可以打开以下链接填写你的基本信息后获得上述免费学习材料:

https://techcareer.typeform.com/to/IKKYnObZ

如果看完免费学习材料后觉得我们的教学风格对你胃口的话,请尽快来付费报名哦,因为我们的第四期马上就要开始了。

教练及助教信息

我们的教练团队越来越大,并且新增了助教角色,所以必须向大家公布一下我们最新的团队信息。以下是我们详细的教练信息:

  • Wilson: ACM/ICPC 金牌选手,毕业后回校训练学生并带队参赛。在 FANG 有多年的面试官经验。
  • Cat: 从小学开始参加编程竞赛,一直参赛到高中获取保送。在 FANG 面试 ~200 名候选人并培养了 ~50 名新面试官。
  • Michael: 在 FANG 拥有丰富经验的面试官。

除了一流的教练外,我们还有非常优秀的助教:

  • Leaf: AlgoTogether 优秀毕业生,成功从非科技行业转入互联网行业。
  • Tiger: 在读计算机博士生,曾在 FANG 实习。

学生评价

以下是我们收到的学生评价,大家可以参考一下,看看我们这个班的强项是否对你有帮助。

jhs:

第二期学员反馈:1. 题目分类清晰内容丰富,一轮项目结束以后对不同类型的算法题会建立起比较直接的大脑反应。对于准备刚开始刷题或者刷了一段时间题摸不清套路的同学很有帮助,对我个人的提升是解题速度更快了。 2. 比较喜欢 mock interview 的环节,能够最大限度的锻炼你 think loud 的习惯,这对于在北美求职的同学来说,这种习惯可能会让你在面试中更有优势。 3. 教练经验丰富,回答很多公司招聘时的实际问题。而这些问题很多时候只有一定经验的面试官才会有,对于求职者来说非常有帮助。

Tianqiang:

第二期学员。之前在知乎上有看到这个项目的介绍,比较感兴趣就报了一期。当时 Leetcode 刷了差不多 200+。项目每周总结的 topic 的题目做下来之后归纳总结,后面碰到类似的很快就有思路。我个人觉得帮助最大的是 Mock Interview,现实生活中很难找到有大厂招人经验的人来 mock,全英文交流以及白板或 codeshare 环境和 leetcode 刷题的感觉完全不同,重点在于与面试官的沟通与交流,习惯后真正面试时也不会过于紧张。推荐刷了很多题但是还不够自信面试的同学尝试这个项目。

Yuqing:

我是第二期的学员,算法知识方面的帮助很多同学都有提到,我也不再多说了,除此之外我觉得这个项目最大的亮点是每周的 presentation 和 mock 都可以得到大厂面试官的点评。面试的时候可能题会做但是怎么能清晰的表达出来自己的想法,把自己的算法和面试官讲明白同样也很重要。在这个项目中能知道专业的面试官希望听到什么样的回答,怎么能更有效的和面试官沟通,这一点是很多其他算法班没办法做到的,对我的帮助也非常大。

FlynnGao:

作为第二期的学员,说一下感受。首先一个给课程结构一个好评,本身预期以为有算法学习讲课,但实质通过模拟面试的讲解和更注重整个面试流程的各种问题,也可以从另外一个方面更加深入理解算法题目。面试过程中可能遇到的各种问题都有解答,有更加全面理解面试流程的效果。算法题的选择上个人觉得整体中等偏简单一些,当然也是为了更加方便整合整个模拟面试流程。对于学员的各种问题回答和表现的评估,AlgoTogether 的几位老师也表现出色。顶着时差基本全部的课都上完了,我给到 85 分。

罗凯:

第一期学员发表一下感想。1.题目归纳的很有条理,基本上跟着项目走下来 leetcode 上大部分题目都有思路了。 2.教练业界经验很丰富,回答了很多关于实际工作的问题,也给出了很多代码风格如何优化的建议。3.同学都很认真,每周一次 mock 的时候听同学讲也收获挺多的。很多题目的想法是 leetcode discussion 里面也没有的。教练也非常有耐心,有一次周二晚上讲一个比较难的题目,一直讨论了快半小时,我这边东边都 11 点多了。4.项目每周大概十个题,要都弄懂还是要花点时间的。做 mock 对真实面试帮助挺大,需要自己思路清晰,代码能跑到把代码讲清楚这之间也是有距离的,需要练习一下。

SKY:

作为第一期的学员,还是非常推荐这个项目的。教练很上心水平也很高,有问必答。每周的资料都很有针对性,涵盖面很广。最后一周还有教练自己收集总结的的最新大厂 OA 以及面试题,受益匪浅,在这里说一声谢谢。

邮件咨询

如果你看到这里了都还犹豫不决,那你发一封邮件给我进行咨询吧,在邮件里说一下你找工作的计划、你对找工作的决心有多大、你觉得现在的瓶颈在哪里,然后附件发送你的简历(或粘贴你的 LinkedIn 地址)。我会回信跟你分析一下你的现状,给你一些建议。

algotogether@techcareer.io

AlgoTogether 算法面试小班(第四期招生)

作者 Cat Chen
2020年8月24日 06:49

AlgoTogether 上周日宣布第三期招生,不到一周时间就全部报满了!一开始我们开放了 20 个名额,结果到周五晚上就全没了。周五一天之内有 8 个人报了名,把剩下的名额都抢光了。因为还有学生特别想要报名,周五没抢到名额就跟我联系,我周六悄悄加了 5 个名额上去,当天就消耗完了。

考虑到 AlgoTogether 如此热门,仍然有学生想要报名,我决定立即开放第四期的报名和支付。第四期预计 10 月 31 日开始,到明年 1 月 2 日结束。课程格式跟第三期相似,但会在第三期快要结束时根据第三期收到的反馈做轻微调整。如果你原本想要报名第三期但错过了,可以点击以下链接报名参加第四期:

https://techcareer.io/programs/algotogether

如果你还不是很了解 AlgoTogether,以下是第四期的详细信息如下:

这门课程适合什么人?

这门课程适合下定决心要在美国一流科技公司找一份软件工程是(Software Engineer)工作的人。这门课程对于首次找软件工程师工作的人来说价值最大,例如说应届生或从其它行业转专业过来的人。找全职实习都适用。对于有工作经验的软件工程师来说,如果最近一年没有做过面试的准备,通过这门课程来准备面试也是一个很好的选择。

这门课程的价值在哪里?

我们的核心价值是「授人以渔」,因此课程的价值如下:

  1. 学会以面试官的角度进行思考。在面试过程中,公司对你的评价不仅仅在于代码的正确性和性能优化。我们会向你解释面试官是如何评价面试者的,这是你不可能通过 LeetCode 学习和训练的技能,你学会这些技能后就可以用面试官的视角审视自己,找到自己应该提高的地方。
  2. 基于面试场景的训练模式。LeetCode 是训练对着机器做题,面试需要的是对着人解题。我们假设你对前者已经有一定的经验,着重训练后者。后者涉及的技能包括:如果通过提问理清面试官的需求、如何清晰地陈述自己的思路、如何接受面试官的反馈和提示。
  3. 给自己下一个破釜沉舟的决心。如果你想要下定决心,在限定时间内做完指定数量的题目,同时完成指定数量题目讲解和模拟面试,我们会保证你对你自己定下的目标负责任。
  4. 理解解题思路而非背诵面经和答案。一知半解就去大量做题的话会导致背诵面经和答案的现象。我们强调学习和理解解题思路,然后通过适量的练习来学会如何灵活运用。以不变的解题思路去应付不断变化的面试题目。

课程结构是怎么样的?

整个课程总共 9 个星期的时间。每个星期我们有 3 个天会进行 Zoom 视频会议,另外 4 天进行线下各自的练习和 Slack 上的交流。

每周教练挑一个主题,例如说动态规划(Dynamic Programming),然后围绕这个主题布置 15 道作业题。你有一周的时间来完成这些题目并且提交结果,你至少需要提交 10 道题目的代码和 1 道题目的详细解题思路。所有学生都完成提交后,助教会组织大家投票选出最优秀的解题思路,并且安排题目讲解(Presentation)和模拟面试(Mock Interview)的时间和人选。教练会在整个过程中针对学生表现提供反馈。

我们每周三、周六和周日晚上 7 点(Pacific Time)通过 Zoom 视频会议进行教学、答疑 、题目讲解和模拟面试。不进行 Zoom 视频会议的日子每天同样时间在 Slack 上进行专题讨论。

课程教练是什么人?

我们的教练都在一流科技公司拥有多年的工作经验和面试官经验。我们的首席教练曾多次获得 ACM/ICPC 竞赛奖牌,此外还培训其它学生参赛获奖。我们的助教团队都有一流科技公司的工作经验,且近期经历了面试找工作的流程,非常能理解正在经历这个过程的学生需要什么。

课程使用什么语言教学?

为了保证跟面试过程和工作环境一致,整个 AlgoTogether 采用全英语沟通。学生毕业后,可以加入微信校友群,使用中文沟通。

如何报名和支付?

请打开以下链接然后选择第四期(10 月 31 日到 1 月 2 日)进行报名和支付:

https://techcareer.io/programs/algotogether

如果你看到这篇文章时已经错过了第四期,可以打开链接然后选择你需要的那一期进行报名和支付。

《牛油果烤面包》回顾(Part 5 - 中国)

作者 Cat Chen
2020年6月30日 10:14

我在这个系列的第一篇文章里面就说到过,当初为了用最低的成本和最短的时间把节目推上线,我们选择了使用 Anchor 这个平台做发布。它如同 YouTube 一样免费和易用,当然也如同 YouTube 一样无法在中国访问。在节目上线后我们很快就意识到这个问题,于是费了很大功夫进行调整。


喜马拉雅是我们的第一个尝试,因为它是国内的平台所以它有义务保证内容可以在国内访问到。在得知 Anchor 在中国无法访问时,我们第一时间去注册了喜马拉雅,并且把我们的节目同步发布到喜马拉雅。之后斯图亚特还去注册了企鹅 FM 和荔枝 FM,但因为手工多平台发布的成本高,但又收获不了多少收听次数,最终放弃了。现在国内的平台就只做喜马拉雅。

提到喜马拉雅就不得不提一下 Apple Podcasts 在中国那一套心照不宣的特殊做法。如果你正常地使用 Podcasts Connect 提交一个播客的 RSS,默认这个节目不会出现在中国区。Apple 通知你节目通过审核时,这仅仅是意味着节目通过了 RSS 格式审核,但这跟中国一点关系都没有。之后 Apple 会默默地审核你的内容,决定你是否能发布到中国区,整个过程不会有任何的沟通,审核不通过也无法上诉。因为我们一开始使用的是 Anchor,它的 RSS 在中国根本无法访问,自然 Apple 不会把我们的节目发布到中国区。

想要把节目发布到 Apple Podcasts 的中国区,民间有一个办法,那就是用喜马拉雅导出的 RSS。因为喜马拉雅本身在中国,所以 Apple 乐意发布任何来自喜马拉雅的 RSS。为此我们专门在喜马拉雅后台申请了导出 RSS,然后把 RSS 添加到 Podcasts Connect。为了避免听众看到我们的 Apple Podcasts 上有两个节目分不清哪个是哪个,我们把喜马拉雅导出的那个叫做牛油果烤面包中国版


考虑到喜马拉雅不是一个泛用型播客平台,为了方便中国的听众使用 Overcast、Pocket Casts、Castro 等泛用型播客应用收听,我们又花钱购买了 Typlog 的服务并且把每一集的内容同步发布到 Typlog。作为付费平台,Typlog 的灵活性比 Anchor 和喜马拉雅都要好很多,而且大多数时候能在中国访问到,不过也有极少数时候用户会报告不能访问。

Typlog 值得一提的是节目文本内容的编辑灵活性。Anchor 和喜马拉雅对内容都是有限制的:Anchor 不允许多层套叠的 bullet points,如果我们写了多层的就必须手工改为一层的。喜马拉雅连 bullet points 都不支持,只能人手在每一行文本前加个星号表示这几行是个 bullet points。Typlog 完全没有这些限制,而且可以用 Markdown 语法来写,非常符合我的编辑习惯。

在建立好 Typlog 发布后,我们就逐步把各个平台收录的 RSS 地址从 Anchor 改为 Typlog 了。这样做是为了保证中国听众就算是通过 Overcast、Pocket Casts、Castro 等平台搜索到我们节目后能正常收听。可惜最近发生了一系列事情,使得很多这些泛用型播客应用被 Apple 中国区下架了,估计这些应用的中国用户将来只会变得越来越少。


最后,为了保证我们的品牌、域名和链接掌握在我们手上,不受任何一个发布平台的干预和限制,我选择了建立我们自己的网站。我们的域名 avocadotoast.live 最初只是简单地指向到我们的 Anchor 页面或喜马拉雅页面(根据访问来源国家智能指向),但这样子我们得不到任何网站分析数据,不知道有多少来自中国的用户因为指向错误而打不开 Anchor。为此我决定利用静态网站生成器编写我们自己的网站,每一集节目都要能在我们的网站上打开和播放。

网站的主体是我一个周末连夜赶工做出来的,之后花了不少时间打磨和提升自动化程度。现在只要我们发布到 Typlog、喜马拉雅或 Anchor,我们的网站就会自动更新,保证最新一集总能在我们自己的网站上看到。为了兼容中国和非中国用户的体验,我们的网站还设计为从多个音频来源进行播放,如果其中一个音频来源播放失败就自动切换到下一个音频来源。这样就算中国听众无法访问某些音频来源,最终还是会切换到喜马拉雅的音频来源从而保证播放。

制作这个网站的详细技术抉择我觉得可以单独写一个系列,就不在这里详细展开。有兴趣了解这个项目背后细节的话,请通过邮件RSS/Atom 进行订阅。如果我接下来还有时间继续写这个系列的话,我会写一写我们进行远程录音的经验。尽管我之前已经写过录音剪辑经验,但现在大家不能面对面录音了所以我们又摸索了一套新方法来做远程录音。这个话题我们下次再聊。

《牛油果烤面包》回顾(Part 4 - 选题)

作者 Cat Chen
2020年6月29日 14:39

之前写了 3 篇《牛油果烤面包》的满月回顾(1 2 3),现在准备继续写下去。当然满月早就过了,甚至连半年都不止了,然而又没到一周年,所以文章标题就不再提及时间啦。上次我们说到了我们从个大平台都能获取到什么样的收听数据,那这次我们就说一说我们是怎么做选题的吧。

要说选题,那必须先说一下我们的受众定位。这在我们几位主播之间其实没有非常统一的观点,我们有人更在乎在美国的中文听众,也有人更在乎来自中国的听众。来自中国的听众又可以细分为更多的类型:有些是已经接触到比较多英文信息和海外信息的,那就比较类似在美国的中文听众;有些技术研究得很深入但只熟悉中文的术语,遇到英文的就比较难听进去;有些技术了解不深,希望多听听浅显易懂的科普内容,对深入话题不感兴趣。因为我们在这方面没有统一,所以选题时也没有刻意针对哪一种中文听众类型来做,每一期的受众会略微不一样。


我们选题一般会有两种风格:一种是侧重专业性的,一种是侧重娱乐性的。前者的目标是让听众爽,后者更多是主播和嘉宾自己爽。

我们先说说前者吧,我们在做这类选题时一般优先从我们身边的朋友里找专业人士,想想他们熟悉的专业内容如何能够塑造成相对科普一点的话题,保证讲述过程中的趣味性和故事性。然后我们就会跟这些朋友说,希望找他们来录一期节目,问一下他们是否愿意,然后再协商什么能说什么不能说。因为他们工作领域和公司的限制,可能有些事情他们能多说,有些事情他们不能说,这些都是要提前说好的。

因为我们强调自己是一个科技性的节目,所以我们找的很多专业人士都是自己的同行,也就是科技行业的从业人员。推荐系统人工智能硬件加速人工智能在传统行业的落地搜索引擎计算机视觉激光雷达都属于这种类型的节目。当然我们身边也有不少朋友在非科技领域有非常深入的见解,我们也会请他们来介绍他们的专业。私人飞行执照美国公务员美国个税疾控中心Airbnb 短租房东桌游电子游戏都属于这种类型的节目,因为作为主播我们自己也不是很懂,所以录制节目的过程也是我们自己学习的过程。

至于娱乐性节目,主要就是我们自己想要说,说完了还想要分享给大家听,所以我们就录了。这方面的选题是非常随性的,总之有主播想要录,我们有档期就录。这类型的节目包括互联网信息获取在家办公台北旅游托斯卡纳旅游伊朗旅游。此外还有一些选题是介于专业性和娱乐性之间的,我们做不到非常有深度的专业内容,但我们觉得嘉宾有独到的经历和见解非常值得分享,于是我们就会去录:中美互联网公司异同美国裁员冻卵硅谷春晚

最后还有一类特殊的选题,叫做蹭热点。我们还是希望我们的节目能够获得快速增长的,而蹭热点有时候是个很有效的办法,虽然不一定能成功。(这是我在知乎回答问题的经验啦,回顾了一下得票最多的答案,有一部分是蹭热点而来的。)这部分选题的时效性非常强,某个事件发生后必须尽快完成录音、制作和发布。我们的第一期节目iPhone 11 发布会就是这种类型,后面还有BlizzCon、CES(1 2)以及下周马上要发布的 WWDC。


回顾我们的选题,从播放量来看最可靠的是专业性题材的节目。这些节目我们可以持续做,只要我们能够一直找到新的专业话题,在一定程度上来说我们可以「量产」这种类型的节目,而且每一集都会有比较高的播放次数,但并不是最高的播放次数。那最高的播放次数来自什么呢?这需要的不是专业见解,而是情绪上的刺激。要让听众一看到题目就能产生情绪上的反应,以下是一些例子:

  • 美国裁员这一集刺激的是大家恐惧的情绪。在美国拿着工作签证的人都知道,一旦被裁员那就有可能被迫离开美国,之前那么多年投资在留学和工作上,就差那么一点拿不到绿卡是一个巨大的损失。这一集的播放次数非常高。
  • 中美互联网公司差异成功的刺激到了中国程序员对行业内卷的负面情绪,大家都想了解一下其它国家的互联网行业从业体验是怎样的,所以播放次数也很好。
  • 搜索引擎通过在标题中提及百度的没落而刺激到了中国网民对百度的厌恶情绪,很多人不仅仅收听了这一集的节目,还在评论种表达了对百度的不满。

有意思的是,人类确实更容易受负面情绪的驱动,在收听播客时也如此。这是现代市场推广所才去的一个常见手段,当然最终会导致民众两极分化,这又是一个新的问题。我们在选题时不会刻意的去选择挑起负面情绪的话题,但从结果上来看确实这样的话题效果更好。

为什么美国网银转账那么慢:ACH 详解

作者 Cat Chen
2020年6月22日 15:22

我刚刚来到美国时就发现美国网银的转账速度很慢,往往中国网银一天甚至实时能完成的转账,在美国需要三天甚至更久,不过后来习惯了也就没再仔细思考这件事情。我最近加入了 Robinhood 的 Funding 团队,我们团队负责用户资金转进、转出 Robinhood 账户,为此我好好学习了一下美国网银转账背后所使用的 ACH Transfer 机制。现在终于可以来向大家解释一下为什么美国银行的转账速度那么慢了。

我这篇文章主要参考了 Gusto 关于 ACH 的系列博客(1 2 3 4 5),大家喜欢看原文的话可以去看看。在开始之前,我们来解释一下下文要用到的概念:

  • ACH:Automatic Clearing House。这是一套自动清算系统,是金融机构之间使用计算机进行自动清算的协议。
  • NACHA:National Automated Clearing House Association。这是一个制订 ACH 标准协议的机构,并且负责管理 ACH 网络。
  • Federal Reserve。美联储,实际运营 ACH 网络的机构。
  • ODFI:Originating Depository Financial Institution。这是代表发起 ACH 操作一方的发起方银行。
  • RDFI:Receiving Depository Financial Institution。这是代表 ACH 操作接受一方的接受方银行。

在美国的金融机构之间进行金额不是很大的转账,往往使用的都是 ACH。一个 ACH 操作中通常会涉及到 5 个参与者,他们分别是:发起方、发起方银行(ODFI)、美联储(Federal Reserve)、接受方银行(RDFI)、接受方。举个例子,Alice 是 Bob 的雇主,Alice 要发工资给 Bob,那么 Alice 就是发起方,Alice 的银行就是发起方银行,Bob 的银行就是接受方银行,Bob 就是接受方。

假设 Alice 发工资给 Bob 的那天算作第 1 天,Alice 的银行会要求 Alice 在特定时间(假设是 7:00 PM)之前发起这个操作,超过这个时间就算是下一个工作日的操作了。为此 Alice 必须在第一天的 7:00 PM 前发起一笔 ACH 操作转出 Bob 的工资,与此同时 Alice 的银行会在 Alic 的账户余额上减去 Bob 的工资。这是第一步。

Alice 的银行(也就是发起方银行)会在午夜把记录了这一笔 ACH 操作的 ACH 文件发给美联储,然后美联储把这个 ACH 文件转发给 Bob 的银行(也就是接受方银行)。这是第二步。

Bob 的银行会在第 2 天清早(假设是 5:00 AM)处理这一笔 ACH 操作,把工资加到 Bob 的账户余额上。这是第三步。如果一切顺利的话,事情到这里也就结束了,工资成功从 Alice 的账户转到 Bob 的账户上。但是如果发生异常的话,那就还有两步要走。

什么情况可能导致异常呢?Bob 可能填错账户号码了,Bob 可能把 checking account 错误标记为 savings account 了,Bob 还可能注销账户了,有各种原因可能导致异常。这些都叫做 ACH Return,会导致一笔 ACH 操作失败。值得注意的是,跟中国的网银不一样,Alice 作为发起方不仅仅可以转钱给 Bob,只要 Bob 签署了正确的 ACH 授权,Alice 还可以要求从 Bob 那里收钱回来。这时候 Bob 余额不足或者是 Bob 撤销授权等等的情况,都会导致异常。

在第三步里,Bob 的银行在第 2 天清早会处理这一笔 ACH 操作,如果它发现有异常的话它可以把 ACH Return 发给美联储,但这个 ACH Return 最晚可以在第 3 天结束之前发出。按照最坏的情况考虑,ACH Return 会在第 3 天和第 4 天之间的午夜由 Bob 的银行经过美联储发送给 Alice 的银行。这是第四步。

Alice 的银行会在第 4 天清早(再次假设是 5:00 AM)处理这一笔 ACH Return,然后把 Bob 的工资归还到 Alice 的账户余额上。这是第五步。整个过程耗费了 3 天的时间,准确来说是 3 个银行工作日(bank day)的时间。

一般来说,Alice 发工资给 Bob,如果 Bob 的银行没发生异常,Bob 是不会提出异议的,毕竟钱是多了而不是少了。但如果 Alice 发起的 ACH 操作是从 Bob 那里收钱,Bob 的银行没有遇到异常不代表 Bob 不会提出异议。跟 Bob 的银行不一样,Bob 本人有 60 天的时间提出异议(dispute),然后就会导致 ACH Return。Alice 的银行收到 ACH Return 之后,就要把已经给了 Alice 的钱拿回去。如果 Alice 的账户已经没有那么多钱了,Alice 的银行就赔钱了。

因此 Alice 的银行进行 ACH 操作向 Bob 收钱后,收回来的钱该不该给 Alice,是全额个 Alice 还是部分给 Alice,具体什么时候给,这些都是开放性问题,不同的银行会做不同的策略。有些银行会等到第 4 天才把钱给 Alice,因为第 3 天结束后 Bob 的银行就不再可能因为异常(如 Bob 账户余额不足)而返回 ACH Return 了。

有些银行会因为 Alice 长期的良好表现而信任 Alice,在第 2 天就提前把全额或部分给 Alice。如果金额比较大,Alice 的银行担心 Bob 在第 3 天结束后提出异议,那 Alice 的银行还可能要进行更多的沟通协调,向 Bob 的银行确认这真的是 Bob 授权了的,那就会拖更长的时间。


希望上述信息能够解释清楚为什么美国的网银转账这么慢。

如果大家对 ACH 文件的格式感兴趣的话,可以读一读这篇文章,里面做了简单介绍。实际的文件格式,请以 NACHA 官方手册为准,那是一本几厘米厚的砖头书籍,我们 Funding 团队就有一本今年最新版本的。

System Deep Dive 系统设计小班

作者 Cat Chen
2020年2月12日 05:38

System Deep Dive 系统设计小班

我开了一个叫做 System Deep Dive 的系统设计小班,面向在美国寻求软件工程师工作的人。系统设计面试跟算法编码面试有相似的地方也有不一样的地方。相似的地方是,你都需要让面试官想要和你共事,这需要说服他你能跟他一起解决复杂系统的设计难题。不一样的地方在于,系统设计问题没有标准答案,不存在 LeetCode 这样的系统为你的训练提供实时反馈。

System Deep Dive 采用教练带领学生探索复杂系统的小班教学模式,帮助你接触常见的系统设计面试题,模拟真实面试过程——鼓励你多提问题了解系统需求,引导你把问题分解成子问题,给机会你独立解决具体子问题。教练会对你的解题过程提供反馈,最后还会展示部分系统代码,帮助你加深对设计难点的理解。

我们的教练曾在大型科技公司中担任 tech lead,有丰富的系统设计经验和面试经验。此外他还创办了自己的 startup,帮助其它科技公司通过实战项目筛选候选人。我们保证你投入到面试准备的每一滴汗水都能有充分的回报。

为了保证跟面试和工作环境一致,整个 System Deep Dive 采用全英语沟通。以下是 System Deep Dive 的详细信息及报名链接:

What is this program?

System Deep Dive is a system design practice group with a coach. The program not only teaches you all the necessary skills to excel at a system design interview but also practices designing complex systems together. In each session, the coach would lead the students to dive deep into a complex system problem and explore different design choices together. Students are encouraged to ask questions and deliberate trade-offs because that’s the best way to learn.

What is the value of this program?

  • Get a feel for the real system design questions asked in interviews. We’ll pick the most popular system design questions asked in industry and elaborate on different paths they lead to. You will learn how to design a real system. We’ll explain in-depth about the usual structure to answer such design questions.
  • You will ask questions and lead discussions like in a real interview. System design interviews are open-ended by nature. In a real interview, candidates will do most of the talks. We will tackle the problem together as a team and you will ask questions and get answers from the coach or other students. In this way, you can understand deeply and learn the trade-offs better among different design choices.
  • You can see how a complex system works in real life. Interviewers often dive deep into the system design to see whether you were actually involved in the project. You might get stuck if you haven’t seen the real thing. We will demo some code and configuration of different system components. In this way, you will understand better, for example, what load balancer does and how the cache is built.

Who is the program for?

People who are committed to getting a software engineer job, have coding experience but lack the experience of designing a complex system.

What is the structure of the program?

This program is structured session by session. Each session will take 2 hours talking about one or two similar system design questions in depth. There will be 4 parts in a session:

  1. In the first part, you’ll be asked to discuss and walk your thoughts out for the question just like in an interview.
  2. In the second part, the coach would challenge you and guide you through different directions of the problem. Students are also encouraged to discuss and it’s a perfect time to explore the pros and cons of design choices.
  3. In the third part, the coach will talk about the common strategy to tackle such a design problem so you can learn the framework tackling it yourself.
  4. Lastly, the coach will demo with real code to illustrate different parts of the system in real life.

Who is the coach?

Our coach has many years of experience designing complex systems and interviewing candidates at well-established tech companies. He’s also the CEO and founder of a startup specialized in helping tech companies hire engineers through practical projects.

How much does it cost?

We’ll be running a series of sessions as a pilot program and charge by each session. A session will cost $200 with a limit of 8 students. Once a session is full, we’ll stop accepting registration and move candidates to the same session of a later date.

What sessions are available?

We will offer the following sessions in the pilot program:

  1. System Design Interview Skills: understanding the problem, breaking it down, exploring each subproblem, deliberating trade-offs and articulating decisions. (3/4 7pm–9pm Pacific Time)
  2. Design an Autocomplete. (3/11 7pm–9pm Pacific Time)
  3. Design a Facebook/Instagram/Twitter Newsfeed. (3/18 7pm–9pm Pacific Time)
  4. Design a Customer Service Center. (3/25 7pm–9pm Pacific Time)

We plan to offer each session once in the pilot program. We will adjust them based on feedback and repeat these sessions after the pilot program.

How do I sign up?

Use the following link to sign up for the pilot program and pick the sessions you are interested in:

https://chen.cat/system-deep-dive-pilot-program-signup

If you are interested in the sessions after the pilot program (or if you are reading this after the pilot program signup is closed), sign up for the wait list with the following link:

https://mailchimp.catchen.me/system-deep-dive-wait-list

We will contact you through email after you signed up.

AlgoExpert 测评

作者 Cat Chen
2020年1月30日 02:36

AlgoExpert 是一个我称之为「LeetCode 精选」的服务,因为 LeetCode 现在的题目实在是太多了,但解答、提示等内容的质量又参差不齐。AlgoExpert 解决了这些问题:首先,它提供不到 100 道算法题,覆盖不同难度和不同知识点;其次,每道题都有一系列循序渐进的提示,使得你可以从毫无头绪开始一步一步做到最优解;最后,每道题最后都有视频讲解,从最笨的解法开始一步一步讲解如何优化,最后达成最优解。

因为我不需要通过刷题来准备面试,所以我买了之后并没有真的去做题,只是打开题目想想自己会怎么做,然后看看提示验证一下自己的想法。我有测试过用它的界面来写代码,感觉还不错。以下是我试用后的评价:

优点

  • 模拟面试过程中的提示,而且是每一道题都有提示,不像 LeetCode 那样有些题目没有提示。我觉得提示是模拟真实面试的总要环节,因为在真实的面试过程中你也可能会卡住,但一旦获得一个小提示后你就能解决剩余的部分。
  • 内置编码环境,支持常见的语言(C#、C++、Go、Java、JavaScript、Python、Switch)。
  • 内置测试环境,可以跑自己的测试数据,也可以查看官方的测试数据。
  • 视频讲解多种解法并对比它们的时间、空间复杂度。

缺点

  • 视频讲解比较慢。(不过内置视频播放器可以用 1.5x 或 2x 速度播放视频。)
  • 支持的语言不如 LeetCode 多(缺了 C、Ruby、Scala、Kotlin、Rust、PHP)。

如果是为了找工作而刷题,我觉得 AlgoExpert 是一个比 LeetCode Premium 更好的选择。AlgoExpert 是 $85 一年,但 LeetCode Premium 需要 $159 一年。尽管 LeetCode Premium 提供的内容更多,但如果你不准备花时间做那么多题其实没用。而且我觉得通过 LeetCode 把各大公司常见面试题死记硬背一遍不是正确的做法,我更倾向于 AlgoExpert 这样子的,用少量题把不同的知识点覆盖到了,然后学会在面试时灵活变通更重要。

如果你想要买 AlgoExpert,你可以考虑用我的折扣码。点击以下链接打开 AlgoExpert,然后记得在付费前在「promo code」一栏输入「catchen」,然后就可以享受八五折。请记得一定要输入「catchen」这个折扣码,单纯使用链接打开是不会自动打折的。

https://chen.cat/algoexpert-referral

❌
❌