一晃做程序员也有十年了,总觉得时间过的很快,仿佛第一次写程序还是去年的事情。虽然到现在也已经换了三四家公司了,但总有种自己没有做过什么的感觉。于是便想把个人的经历写下来,留给以后的自己作为个回忆。
大学时代
我原本并没有想过学计算机。在高中时我搞过化学竞赛,2005年考大学时填的志愿也和计算机无关,然而机缘巧合,我的分数只能服从专业调剂分配,误打误撞进入了计算机专业,属于没有任何编程基础的”普通学生“。在大学的时候一直仰望着各种信息学竞赛大神,在被碾压中渡过了四年本科时光。大一大二的时候对于编程是觉得既新奇又畏惧,常常在语法上被困扰半天,又或是为了一个边界条件而调试半个小时,算法也一直是我的短板,只到现在还是会对算法面试有一种天生的紧张。如果说在本科的大学时光中有什么事情是我对于写程序这件事情产生热爱,那便是大三时选修的一门《基于Visual C++的MFC编程》。MFC技术已经作古多年,知道这个名词的程序员估计也都年纪不小。虽然这门课程上学到的知识在后来的工作中并没有用到,但是第一次写出在Windows上可以运行的非常简单的界面程序,那种成就感,比大一大二抄书本调试出来的递归程序要高的多,这门小技术也让我在大三大四的许多大作业上沾了些光:毕竟助教比起在命令行运行的指令,更喜欢鼠标点点就可以出来的界面。自此我便在单机图形界面程序上兴趣大增,大四的时候用C#写WinForm的程序作为软件工程课的大作业,和大部分其他用Java写出的界面”划清界限。
移动应用工程师
学生时代写程序并不能作为”程序员“的经历,我真正的成为一名程序员要从大四开始说起。大四的时候保研结束,有些空闲的时间,想着找一些事情做做。机缘巧合认识了一个大很多届的师兄,自己开了一个公司做智能手机应用。那是2009年,现在二分天下的苹果和安卓的代表手机还是iPhone 2G和HTC G1,系统的版本是iPhone OS 2和Android 1.5. 我进公司实习的第一个应用是把一个在iPhone上的工具类程序移植到Android上。当年的Google还可以访问,但是Android的代码示例也几乎只有官方的Demo,公司里也并没有没有别的会写Android程序的程序员。正是在这样一抹黑的情况下,我迈出了移动开发的第一步。当时每天去公司就是把一台G1连上笔记本电脑,改两行代码后花上半分钟运行一下,看看效果,不行再改两行再试。效率非常的低下。功夫不负有心人,做了三个月之后程序终于上线了。虽然反响也并不好,没有达到iPhone上的营收效果,没多久就从Market上撤下来了;但不管怎么说,也是我第一次写的产品代码,我在程序员的道路上迈出了第一步。
09年毕业后上了研究生,研究的项目也正好是基于Android的,期间做过Android系统程序的修改,包括修改Java代码和底层的C代码,现在看来都很简单粗糙,完全不值一提。但是最宝贵的可能就是读了大部分Android的框架代码,虽然现在已经面目全非,但是在以后的工作中看再大的代码库也不会觉得无力。
研究生期间”不务正业“又辗转做了几个公司的intern,当时为了能兼顾实验室和intern,特意都选择了可以remote的实习。其中包括两家在美国的公司。在这两家公司最大的收获便是锻炼了英语读写说的能力,从一开始面试的时候连名词都听不明白,到后来可以和老外侃侃而谈,在这里迈出的第一步很关键。另一个收获就是除了Android之外,又接触了iOS的编程(当时还叫iPhone OS),学习了一门叫Objective C的语言,以至于很长一段时间在Java和Objective C之前切换的时候会不自觉的打出括号和点的组合。
整个研究生期间我的技能点几乎都点在了移动开发上,关注各种安卓苹果操作系统的新功能,也会借着职务的便利去玩一些新的机型硬件。加之那几年移动应用的发展迅速,市场是对于移动应用开发者还有很大的需求,于是自己对自己的定位为一名移动应用开发者,并将至作为自己毕业后找工作的方向。
2012年7月研究生毕业,在年初的时候我开始了找工作,由于实验室的背景关系好多师兄毕业后都选择去了国外大公司工作,于是我也在期待着可以步师兄们的后尘。无奈自己的硬实力不够,没有能够通过国外大厂的面试。在国内的找工作也并没有非常好的进行,大公司并没有很多移动开发者的职位,待遇比较不错的职位投递了简历却并没有得到回应。在机缘巧合之下,有几家日本的IT公司来到中国招聘毕业生,我参加了其中一家公司的招聘会并顺利的通过了面试,来到了日本东京开始了自己的程序员生涯。
我的正式职业生涯的第一家公司(暂且称之为D社),在当时是一家移动手机游戏为主体运营业务的公司,在当年凭借着功能机上的游戏地位站在日本手游界的Top2位置。D社虽然其收购了美国的一家公司在旧金山也有分部,并且也连续几年在海外招聘了一些外国的毕业生程序员,但是本质上还是一个比较偏传统的日式IT公司。
D社对于新加入公司的毕业生程序员的培训不得不说还是做的比较到位,首先对于海外招聘的毕业生,提供了日语的全日制培训以及之后正式入岗后的日语追加培训。其次,在正式分配部门之前,有为期两个月的技能培训。技能培训的内容是将公司内的Perl框架简化后让大家进行一个类似于填补作业的项目,并全程有老师指导,每阶段需要提交代码并且答辩。答辩不通过的话需要再等两天后才可以预约下一次答辩。现在看来过于严苛和形式主义,但是在以后的工作中,越来越体会到新人培训的重要性,因为在之后的公司从没有过这样细致到“手把手”式的培训。
我自己觉得受益比较深的几点:
- 对于每一句写下的代码,老师会问为什么这么写,有没有别的写法,各种写法有什么不同?会细扣到代码的顺序,变量名的命名,注释的语法等等。实际上这是非常细致的代码审查(Code Review)流程,大部分新人程序员着眼于如何快速的实现功能,有时会不假思索的借鉴来代码,而我之后供职的公司并没有这样的培训,大多数的代码审查也只是停留在错误检查和性能上。个人觉得在一开始写程序时养成良好的习惯非常重要,尤其是对于刚脱离校园环境的程序员。
- 老师也教会了很多工具的使用,比如vim,git,bash等基本操作,比如用bash完成对Apache log的简单统计分析等等。这个其实是程序员的提高生产效率的方法,在之后的公司中遇到太多的新人进入公司好几个月还在git提交上遇到各种困难。或者不得不耗费体力做一些简单脚本可以解决的问题。比起教会的知识,更重要的收获是万事都可以脚本化的信念,不会因为自己的本职工作不包括写脚本而对于脚本就打退堂鼓。
在D社的培训结束后加入了一个临时的岗位,做了三个月的Perl的网站开发,之后又调去了别的部门操刀旧业,维护公司的门户App,说实话门户App的技术含量真心不高,就是WebView封装出来的。期间做了唯一一件有些技术含量的事情是把App内的聊天功能加上了推送功能。在客户端启动时,在服务器端注册客户端的推送口令,在客户端的网页里通过api来通知服务器把消息放进队列,服务器端有定时任务去消化队列中的消息。 这其实是一个非常普通的小系统,也比较成熟,对于个人来说主要的作用就是独立设计并且完成了一个完整的系统,并且在生产环境中实际的运行起来。
在D社的日子没有持续很长时间,最后在公司的半年被调任到游戏部门,做了一款基于Unity2D的手游,虽只有短短的四个月经历,但是也可成为是个人职业生涯中出品的第一款游戏,它是一款抄袭了FlappyBird的山寨游戏,在制作的过程中重温了一回用C#,顺便熟悉了一下Unity2D开发环境。
反思一下自己在D社的1年半,在职场的第一份工作并没有能够很好的积累。而只是停留在完成布置的任务阶段,并没有去主动的学习。
后端程序员
在D社待了一年半之后跳槽到了I社,I社是一家在美国以工作搜索引擎为主体业务的公司,被日本R社收购后在日本开张了办公室。加入I社的时候办公室只有二十名不到的程序员,等到离职的时候已经超过两百人,可以说见证了I社在东京办公室的快速发展时期。
在I社的前三年我一直在SEM组工作,SEM(Search Engine Marketing)组的主要任务是自动的将公司投放在搜索引擎上的广告优化。这是一个纯后端的组,以前的工作经验在这里并没可以发挥的地方。我的工作内容,从一开始去开发维护一个基于Python的内部工具网站(后来知道老板看我的简历是做App的,误以为我是前段能手),三个月之后Python工具网站的开发告一段落,开始接触竞价算法(Java后台程序)。当时正适逢移动流量开始渐渐追赶并超过个人电脑的流量,针对移动端广告进行竞价调整(Bidding Adjustment)是一个重要的功能。正是在进行这一工作的时候,我有机会去主导从MySQL切换到RabbitMQ的解决方案,解耦合算法端和API端的紧密联系。并且通过和系统工程师的配合,解决了首次部署RabbitMQ中遇到的问题,并设置了警报规则去监视系统的运行健康状况。在切换的过程中,为了保证无故障的切换,先后采用了试运行(Dry Run)的方式模拟从MySQL切换到RabbitMQ的场景,接着运用了A/B test的工具分出少量流量测试RabbitMQ流程的稳定性,最后达到100%切换后进行代码的清理。在这个项目中我学到了很多宝贵的经验,对于以后进行的一些重构式工程有很重要的方法论上的参照意义。
在SEM组的工作使我从一个入门的初级程序员,成长到可以去带新人的mentor,除了做每个季度组里的季度目标意外,我也参与到全公司的推进的项目中。比如JDK从1.6升级到1.7,从旧的部署系统迁移到新的部署系统,启用CI/CD模型等等,在做这些项目的同时,自己接触到了在平常的开发过程中不会遇到的问题,比如如何解决库中的class冲突,CI/CD模型适用/不适用的情况等等。
于此同时,自己也不满足于只是去做分配下来的任务,开始观察并思索作为工程师的痛点。比如,每次上游的一些库会莫名其妙的改变一些公有接口,导致下游的项目构建收到影响,结果给下游项目的开发人员带来了额外的负担。另一方面,上游库的开发者要想改变删除过期的接口让下游项目迁移到新的接口,又苦于在公司内部喊嗓子得不到有效的回应,下游项目的工程师没有动力去及时的跟进改变,导致过期接口的删除迟迟不能进行。在这种情况下,如何可以减少不必要的公有接口修改,同时又能提高必要公共接口修改的曝光性?在研究了公司的构建系统之后,我决定在构建系统上,利用一些开源工具和Java编译插件的技术,实现了两个小功能:1. 在发布库的新版本是总是和最后一个旧版本比较API的修改,如果有任何公有接口的修改或缺失则给出警报。2. 提供编译期的注解(Annotation),让程序员可以对公有接口(类)设置过期时间,在过期时间到来之时下游的项目如果有引用则会出发构建失败。这两个功能我是一前一后做出来并在公司内部发布,但是风评却是前一个平平偏向负面,后一个得到不少的点赞和使用,但也引起了不少麻烦。然而由于当时急功近利的心里,并没有很好的去follow。
I社是我从一名初级程序员向着高级程序员成长,随着在公司的时间增长,手头的工作也很快不能够满足自己的兴趣,在SEM组待了将近三年之后我的经理建议我换组,在经历了一番挣扎后我选择了去一个有前段以及顺带一些移动应用的组,在这里我又重操了一段做移动端应用的经历,并且又学习了一些前段方面的知识。
在I社待了3年半的时间,当公司越来越大之后,时常会感到个人的贡献越来越有限,感觉个人的成长也在逐步的缓慢。在对比了其他同事的晋升道路后,仿佛看到了自己在N年后的场景。但是之前觉得在日本没有比I社更适合自己的公司了,于是也一直没有去寻求新的机会。去年随着几位前同事的离职,自己也开始认真的考虑换工作的事情。
恰逢也同样是美国总部的H社在东京开始招全栈程序员,虽然同样是美国公司,但是H社还尚未上市,团队也较小,所以抱着去施展一番拳脚的想法去面试了H社全栈工程师的职位,并于去年7月加入了H社公司。
全栈程序员
加入H社后首先感到的很大的Gap,便是在公司的技术上。在I社,我所碰到的领域都已经有了成熟的解决方案。但是在H社,跟I社所对应的一系列基础设施建设却远远称不上完善。这让我进入公司之后很是怀疑了自己的选择。在进入公司的前两个月,我经常会发信给全公司的程序员,去探讨为什么我们要这么做而不是那么做。并且也提交了很多改进方案,希望可以改成我在I社所接触到的方案。当然这些都并不是很顺利,在H社的老人们给了非常强力的反击。在拿不出充分证据论证的情况下,我只好选择了暂时蛰居,先处理好眼下自己手头的工作。
加入H社后的首个项目是将一个年头已久的PHP前段+后端网页改成PHP + Apache Thrift + GraphQL + NodeJS +React的新框架,作为全栈(Full Stack)工程师,我需要从PHP到React头到尾都做一遍。首先便是读原来的PHP代码,并抽象成Thrift服务。其次便是在NodeJS服务器端将Thrift服务映射成GraphQL的Schema,并实现GraphQL的Resolver逻辑,然后便是用一个Node应用代替PHP的前段,用React的框架来渲染出一模一样的网页。在短短的几个月内,从一窍不通的React小白,到完成了整个页面的迁移,自己对于React框架的应用和一些实践有了自己的理解。GraphQL也是一个对我新鲜的概念,在GraphQL的实践中,我感到这个框架其实也很适用于我在I社工作的第二个组,甚至可以在脑海中把原来的API用GraphQL一一对应起来。这种相互印证的感觉让我再次意识到做出换工作的决定并没有错误,否则我的思路会很长时间局限在I社的框架中。
在加入H社的三个月之后我相通了这样的道理:一个什么都做的很完美的公司,或许更不是一个什么都不完美的公司,因为前者让人失去了去改进的机会,而后者却给予了很多这样的机会。于是,我便在工作中,挤出一部分精力去做一些力所能及的改变。首先便从使用的GraphQL入手,通过调试发现存在着过度查询(Over fetching)的情况,某些查询代价较大的字段,明明没有出现在查询语句中,但是后台却仍然将其返回。于是我通过标注(Annotation),在Resolver层面讲字段和Thrift服务的参数进行映射,使得GraphQL被翻译程序Thrift请求时可以自动的附上请求字段的列表,在服务器端根据字段的列表可以选择性的返回字段,达到“减负”的目的。
春节期间利用闲暇时间,把公司的A/B测试系统进行了优化,这个优化也是我刚进入H社时最想改变的一点,然而遭到很多质疑的点,于是我在进公司提出的propse基础上做了退让,专注于解决最基本的痛点,加入了基于不同域名实行不同的分配(Bucketing)。在于现行系统并存的情况下一步一步的将功能发布了出来,在公司内获得了好评。
下一个十年
从2009年第一次实习经历算起,一眨眼我已经做了十年的程序员。我也过了而立之年,眼看着行业里自己已经算年龄偏大的从业人员。
纵观我的程序员经历,从移动应用开发,到后端、前段,以及零星的DevOps和Release Engineering的经验,我觉的我自己是朝着“全才”的方向发展。然而全才意味着什么都懂一些,但是又说不上是哪个领域的专家。
近两年来在各种媒体上看到大龄程序员的囧境,时常会思考自己的以后的方向。我时常仍会关注国内程序员招岗的要求,发现大多数岗位还是需要领域专家的人才,而不是全才。我也时常会质疑自己,是否太过贪多嚼不烂。然而我最近似乎想通了一点,领域专家vs全才,两种人在这个行业都是被需要的,只是一般的岗位确实会需要你只会干某一样便可以。这并不意味着会的越多就没有施展的地方,在初期的创业公司,以及新成立的部门,这样的人才还是很有必要的。既然自己的兴趣在于了解和挑战不同的领域,不如索性就将其发展到极致。
下一个十年,我想我任会热爱程序员这个工作,我在现在的岗位上,便朝着填补我技能树上的空白努力,争取在下一份工作,可以将自己全部的所学都能够用上。