编程篇

面试就好像是在第一次约会的时候就要决定是不是要结婚

这是一个真实的笑话。在每日高强度的工作当中,我们和一些同事相处的时间甚至会比家人还要长。所以面试是工作中十分重要的一个环节。

正文开始之前,请你先想像一个场景:有一天,你的老板找到你,让你和另一名工程师合作完成一个项目,那么你希望这名的工程师具有什么样的品质?开发速度快(coding machine)?代码清晰易懂?接口易用?系统搞设计能力强?知识渊博?靠谱?有激情?容易相处?自我驱动力强?或者以上所有?那么你所想看到的优点也就同样是你在面试时候需要考察的重点。面试首先要明确目标,再通过目标反向推导出面试的考察点和内容。硅谷大公司的面试分为三种类型:编程面试,系统设计面试,行为面试。一般在编程面试或者系统设计面试中不会涉及行为面试,但是在行为面试中可能会加入简单的系统设计以及编程。本文主要分享的是编程面试的经验总结,所以不会涉及过多的系统设计或者行为面试的内容。其他类型的面试会在之后的文章中讨论。

编程面试中我们应该考察面试者的什么能力呢?

编程面试主要测试面试者的如下几点能力和品质:解决复杂问题的能力,沟通能力,编程能力,调试能力,测试能力,知识储备等。除此之外,面试者的态度,习惯等也在考察范围之内。

解决问题:解决问题的能力包括是否能把复杂问题简单化,比如把一个大的问题分解成若干个小的问题。面对复杂问题的时候,面试者能否先想出一个简单的解法而非最优的解法。在工作中,我们经常会遇到复杂的项目和问题,需要工程师把问题分解,先想出一个方案可以快速满足需求。

沟通能力:沟通能力十分重要。我相信很多人都经历过因为沟通中出现的问题导致项目进展不顺利或者做了无用的努力。在系统设计和行为面试两轮会对面试者的沟通能力有更全面的评估,代码轮更多的是考察面试者解释思路的能力。

编程能力:这个涉及的范围就非常广泛了:代码的正确性,结构是否清晰,设计是否合理,编程习惯是否良好,代码是否易读,速度是否快。对于代码的要求和在平时工作当中是一样的,并不会因为是在面试而降低要求。

调试能力:我很少见到有人在面试的时候能把代码一次写对,编译和运行的过程当中总会有错误。我们需要每一位工程师都有能快速找出代码问题的能力。

测试能力:这里最重要的一点是能否找出所有的edge case以提高测试的覆盖率。我们每天在做代码审查的时候会尽量考虑到所有可能的情况。然而现实中,大部分的edge case都是由代码编写者发现并处理。所以写好测试的能力是十分重要的。

什么样的题目算是一个好的面试题?

一个好的面试题应该能够考察面试者以上所有的能力。

首先,一道题目不能太过直接简单。简单的题目既不能测试出面试者的能力,又不能拉开优秀和普通工程师的距离。

其次,问题应该具有延展性。有些面试者会比其它人更快的做出题目。这个时候如果重新问一道全新的题需要花时间解释。然而在已经完成的题目上做延伸就不会有这样的问题。 比如可以禁止一些操作,或者对复杂性提出更高的要求。更难的问题可以帮你找出真正优秀的工程师。

第三,题目可以具有一些不确定性。比如在解释题目的时候漏掉一些信息。举个例子,输入integer的范围和正负性。这种方式可以帮助你考察面试者思考问题是不是全面(测试能力),以及是否有良好的沟通能力。

第四,要避免智力题和脑筋急转弯。有些问题太过于依赖面试者的智力表现。举个例子,25匹马赛跑,最少几次找出前3名。不推荐这类问题有几个原因:1. 通过智力题目选出来的工程师不一定能写出优秀的代码。上文中的提到的绝大部分能力都无法考察。2. 尤里卡时刻依赖灵光一现。面试的时候有没有尤里卡时刻有一定的运气成分。一个优秀的工程师很有可能因为客观的原因没有想到答案而被淘汰。

第五,请时不时的去网上搜索一下贵公司的面经,看看自己的题目有没有被泄露。如果被泄露,为了公平起见,请及时更换题目。

