如何招聘一个能干活的程序员
写这篇文章不是件容易的事情,因为需要举一些实例,就会牵扯到过去现在的各种见闻,也会牵涉到业界的各种看法。既然博客也是公开状态,还得考虑有的话是否需要委婉一点讲… 不过毕竟出来混了也有一年半载,对这方面还算略有心得,那就班门弄斧一下,还望高手不吝赐教。
关于一个程序员该具备怎样的能力,这篇帖子里面一共讲了7点(伯乐在线注:经询问文章作者,得知前文提到的帖子是在一个内部论坛,外部人员看不到,故而删除文章链接了)。这7点本身没错,放之四海而皆准。但是对于程序员这一个特定的群体,似乎又显得过于宽泛。有一些比如善于沟通这样的能力更多是靠环境激发出来的。如果不幸混进一个国企,那其实锻炼更多的还是和领导套近乎和推卸责任的能力… 所以我认为,一个优秀的程序员有两个最核心的能力是不可代替的:解决问题的能力与学习能力。
先来说说“解决问题”的能力。
这个能力是相当宽泛的:debug是解决问题;设计某种架构是解决问题;提高用户体验是解决问题;根据需求给出整体的解决方案同样是解决问题。概括起来就是本文表退中的“能干活”,或者说就是“getting things done”。解决一个问题实际上分两方面:其一需要有足够的技术能力找到解决方案;其二需要对业务有充分的了解,明白到底该怎么做才能达到用户的预期。结合实际来说,做一个游戏不仅要完成策划提出的功能需求,还要考虑做出来的功能操作上是否便利,是否符合玩家的习惯和心理预期,是否可能引起玩家的误解和不满等等…
在技术熟练的情况下,绝大部分的功能要实现起来并不需要花很大的精力在技术上,而是需要把精力投入到用户体验上面,对每一个细节都斤斤计较,这样才能做出精品。这和技术实力并没有太大的关系,很多时候关键看执行者的耐心、细致,甚至是品位。在对大部分游戏系统有充分了解的前提下,只需要策划给一个大致的思路,略加点拨就可以做出符合预期的实现,这才是一个合格的游戏程序员应有的素质,吃过了五星级料理,虽然未必能总结出什么理论,但直觉会让你明白什么是一定会被策划打回来的狗屎,所以这就是我会去花时间玩大量不同类型游戏的主要原因(终于成功为自己玩游戏找到借口,噢耶~),
熟悉业务是解决问题一方面,另一方面就是技术水平。虽然大部分情况下不需要非常高深的技术,但如果牵涉到了一些关键的核心算法,或是性能调优、架构设计,技术水平就显得尤为重要。这就一定会牵涉到学习能力的问题。不可否认的是,技术积累非常重要,但同一个方向上的积累同样会造成思维定势,从而不愿主动走出自己的舒适区。光从解决问题的角度而言,在碰到难题时,首先需要尝试相对稳定和成熟的解决方案,但同时也不能排斥在必要的时候使用一些比较“古怪”的方法。保持开放的心态(keep an open mind)在解决问题时同样也很重要。
在来公司以后,我参与的第一个项目是一款和《百战天虫》相近的回合制射击类游戏。当时刚进入项目,听了策划介绍后我就把战斗场景按难度分成了四个主要部分:1. 人物移动等基本操作 2. 场景视角缩放 3. 炮弹飞行轨迹 4. 地形破坏。实际上前三个部分在技术上没有太大的难度,主要还是跟着策划的思路走,但第四个问题在当时确实把我给难住了,因为在这方面没有技术积累,根本连如何解决的思路都没有。之后用过了两个方案,一是用物理引擎来实现炮弹飞行和破坏,这个方案确实可行,但与游戏的整体风格不符,而且物理引擎本身也会有一定限制,在不熟悉的情况下适得其反。第二个方案是用openGL提供的一些接口,通过遮罩图来实现破坏。这个方案在应用中发现效率太低,炮弹落地时会需要将近一秒才能重绘完成,之后发现是openGL的运算接口占用了大部分时间,这也是很难进行调优的。当时沈晟给了一个解决方案,概括起来就是自己实现一套相关算法,然后重载材质。
当然这套算法比较hack,需要直接操作内存中的图片数据,当时因为对引擎和图片格式不熟结果我没有看懂沈晟给的示例….后来又去调视角缩放花了很长时间,在将近一个月后某个周末的早上突然开了窍最终把这个问题搞定了。实际花了一个多星期的时间写出了最后的解决方案,只需要0.1秒就可以完成地形破坏的运算与重绘,顺便还解决了人物移动和碰撞检测的问题。当然那套算法在实现时很丑陋,因为使用了非常底层的方式,还存在一些溢出的bug需要调整。不过这个解决这个问题的过程给了我很大的启示:学习能力,特别是底层的基础知识非常非常之重要。现在想来,如果之前的一份工作不是写C语言而是隐藏了太多底层实现的JAVA,恐怕就几乎不可能解决这个问题。
学习能力:重视基础,不纠结于细节
在上一篇文章中,我提到了程序员能力的两方面——解决问题的能力与学习能力。这两点可以说是相辅相成的。而在学习能力中,基础又是必不可少的一条。下面就来说说我对基础的理解。
当我还在大学的时候,我的数学成绩一直不太好。有一个同学在其他方面毫不出众,但他所有和数学相关的学科都学得非常好,考试前也只是翻翻书,并没有付出相当大的精力和时间。我始终以为他天赋异禀,但后来才发现,无论学到什么新的东西,他总是能用非常简洁的语言解释这个概念的本质。换而言之,在绝大多数人还在死记硬背的时候,他已经了解到了一些本质性的东西——这些东西可以把所有和数学这门学科相关的知识串联到一起,对他而言,看问题的视角也和绝大多数人不同了。
什么是“基础”?我认为这是对一门学科本质的理解。什么是计算机科学的基础?计算机架构,计算机程序的构造,编译原理。几乎所有的现代计算机都基于冯诺伊曼架构,为何这一架构会成为现代计算机的主流?它又是如何设计出来的?我们都知道一个程序的地址空间包含堆区、静态区和栈区,那么为何要将其分开设计?为何在预编译时需要进行词法和语义分析?能回答出这些问题中哪怕一个,就说明对这些本质性的问题已经有了较深的了解。
最近我一直在看王垠的博客 ,其中提到了很多关于程序设计方面的知识。此人读过三个博士学位均未毕业,但不可否认的确是个大牛。他在文章中提到的很多东西,感觉就让人上升到了一个新的层次。虽然计算机语言层出不穷,但是归根结底,其本源都是相同的。有经验的程序员可以很快地学习一门新语言,更多地是因为对程序语言本身的了解,所以一通百通;至于一些技术细节,大部分时候通过查资料就可以解决问题。遗憾的是,在中国很多招聘者与应聘者都没有意识到这一点,于是就出现了“程序员干到30岁就得转行”这种错误的趋势。很多人并不明白他们应该用什么方法来积累经验,编程本身对他们而言也许只是种维生手段。而雇主也用应试教育的思维去挑选应聘者,在这样的前提下挑选出来的往往只是一些代码机器。当然代码机器在外包公司这种劳动力密集的地方还是大有用处的,谁叫我们国家人口众多,劳动力从来不缺呢,呵呵。
废话讲了一大段,接下来还是讲讲关于如何招聘吧。
首先需要声明的一点是,以下这些建议,不适合外包公司、国有企事业单位,只适合100人以下,团队和人事关系相对简单的中小型公司及创业型团队。当然,如果是大公司下,团队leader比较强势和开明,有较大的资源调配及话语权,也是可以一试的。
在我看来,要招聘到一个“能干活”的程序员,有以下几条原则:
1. 此人是否和团队有着相近的价值观,作为一个团队的leader,是否愿意能够留住这名员工?
之所以把这一条放在第一位,是因为国内现在IT行业人员的流动性实在是太离谱了,干个一年拿了年终奖就跳槽的事情比比皆是。在IT这个行业,新员工加入任何公司都至少需要半年左右的磨合期才能发挥出其应有的水准,相当于一半的时间都在磨合中渡过,对于自己和整个团队都不是件好事。
寻找价值观相近的人共事,首先就可以确保这个人对团队所从事的事业和目标有着相当的兴趣和动力。这些兴趣和动力,可以转化成额外的工作成果,这也是优秀程序员和码农之间最本质的区别。
2. 注重细节,但不拘泥于细节
把这一条放在第二位,是因为我看到了这么一道题:
int a=2; a+=a-=a*a; a=?
首先我要声明的是,如果在任何面试中面试官给我出类似的题目,即便我没有立刻走人,这家公司在我心目中的地位也会一落千丈。在实际编程中,最重要的是代码的可读性和非二义性。在 不同环境 下,由于编译器的实现不同,这道题的答案是不唯一的。何况这样的写法在实际工程中除了造成其他同事的困惑和可移植性降低之外,一无是处。
前两天我还碰到了一个类似的问题(写得不太严谨,各位包涵):
string text = "123";
this->methodA(text, text.append("456"));
void methodA(string textA, string textB)
{
string textC = textA;
}
问 textC 的值是多少?
很多人第一反应,textC的值当然就是123。对函数的压栈顺序有一些了解的人会认为是123456,因为参数从右至左压栈,会先执行append方法将text值扩展为123456。但在实际应用中,ios平台上的执行结果是123,安卓平台编译后的执行结果是1230。是不是很奇怪?其实这是一道不错的面试题,但不应该直接问面试者执行结果,而应该问这段程序中可能存在的问题,以及改进方法。最简单的方法就是加一行string text2 = text.append(“456″),这样就不存在压栈顺序不同导致不同平台下结果无法预期的问题,代码的移植性也就更好了。如果拘泥于一个“正确”答案,那就陷入了应试教育的泥潭。我们要招的是一个能够解决问题的人,而不是纠结于茴字有几种写法的书呆子。
当然,这些细节问题可以体现出一个人的功力,但不能本末倒置,以了解所有的细节为目标。在面试中,我还是主张这些问题尽量少问,如果面试者无法回答,可以用探讨的方法给出一些提示,主要还是观察其思路和态度为主,不宜过分纠结。我曾经经历过国内某规模不小的公司一场很不愉快的电话面试,面试我的人听语气应该也不是个等闲之辈,遗憾的是他在20多分钟的时间里有15分钟都在一些细节问题上面纠缠不休。他问了我在之前工作中碰到多线程问题的解决方案,我当时回答他在某某地方使用了读写锁,之后他就开始不停询问为什么要用读写锁等等等等。其实理由很简单,因为在那个特定条件下读操作次数远远多于写操作,所以读写锁是最优解决方案(事实上我在设计时也是这么想的)。他可能是要问如何尽量避免加锁达到性能优化,但是首先性能优化必须在有充足证据证明效率低下的情况下才有必要进行,而我当时并没有碰到这种情况,所以没有必要为了一个可能的性能问题大费周章。我当然也不能对他说“你这个臆想症患者给老子闭嘴”,于是只能在尴尬中渡过这个问题。后来他问了我一个gdb的使用问题,我想他的本意应该是看看我调试程序的功力怎样,没想到他开始问我gdb的常用命令有多少多少个,条件断点该如何设置等等等等。对于这些问题,我完全可以找一本gdb使用手册扔在他面前。经过了这场面试,不管对方的态度如何,这家公司的技术实力在我的心中也已经打了一个大大的问号。
在面试中,一个面试官应该尽可能地去了解并引导对方解决问题的思路,而不是拿一些细节问题是否答出了“标准”答案来评判一个人水平的高下。毕竟,细节知识上的欠缺很容易弥补,良好的思维习惯却是无法在一朝一夕养成的,这一点对于评判那些应届毕业生尤为重要。
顺便多提一句,很多公司的招聘启事在应聘者要求中往往会不小心写错一些技术的名称,或是犯一些低级错误,这会让应聘者对这家公司的专业性产生怀疑,反之亦然。在简历和招聘启事上,还是多注意一些细节吧~
3. 良好的编码风格
在编程中,最困难的事情莫过于起变量和函数名。好的变量和函数名能让人一眼就看出这段程序的功能,而糟糕的名字(没错!就是我上面写的textA这种玩意,所以我也是个烂程序员)往往让人摸不着头脑。由于迄今为止还没有任何一种主流编程语言支持英文以外的字符,所以使用标准的英语起名应该成为共识。这里我要吐个槽,我在代码中发现了一个叫“Supersport”的玩意,光看这名字还以为是什么比赛,一问才知道是“竞技场”,实在想不通为什么不用”Arena”。还有像强化装备翻译成“upgrade”也容易造成歧义,国外一般都用“enhance”特指装备强化。还有像“baoxiang”,“yamede”,”yikuyiku”这种变量名真是让我泪流满面….(后面两个是我杜撰出来的,LOL)。所以,要辨别一个程序员的水平,最好的办法就是看看他的代码变量名是否清晰,换行缩进的格式是否严格,会不会写一个几百行的函数不知道把其中的功能块进行封装….
4. 是否有个人博客网站、github、stackoverflow
是的,如果一个应聘者有以上这些内容,我一定会考虑优先面试及录用他。
以上这些内容都需要在平时挤出业余时间来完成。架设网站,给开源软件和社区作出贡献,在专业性上肯定不如严谨的日常工作,但同样需要付出精力和时间。如果一个人愿意在业余时间做一些和技术相关的事,至少能说明这个人不仅仅是想混口饭吃,而是将其作为一种爱好,这样的人假以时日必有所长。这些公开资料同样可以检验应聘者的真实水平:个人网站可以看出一个人的品味及价值观;github上的代码可以看出编码风格;StackOverflow上的活动可以了解他的表达能力和英语水平。能做到这些的应聘者在国外较多,但在中国并不多见。当然,还是以兴趣和实际能力为主,做不到也无需强求。
前前后后花了快一个月总算把这篇文章写完了!我写文章的速度的确不容乐观啊…..总算是坚持到最后一刻了!
来自:侯帅英(@江南奇侯)
http://reckhou.com/blog/%E5%A6%82%E4%BD%95%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E8%83%BD%E5%B9%B2%E6%B4%BB%E7%9A%84%E7%A8%8B%E5%BA%8F%E5%91%98%EF%BC%88%E4%B8%80%EF%BC%89/