上个月,在负责技术晋升评审的过程中,有人认为在评审过程中以述职讲述为主,可能对某些比较擅长写代码而不擅于演讲的同学不公平。而对于中级别的程序员技术晋升我们更倾向于筛选出擅长编程,而非仅仅是说得好的同学。
这个过程里面,存在四种情形:
代码写得好,也说得好
代码写得好,但说不出
代码写得不太行,但说得很好
两者都不行
晋升筛选的目标是选出 1 和 2 两种,筛掉 3 和 4。这里面的挑战在于,在采用述职答辩这种形式下,1 和 3 这两种很难分辨,同时 2 和 4 也很难分辨。关键就在于如何识别并判断代码写得好还是不好的问题,区分度的标尺怎么定的问题。这个判断问题在面试程序员时也存在,要不就先从「代码面试」说起吧。
1
在我过去十年多一些的从业经历中,倒是面试过很多次,其中不乏面试写代码的。
刚毕业那年第一次去面试,聊了没几句面试官就给了一张白纸和铅笔,要求在纸上用 C 语言写一个快速排序算法的实现。这次经历我记忆犹新,差不多半小时,我磕磕碰碰的写了一个实现。在和面试官讨论时,被指出了不少没考虑到的情形和漏洞,后来的结果自然是没能通过。
现在回想起来,在纸上编程真是一件很难受的事情。虽然五十年代的程序员基本都在纸上编程,那是因为那时计算机的运行成本很高。但面试时的纸上编程,一方面时间很有限,另一方面环境和氛围比真正的编程要紧张不少。所以,我是不支持纸上编程这种形式的,它既不能让候选人很好的发挥,另外一方面也可能没有足够的区分度。比如,像上面那样写一个著名的算法实现,背过和没背过差别可以很大,但对真正的编程能力却不足以区分。
后来,再有一次面试,被要求在白板上编程,我是拒绝的。只在白板上写了思路,并没有去写细节的代码实现,不过这次倒是通过了。
2
除了要求在纸上写代码的,也有公司会要求上机编程,我在工作一年多以后的第一次跳槽就经历过这么一次。
第一轮的面试以问答为主,但第二轮就直接给了一个题目,并分配了一台电脑要求直接编程实现。题目并不算大,题目细节记不清了,大概记得是搭建一个 Web 应用之类的,考察的更多是工程应用能力,而非算法。
如今回想起来,其实就是判断下实际的动手能力,看能不能干活。既不用和当时一些外企偏爱的逻辑智力题较劲,也没有让人尴尬的纸上或白板编程环节。当时面试的一家国企的软件部门,还算比较务实,但对候选人的编程潜力和能力的要求真不高。
3
十多年前,大家都看那些跨国巨头的软件外企是怎么玩的,而今天,大家都看互联网的巨头是怎么玩的。
互联网的巨头标杆当然是 Google,但 Google 式的代码能力面试槽点也是在网上被人喷的不行。比如,最著名的一条,Max Howell(Mac 下的著名软件 Homebrew 的作者)在面试 Google 被拒后发过一条推文:
Google: 90% of our engineers use the software you wrote(Homebrew), but you can’t invert a binary tree on a whiteboard so fuck off.(我们 90% 的工程师都用你写的软件,但你不能在白板上翻转二叉树,所以滚蛋吧。)
正因如此,有人对 Google 面试的吐槽像下面这样:
“谷歌式” 的面试真心是让人又爱又恨,它糟糕透了:好的应聘者落选,坏的应聘者背背答案就能通过,呵呵。
好吧,上面这句吐槽,我就看到了恨,倒没看到爱在哪里。《Coders at Works》一书(中文翻译版叫《编程人生:15位软件先驱访谈录》)作者 Peter Seibel 曾采访 Ken Thompson,一位传奇程序员,C 语言和 Unix 的发明者、图灵奖得主,他后来加入了 Google。
Peter Seibel: 我知道 Google 有一个规定,每个新员工都要在接受编程语言测试之后,才允许提交代码。那就是说你也得考(你自己发明的)C 啰?
Thompson: 是啊,我还没考呢。
Seibel: 你还没考? 难道你还不能提交代码吗?
Thompson: 是啊,我不能提交代码,不行…我只是还没有去考试,还没觉得有必要去考。
我猜这可能就是 Google 让人“爱”的地方,Google 坚持了一个对所有人一视同仁的标准和规则,即使这个标准有时执行起来得出的结果让人觉得非常不合理。
之前看过一个 Google 官方的代码面试视频,还考察写代码的过程。不用纸笔,而是请面试者打开一个协同工作的窗口,两个人开同一个页面。你改了什么,对方那边是实时反应的。这意味着你的面试官可以在另一端看到你是怎样完成的这段代码,你先写了哪个变量,后写了哪个方法,中途觉得哪里不对,做了怎样的删除,做了怎样的修改…从开始到最终完成,面试官一清二楚。
这个过程其实比看最终的代码更能直接反应编程能力和思考过程,当然这对候选人也会带来一定的心理压力。我觉着完全让候选人不知情的情况去观察可能更有利于真实水平的发挥,否则观测本身就有可能影响结果。
4
另外,还有一家面试代码能力很有特色的公司:ThoughtWorks。
它有一套与一般公司有点不一样的面试流程。对候选人快速初步筛选后,会发给候选人一些题目,让候选人选用其喜欢的任何语言来编程解决。候选人会提交代码用于后续的面试过程使用,在后续面试过程中将与一位 ThoughtWorker 一起结对编程,扩展最初的代码,添加新的特性,在这个过程中来判断候选人的代码能力。
对,这的确是一个独具特色的筛选程序员代码能力的过程,比 Google 式的实时观察更进一步。但这种小众的筛选过程都面临一个问题:可操作性比较复杂,而且成本高。在面临需要大规模的招聘和筛选(晋升)时,可操作性和成本就是一个绕不过的槛。
5
我大概就知道上面这些代码面试方式,似乎没有哪种让人感觉特别完美。
我们考察算法和数据结构,是希望候选人能够具备某些关于算法和数据结构的知识,虽然这些知识很可能在实际工作中并不常用到。候选人也许会去提前学习和记住一些面经中的内容,这样你就评估不了真实的解决问题的能力,而仅仅是看到了他重复回放算法的过程。一些开发人员可能会过于紧张,所以在面试或述职时失败,但也许他们真得具备独立解决问题的能力。而纸上或白板编程是不太好的,这种方式会导致代码人员犯一些在工作中不一定会发生的错误。而且,这种方式又慢又痛苦。
我在想,理想情况下候选人应该有一个全面的 GitHub “简历”。一份好的 GitHub “简历” 包括了你的代码作品以及形成这个作品的过程记录。而 GitHub commit log 天然具有这样的过程跟踪能力,所以我们就能从中看到很多东西。而一份不好的 GitHub “简历” 就是一次性的把作品提交上去后再也没有变化,而不是借助 GitHub 的过程记录来完成这个作品。
有了 GitHub 这个代码简历,就能分析出一个程序员的「代码基因」。代码基因是我临时联想到的一个概念,因为在读《信息简史》这本书时,里面仔细分析了基因的本质,在这里我觉得二者(代码与基因)有相似点可以结合。
基因定义为一种遗传的基本单位,是某种表现型差异的根源。在生物学里,它存在于一种物质中,这种物质是一种核酸,更具体点,就是脱氧核糖核酸(DNA)。薛定谔曾经把基因想象为:某种遗传特征的假想的物质载体。一种微小的实体,却包含了生物体的全部模式,并且这个模式还必须是个四维对象 —— 生物体本身是三维结构,再加上从胚胎到成年的每个发育阶段演变的时间维度。
所以,这就是为什么要具有过程记录能力的 GitHub “简历”,它才拥有时间这个维度,一个代码作品从无到有的演变过程全部记录了下来。通过这样的“简历”,我们就可以针对一些代码的设计演变去问问题,去测定程序员的代码基因。如果我们大量去读过一些著名开源软件的代码,就会发现一些好代码中不仅仅体现了规范性,还体现了特有程序员的「代码基因」所形成的根本性的表现差异。
可惜的是,测定「代码基因」依然是无法规模化的方式,更何况很多程序员根本没有一份合格的 Github “简历”。
…
如果用像《中国好声音》这样的唱歌比赛来做个类比,一份合格的 Github “简历” 达成了基本的技能要求。高辨识度的「代码基因」达成了音色的要求,而实际在《好声音》中评委大部分的转身都是因为音色而转的。
两个同样品质的东西,识别成本低的,通常会胜出。