天天看點

我的十年程式員之路

一晃做程式員也有十年了,總覺得時間過的很快,仿佛第一次寫程式還是去年的事情。雖然到現在也已經換了三四家公司了,但總有種自己沒有做過什麼的感覺。于是便想把個人的經曆寫下來,留給以後的自己作為個回憶。

我原本并沒有想過學計算機。在高中時我搞過化學競賽,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全才,兩種人在這個行業都是被需要的,隻是一般的崗位确實會需要你隻會幹某一樣便可以。這并不意味着會的越多就沒有施展的地方,在初期的創業公司,以及新成立的部門,這樣的人才還是很有必要的。既然自己的興趣在于了解和挑戰不同的領域,不如索性就将其發展到極緻。

下一個十年,我想我任會熱愛程式員這個工作,我在現在的崗位上,便朝着填補我技能樹上的空白努力,争取在下一份工作,可以将自己全部的所學都能夠用上。

繼續閱讀