要注意面试者的哪些错误?

有一些常见的错误是面试者的通病,需要在面试反馈中记下来:

  • 在没弄清楚题目之前就开始写代码。比如题目的输入是一个数字,面试者有可能假设这个数字是整数而直接写代码。
  • 没有想清楚思路就开始写代码。经常有面试者写着写着发现思路不对,于是删了代码重写。这个时候时间已经过了一半。大部分情况下,做开发之前都要弄清楚需求,不然很有可能会做无用功。以上两个问题都可以反映出面试者是不是有犯这类问题的潜质。
  • 代码没有处理一些明显的edge case。如果一个人的代码只能在80%的情况下运行,环境稍微特殊一点就会crush,那么你以后审核他的代码必须要小心翼翼,这样的人招到组里来大家都很累。
  • 写代码很慢。大部分公司都需要程序员可以高效的开发。这里就不赘述了。
  • 代码不规范。写出清晰漂亮的代码应该是深深刻在每个程序员骨子里的东西。面试的时候虽然时间紧张,但是有些时候一个空格,一个换行都应该是习惯性的肌肉记忆。如果代码写的很乱一定程度上反映出了面试者平时的编程的习惯。
  • 基础知识缺乏。从简单的时间和空间复杂性到常用的数据结构,都应该是各个层次的面试者要熟练掌握的。
  • 沟通能力较差。无法清晰的解释自己的思路或者代码。这个在工作沟通当中效率会受到影响,招进来需要进一步培训。

如何写好面试反馈?

Facebook最常用的是“加减号”记录法:按时间顺序记录面试者的data points,用加减号区分加分项和减分项。比如:

  • +清晰的描述了解题思路,并确认了输入数值的范围。
  • -没有处理输入值是0的情况

这样时候我们可以清晰明了的看出面试者的优缺点。

  1. 要有一个概括总结。上一条提到我们提到按时间顺序记录面试的过程,但是这个过程往往很长,我们还需写出简单干练的总结,概括出面试者在各项能力上的表现。这样别人在看你写的反馈的时候就不需要通读全文了。
  2. 备份面试者的代码。如果是在线面试,只需要把代码复制一下。如果是在白板上,那么请照相,然后贴在反馈里。
  3. 为了提高效率,我会准备一个反馈模板。填空要比每次重头写更有效率。也可以防止漏掉重要的信息和考察点。

如何决定是不是给offer?

无论是否给offer,很多时候都是一个艰难的决定。当两难的时候,我通常问自己的问题是:我愿不愿意在未来的几年里,和这个人坐在一起,共同开发一个项目? 这个通常是一个很实用的标准。如果遇到十分极端的情况,你在问完自己这个问题以后还是不确定,那么就不要给offer。一个好的hire一定是让你和他/她一起工作你会很兴奋。如果做不到的话,宁缺毋滥。

面试官要注意什么?

面试是双向的。面试不仅是我们考察面试者是否合格,也同样是面试者考察这个公司或者这个组是否值得加入。我们经常看到因面试体验很差在网上吐槽的事情。这对公司来说是个极其负面的宣传。所以请在面试的时候尊重面试者,无论他看起来是否符合你的招聘标准。通常Facebook要求面试官做到一下几点:

  1. 关闭手机的消息提醒。频繁的震动会给面试者带来不必要的压力,也会打断面试者的思路。
  2. 用电脑之前告诉面试者你是在记笔记,而不是在处理自己的事情。
  3. 态度要友善,专业。

如何培训面试官?

Facebook一般在面试官上任之前要经过如下四个步骤:1, 在公司呆满三个月(曾经是一年),这样对公司的文化才有一定的了解。2,经过一个小时的面试培训,培训的内容已经包括在上文中了。3, shadow interview 一轮,也就是在旁边看资深面试官是怎么面试的。4. Reverse-shadow inteview 这轮面试的时候有资深面试官在旁边观察你,如果你通过考察之后就可以独立面试了。

写在最后

乔布斯曾经说过“一流的人只愿跟一流的人在一起,如果有二流的人进来,就会有三流、四流、不入流的人进来”。找到一流的人才会给公司带来更多一流的人才。所以作为公司的面试官,一定要为公司把好关。

