写这篇文章的时候,面试结果还没有出来,所以标题并不是“我软求职记” <--但我的确想
对于微软,当然最早从Windows 3.1开始,家里那一台386,然后是Windows Me 和 xp,跑在赛扬333 和 Athlon xp (Barton 2500) 上,然后是用Word 97 编辑各种小报纸,文本框粘贴画等等,再然后就是Frontpage,Dreamweaver...这写下去就没完了。
事实上,和微软较为亲密接触应该说得上是Anders Hejlsberg,他发明了Pascal,成为我第一门语言,影响之深刻,直到我前几天去微软面试写程序的时候都习惯i:=0。他还发明了C#,成为我每天使用的语言,也是我经历过的最美的语言。后来,他还发明了TypeScript,前几天的新闻说Angular2 基于TypeScript 开发。
编程语言学真正系统地学习是通过Coursera 上面的课程,来自U of Washington。课上分析了Lisp,Standard ML,Scheme,Racket等函数式语言,并且结合了Ruby 和 Java分析了面向对象的过程语言。可以说,只有少数时候提及Java时才会提到C#,上完课后,我就彻彻底底地谷粉转软狗,转Anders粉,同时也解释了为什么我无法爱上Python,爱上JavaScript。我读到了Jon Skeet 的博客,后来买了他的书,他是谷歌员工却又是微软MVP和C#方面的专家。
我想说,我是真的因为是C# 粉才在半年前关注微软近几年的发展,关注C#的发展史,并渴望加入微软。当然啦,你也可以辩解说,我只是因为去不到谷歌,才没有投递任何简历。
在今年年初我又来到了mitbbs,那里戾气和怨气太重,我基本上是不怎么去的。但听说现在面试都要靠内推,我才来碰碰运气。大概是因为我是非CS专业背景,并没有太多“微软内推”的楼主关心我。直到后来,我碰到了Yammer 的员工,这是微软两年前收购的公司,其产品是企业级社交网络。他很乐意帮我做了推荐,很快就有负责内推的人联系我,上来就说她不是HR,只是负责联系HR和我的联系人。
花了一个晚上填写了长长的调查问卷,耗时最多的是为什么加入微软,和自己的项目介绍。到头来,我也不知道这样的来自英语渣的文字有多少人看到。认认真真写了,并选择了5个岗位,我完全没有概念,这些岗位包括孵化项目,浏览器渲染引擎,Visual Studio,Visual C#和 研究院的软件工程师。很傻很天真,这些高不可攀的职位没有给我任何回复,而后两天,再补投两三个Bing 和Azure 的职位。
两周过去了,这个不是HR的联系人说她工作职责完成了,在我投递的岗位中,没有HR愿意给我尝试的机会,并鼓励我继续关注微软的招聘网站。
这一切都好快啊,我写了一封信感谢那位Yammer 的前辈,这事情大概就算结束了。
我还是太弱了,我还以为微软能为我申请多一份H1B呢,好吧,让我抽签结束后半年好好再努力吧。
然后,我给这个论坛带来了更多的怨气,写了一篇发牢骚的帖子。http://www.mitbbs.com/article_t/JobHunting/32885421.html
帖子除了几个黑微软 的跟帖,大部分都给我了鼓励和支持。最幸运的是,居然还忽悠(打动)了Grace,她成了我的贵人。在她的帮助下,至少我坐上了前往西雅图的航班,来到了微软总部——我的朝圣地。
来自微软的HR部门的Nitin帮我安排好了面试,机票和酒店,从下飞机租车到面试当天,几乎所有人都知道我是来面试的,估计飞机上坐我旁边的大哥也这么认为的——飞机上我看完了半本《Pragamtic Thinking & Leaning》
一路都很顺利,住得很舒服,酒店吃得略贵,以至于面试结束后就再也没去吃过。下面流水帐说说面试的过程吧,就当是面经了。
面试的组是Azure相关的,本来安排了J先生第一轮,但他没能赶来。在我等待十五分钟后,R先生补位迎接我来到了他们的办公区域。双方自我介绍后,R问了我一个设计题——设计一个Car Pooling 的App,我从底层开始写每个类如何定义,并且我提出说使用Actor Pattern来做到,数据和操作分离,在数据上采用读写分离,我还为此定义了Actor 和 数据层。大约写到70%,他让我停止,并询问我如何实现前端的交互,我提出用Microsoft Web API,来提供Rest API,还提供Swagger UI做documentation,并把这个Web API 发布成Windows Services (原谅我没有说发布到Azure,因为我的确没用过)。前端的实现就和后端形成了弱耦合的连接,前端可以是Angular,可以是MVC,甚至是流行的Ruby on Rails。这样的设计题就算是到此结束了,R先生又写了一个JQuery 的语句,语句大概是$('MyControl').val(123). 问我,为什么123 无法赋值,我直觉觉得这个不可能,后来又想到如果是下拉菜单,有一个烦人的selected tag,我就提出说应该下拉菜单应该先去除selected,然后再赋值。他进一步告诉我这不是下拉菜单,并写了一句html 的tag,我在他写了一半后就说这个要用 符号# ,他就再细问了我# 的含义,以及其他selector 如何使用等等。我猜这道题的答案大概就是没有用何时的selector 吧。
稍微休息两分钟后,一个年轻的三哥进来了。他似乎没有什么耐心听我介绍我的项目,很快就让我开始写代码。第一题是给予一个随机数产生函数,他会生成一个32位正负整数,让我写一个函数,利用这个函数来产生区间【a, b】的函数。我的直觉就是映射,因为我原先设计一个智能鼠标,让鼠标跟踪到老师手上的教鞭,靠得就是映射——从摄像头的识别坐标映射到显示器的坐标。很自然的,我说先除以 Int.MaxValue * 2, 然后,此时产生的数一定是0 到0.5 的数字,如果原来的数是整数,再增加0.5. 阿三哥就愣了。他问我怎么可能,我费尽解释并投入一个数字跟踪后,他问我有没有什么问题,我说我们应该考虑边界问题,还有你原来的随机函数产生的都是伪随机数,我就解释伪随机数产生的原理,这是蒙特卡罗方法的重要部分。他晕了一下之后问我,有没有更简单的方法。我想了一分钟,告诉他可以取余数,例如5 - 14 的话,范围是10,除以10 取余就得到0-9的数字,然后加上5就可以了。
第一题就这样稀里糊涂的各种数学推理中完成了。阿三来了第二个题目,让我复制一个链表,链表的元素还有另外一个指向非相邻元素的支链。我很快写了一个,此时他自信很多,他回应这个只是复制了引用,在内存上并不是克隆。于是我写了一份,直接掉入他的陷阱,他提出支链无法指向这些没有生成的元素。看来此处我要用两个循环了,第一个循环生成元素和主链,并存下支链的相对位置,第二个遍历再拷贝支链。大概时间不是很够,他没有再让我检查数据,考虑边界,进一步优化,就说了一句大概就是这个意思,他的这一轮就结束了。这一轮,可以说真正体会到了论坛常常说的“阿三的难题”,难题不至于,我自己没有发挥好,而且有意思的是这个面试官基本没有提示,我写的时候,他在看自己的手机,只有我提到我写完后,他才指点一二。当然啦,技不如人也是要承认的。
第三个面试官是阿三J先生,他是真的是一名技术上大牛。出的题目都是当下想出来的,合并排序,控制内存合并排序,链表合并排序,然后请我到楼下吃饭聊人生,聊职业发展,聊西雅图的生活。一切都进展得很顺利,可以说我很享受和他聊天的每一分钟,在结束后我都觉得时间走得太快,我还想多回答几道问题。当然啦,一路也都不是顺顺利利的,在控制内存合并排序中,我思考了有五分钟,在他提示后完成的。在最后十分钟最后一题的hashing conflicts 的时候,他提出可以再次使用链表来实现contains,而我却没能想出来。
最后一个面试官是大老板N先生,N对我的项目背景是很感兴趣的,在我讲述了我的分布式权限系统后,他觉得好笑,像个玩具一般。因为如果这个系统直接应用Azure 的 Active Directory,很多模块是完全没有必要重复造轮子的,而且Azure 还能提供更优化的网络会话连接。我同样能理解他的意思,但我也提到,我连Windows Server 2012 的VM 都无法分配得到,我连实验都无法完成,架构那边,可能因为监管问题,对于Azure 的认识总是滞后的。我们的云端都还是自己维护的服务器,而不是采用IaaS。这部分结束后只剩下半小时时间了,他让我完成一个矩阵的划零问题,这是经典面试题,如果一个矩阵有一个元素是0,那么把所在的行和列的元素都设置成0。在图像处理里面,这样的应用可以很快地发现极值0所在位置。题目很简单,很快写出来了,双循环,第一个找0,第二个设0。但第二个设0的时候,遍历走错了,马上很快改了过来。然后N先生问了List 和Array哪个更优的问题,还纠正了我的代码风格。我的代码使用了较多的Error Code, 这是C++常用的处理异常的手法。他让我回去好好学习在现代语句中,避免Error Code,而更好地处理异常。
流水帐在这里结束,大老板N先生告诉我上周二或者周三就有结果。遗憾的是,至今我没有收到任何信息,我的猜想是可能我没有优秀到可以马上录取吧,现在我大概在几位“差不多”的候选名单当中,他大概在等待最佳的出现。如果他仍未出现,再从“差不多”名单挑一个吧。让我好好等待吧。
微软梦,希望能成吧!求祝福!