系统设计篇

硅谷大部分公司(FB、 Amazon、 Uber 等)会有专门的一场面试考察候选人的系统设计能力。一些公司(如Google)会时常把系统面试和编程面试或者行为面试混在一起考察。无论是哪种形式,过硬的系统设计能力都是优秀程序员必备的能力,所以系统设计则是面试中的必不可少的一个环节。候选人等级越高,对候选人的系统设计能力要求也就越高。根据候选人的等级(初级,中级,高级,staff等)和岗位(比如产品,infra等),面试官需要对系统设计题目的内容和难度做出一定的调整,在下面的文章中会做出详细说明。本文不会涉及具体某个系统,而是把重点放在面试方法上。因为技术更新的很快,而面试题目也会因为被用的次数太多而被抛弃,但是面试的方法和思路是很少改变的。

所有的问题解决方案都应该从问题本身出发

开发系统就是为了解决问题。那么在设计系统之前就要分析问题的本身。我发现越是有经验的候选人,越是会在开始设计前弄清楚要解决的问题是什么。比如:”我们为什么要设计这个系统?“, ”我们具体要解决什么问题?“。这就好比一个人管你要锤子,帮助这个人的最优的方式不是给他一个锤子,也不是问他要什么样的锤子,而是问清他到底要做什么?如果这个人的目的是开核桃,给他一个核桃夹子更能帮助到他。有些候选人会认为问题的本身不言而喻。然而在现实中,开发的系统不解决用户问题的例子比比皆是。我们在面试中要寻找那些会从用户角度出发思考问题的人。

除此之外,候选人应当主动去了解问题的细节。比如DAU是多少?具体要提供哪些功能?系统需要支持什么操作?是不是大部分情况下是读操作?这些细节问题会直接影响整个系统的设计。这个环节面试官可以让候选人自己提出假设。重点不是什么假设,而是假设是否合理。面试官在一开始描述问题时,可以选择模糊细节以鼓励候选人提问。比如问题可以是:设计谷歌地图、设计redis。这些系统和产品不可能在短时间内设计出来,因此面试者必须首先确定问题的范围。

定义成功

另外一个候选人经常忽略的问题是如何定义系统的成功?根据要设计系统的不同,成功的衡量标准可以是具体的数字指标:比如延时在200毫秒以内,吞吐率在1k/s 等。也可以是功能的实现:比如最终一致或者提供搜索功能。或者是定义成功的指标:比如DAU、MAU,留存率等。有经验的候选人一般会很自然的提出这点,因为在日常的工作中,定义成功是系统设计中十分重要的一个环节。这个部分答案本身并不重要,重要的是为什么是这个答案。比如候选人定的目标是延时在200毫秒以内,重要的是候选人能否说清楚为什么要在200毫秒以内,而不是500毫秒或者1秒。

系统组件设计

候选人应该具有从全局出发的能力,思考系统需要什么组件,每一个组件的职责是什么。常见的错误包括:1. 组件的职责不清楚:候选人无法解释清楚一个组件到底是做什么的。2. 一个组件负责的功能太多:候选人应当拆分组件以增加可复用性。3. 功能放在了错误的组件上。 4. 组件高度耦合:一个组件过于依赖另外一个组件的具体实现。产品工程师可以把重点放在组件API的设计上。

估算

容量估算是系统设计中重要的一个环节。候选人需要根据之前所给的需求量、数据量等指标来预测设计是否可行以及需要消耗多少资源。如果候选人没有预估,面试官应该主动要求候选人做出计算。比如:我们需要部署多少台机器?每天需要新增多少存储?给出的答案不重要,重要的是候选人的预估是否合理。如果做出了假设,假设是否合理?预估很有可能会影响系统设计中所做出的选择,所以要及时讨论可能存在的问题。有经验的候选人会时常停下来做预估。

提出多种方案,利弊权衡,做出选择

面试官应当鼓励候选人给出不同的解决方案,并询问每种方案的优缺点。候选人应当最终选择一种方案,然后给出选择的理由。面试过程中偶尔会遇到优柔寡断的候选人,面试官应该鼓励候选人做出最终决定。因为不选择很多时候比做错误的选择代价还要高。

挖掘T型人才

T型人才是指一个人的知识点既有深度又有广度。他对全栈的每一个部分都有一定的了解,但是又对某一个或几个领域有很深的研究。T型人才的深度可以帮助公司解决复杂的问题。而他们的广度可以让他们更好的和他人合作。想找到T型人才就需要在面试过程中深度探讨一些技术细节。建议面试官提前阅读面试者的简历,并找到面试者所熟知的领域。比如数据库、网络、系统安全等,在面试的过程中可以花多一点时间讨论这些领域,深挖细节。所选的领域最好是面试者熟悉的,否则很难对候选人做出正确的评估。但是注意避免因讨论过多的细节而没能给面试者足够的时间设计出端到端的解决方案。

根据候选人的等级和岗位调整题目

一般本科毕业生(对应Google level 3)并不具备很强的复杂系统设计能力。上学阶段很少有机会设计大型系统,所以OOP设计比较合适初级工程师。对与中级工程师(level 4)的要求是可以做小型项目,所以可以问service oriented架构设计。高级工程师(level 5)则需要涉及到到可扩展性、高可用、跨数据中心等问题。Staff engineer (level 6) 在level 5的基础上还要从多方面考虑系统的operations。比如可测试性,安全性和可调试性等。对于系统中每个组成部分都要有比高级工程师更深刻的理解。

对于不同的岗位,系统面试的侧重点也应该不同。比如前端工程师的系统面试形式和后端就不太一样。做infra的和做产品的也不一样。做ML的和大家都不一样。大部分公司做产品的工程师不需要知道公司下半年要多买多少台机器来支撑服务,而infra的题目不需要有太多的API设计。产品工程师要考虑用户的交互,而这些并不是infra面试的重点。在选择面试题目和难度的时候都要根据工程师的级别和岗位做出相应的调整。否则既无法做出正确的判断,候选人的面试体验也很差。

建立系统设计面官试培训流程

系统设计面试的难点在于问题通常没有统一的答案和标准。根据候选人申请的岗位、级别、背景不同,面试官要对题目进行调整。因此设立面试培训流程就很有帮助。流程一般包括三步:培训,实例分析和面试观摩。培训的内容可以以本文做参考。实例分析中,经验丰富的面试官找一到两道真题,掰开揉碎讲给学习者听,通过例子告诉学习者哪里是考点、哪里是难点、如何给提示、如何评级等。面试观摩一般分为两步:观摩和反向观摩。观摩一般是去旁听有经验的面试官面试。反向观摩则是有经验的面试官去旁听学习者的面试,提出改进意见并且打分。不合格的需要反复进行这个部分。对于新的题目,在拿来面试之前,一定要找同事来模拟,以保证题目的质量和评判标准的准确性。

写在最后

和其它形式的面试一样,面试官培训中要包含其它的一些主题,比如如何写好面试反馈,如何决定是不是发offer,面试官在面试过程中要注意什么等。

俗话说:文无第一,武无第二。如果说编程题目是比武的话,系统设计就是比文了。系统设计没有正确的答案,只有适合不适合。面试官不应心存完美的设计,并要求面试者给出一模一样的答案。对于同一道题目,每个候选人都会给出不一样的设计。我们要分析每一个设计的利弊。

把握面试的节奏

如果面试是一个小时的话,我一般这么分割时间:

0-5分钟:问面试者是不是需要喝水上卫生间。简单的互相介绍一下,问问面试者之前的经历。从面试者熟悉的内容开始有助于缓解面试者紧张的情绪。

5-55分钟:做题。这个时候你需要尽量收集面试者的信息。所以如果面试者卡在某个具体的问题上太久,你可以适当地给一些提示,如果在一个问题上花费太多的时间,他就没有机会把系统设计完整了。

55-60分钟:回答面试者的问题。这个环节有两个事情需要做:第一是推销你的公司,第二是从其他方面了解面试者。面试者的问题很多时候都反映出了工作中哪些地方对他/她重要。比如公司文化,工作时长等。

如果面试是45分钟,我会把中间做题的时间缩短,前后的十分钟保持不变。因为两部分对面试十分重要,不能缩的更短了。