1. 程式人生 > >【轉】什麽是現實理想主義者

【轉】什麽是現實理想主義者

alt 集成 價值 能夠 org 證明 其他 管理者 隔離

曾經有人看了我的文章,以為我是一個“理想主義者”,來找我聊天。他說:“你知道嗎,我跟你一樣喜歡簡單優雅的代碼。上次我在某公司工作,看到他們的代碼亂得不成樣子,二話沒說給他們重寫了,結果有幾個小地方跟原來的代碼不大一樣,後來系統因此當掉了。老板對我說,明天你不用再來上班了!你說我是不是好心沒好報啊?”

雖然我同情他丟了工作,然而我並不認同這種不經同意就推翻重寫別人代碼的作法。實際上我曾經跟一個老喜歡重寫別人代碼的人合作,後來整個團隊(包括我)都差點被他給弄瘋了。所以我對他說:“你不可以這樣改別人的代碼的!如果我是你老板,可能不會開掉你,卻也會給你一個嚴重警告的。”

從我們的對話你也許已經發現了,我並不是一個通常人所謂的“理想主義者”。雖然我有很多新穎而美好的想法,然而它們全都深深植根於現實中。我反對不以現實為基礎的“理想”,實際上那不叫理想,而只能叫做“空想”。我的直覺和理性會很快的告訴我,哪些事情是可能的,哪些是不大可能的。我往往在早期就能察覺和避免那些最終會失敗的“理想主義作法”。

從我對各種“新語言”,“新理論”和“新技術”的看法,你也許已經發現了我的這個特點,我不再是十年前那個“熱愛新奇事物”的王垠。不理解的人甚至會覺得我“守舊”,然而我只是通過理性分析,預見了某些“新技術”的失敗。在我的心裏,事物和技術並沒有新舊之分,只有合理與不合理的差別。

如何對待別人的代碼

那麽我是如何對待別人的“垃圾代碼”的呢?你也許會很驚訝我的做法:我盡量不動它們!

雖然我喜歡簡單優雅的代碼,然而對於別人寫的代碼,就算它再醜再亂,我也不會亂動它。我就像一個外科專家,多次對已有代碼進行“換心手術”。這種手術成功的要訣,是制造盡量小的“切口”,剛好可以換掉心臟,而不動其他部位。就算那些地方血管亂繞,堆滿各種垃圾,也不要去動它們。

這是為什呢?因為代碼首要的目標應該是“解決問題”(包括“沒有 bug”),其次的目標才是“簡單優雅”。如果不能解決問題,再優雅又有什麽用呢,只不過是玩具而已。對於已經可以解決問題的代碼,就算它再亂再復雜,我也是高度尊重的,絕對不敢像這個朋友一樣,不假思索就刪掉重寫。這就像你給別人做換心手術,看到大腿上有些血管是亂的,就把大腿也切開倒騰,你的病人不死才怪呢。

我自己寫代碼的時候,“解決問題”和“簡單優雅”往往是緊密結合,交織在一起的。如果我寫不出簡單優雅的代碼,我就不能又快又正確的解決問題。所以我的代碼往往從一開頭就是簡單優雅,模塊化的。我從很小的函數開始寫起,每個函數只解決很小的問題,最終我把它們組合在一起,解決掉整個問題。

對於別人的代碼,情況就很不一樣了。很多人寫的代碼很亂,很復雜,不易理解,看得我頭痛,但由於他們在上面花了很多的時間,而且這些代碼經過了很長時間的使用,大量現實情況的考驗,所以它們已經算是解決了問題。對於這樣的代碼,我的經驗是這樣:如果把它刪掉完全重寫,是很難不犯原作者已經犯過的錯誤的。就算你自認為水平世界一流,寫的代碼極其簡單和優雅,也不能避免犯錯。

這不是一個智力的問題,而是一個智慧的問題。喜歡刪掉別人代碼重寫的人,也許有很高的智力,卻缺乏智慧。代碼是用來解決現實問題的,而現實有許許多多的細節,代碼需要覆蓋現實世界各種不完美的地方。這些不完美也許來自庫代碼,也許來自操作系統,也許來自網絡協議,也許來自用戶習慣,也許來自自然界。我們必須承認,很多這些東西我們是沒有能力,沒有時間,也沒有必要去改變的。

別人已經寫好,用了幾年的代碼,很有可能已經遇到各種現實問題,各種邊角情況,原來的作者雖然不像你一樣思路清晰,卻也為此付出了時間和精力。這些復雜混亂的代碼邏輯裏面,已經針對現實世界的不完美,做出了基本可行的解決方案。一個有智慧的人,必須能利用這些前人留下來的混亂代碼,因為它包含了時間積累下來的財富。

那麽我一般是如何利用別人遺留下來的代碼的呢?我的策略包含好幾個要點。

首先,我盡量保持別人的代碼原封不動。因為別人的代碼解決的問題,很可能不是我當前需要解決的問題。因為看不順眼而去改別人的代碼,不但分散自己的精力,而且有可能制造新的 bug,導致新老代碼中同時多處出現 bug,難以追蹤和修復。為了保持別人的代碼原封不動,卻又讓自己寫的新代碼簡單優雅,我必須理解原有代碼的接口(interface),以及它原有的各種特征,我力求保持它們不變。這就像外科大夫做換心手術,他必須保證已有的血管都連接到正確的地方。

我喜歡把自己的代碼做成一個可替換的,模塊化的元件,可以隨時在系統裏插入或者移除。一旦發現出了問題,我可以隨時切換到原來的代碼,重新測試,這樣我就可以知道問題出在原來的代碼,還是出在我的新代碼裏面。另外,我還會註意避免對已有函數進行換名,這樣我可以把自己的修改局限在一個或者少數幾個文件裏面,避免 Git 的歷史裏面出現不必要的,讓人分心的修改。就算要換名也應該單獨作為 commit,而不應該跟邏輯的修改混在一起。

如果經過多次試驗,我發現別人的代碼的確需要改,不然我沒法繼續寫新的代碼,那麽我只好對它進行修改。由於已有的代碼復雜混亂,我一般會極其小心的對待它。我不會刪掉大片的代碼,從頭開始寫,那幾乎註定是要失敗的。通常我會先“隔離”出很小的一塊代碼,對它進行重寫。隨之立即進行大量的測試和試驗,找原作者來幫我檢查是否有問題,如此反復……

那麽這塊改掉的代碼需要小到什麽程度呢?我也許就只改寫一個 for 循環,把幾行代碼提出去做成幫助函數,簡化一個表達式,把一個類成員變成一個局部變量,改幾個局部變量的名字之類的。你可以參考我在《編程的智慧》裏提到的各種改進代碼的方式。每一個這樣的小改動都有可能出錯,所以在此之後必須進行嚴格的驗證,確保修改後的代碼和原來的代碼語義相同。這樣反反復復很多次之後,你才能正確的替換掉原來的代碼。

從我對待別人代碼的方式,你也許已經發現了,我不是一個通常意義上的理想主義者。我不會為了自己簡單優雅的理想,而完全推翻重寫別人的代碼,因為我知道現實世界的復雜性,我知道這樣做註定是要失敗的。我對待別人代碼的態度,是深深地植根於現實的。通過極其嚴密的措施,我確保改進後的代碼跟原來的代碼語義完全相同,盡最大可能避免重復前人的錯誤,避免制造新的 bug。

由於我的理想植根於現實,我把自己稱為“現實理想主義者”(practical idealist),而不是“理想主義者”(idealist)。我曾經跟純粹的理想主義者共事,這種人總是嫌別人的代碼醜,不經商量就大幅度的刪除重寫大量代碼,結果給團隊的開發帶來災難性的後果。我在將來會避免跟這樣的人共事。

通過這個例子,你可能已經發現為什麽“現實理想主義”是優於“理想主義”的。下面我來講一下,為什麽“現實理想主義”也超越了完全的“現實主義”。

超越現實主義

既然我不是一個完全的理想主義者,那麽是不是說,我就是一個完全的“現實主義者”呢?在我的職業生涯中,我已經多次證明了,我不是一個完全的現實主義者,我能做到現實主義者做不到的事情。我心中的“理想”成分,讓我能夠看到現實主義者看不到的可能性,而我的“現實”成分,又幫助我為這種可能性找到切實可行的路線。理想和現實的結合,指引我達到現實主義者認為是不可能的目標。

說到這一點,第一個跳進我腦海裏的例子,是我當年在 Google 完成的項目。Google 需要一個可以像 IDE 一樣索引 Python 代碼的工具,可以支持準確的“跳轉到定義”功能。作為現實主義者的團隊領導(Steve)對我說,你去拿一個開源的 Python 工具,比如 PyDev,修改之後插入到我們的構架裏就可以了。

當我調研了十多個開源 Python 工具和 IDE 之後,發現它們都不能準確地實現“跳轉到定義”。它們的實現方式基本都是字符串匹配而已,所以找出來的“定義”完全不著邊際,甚至把字符串裏出現的名字都給加亮了。這時候,我的理想成分告訴我,準確的定義查找應該是可能的,只不過現有的工具都不知道怎麽實現它而已。為了給 Python 這樣的動態語言實現精確的定義索引,就必須實現類型推導,而這是我很在行的事情。於是我決定做一個新的 Python 類型推導器,這樣就可以利用它實現精確的跳轉功能。

我把這個想法告訴了 Steve 和其它團隊成員,結果作為現實主義者的他們,非常的擔心這個項目無法在三個月的實習期內完成。Steve 說:“你知道嗎,光是寫一個 Python 的 parser 就夠寫三個月了。我很擔心你不能完成任務!” 這時候,我的現實成分開始起作用。我說:“你知道嗎,我並不覺得寫 Python 的 parser 是一件很難的事情,但我也不覺得它是一件很有意義的事情,所以我會拿一個開源的 parser 來,利用它生成的語法樹,然後在上面完成我們需要的功能。”

結果,我拿了 Jython 裏面的 Python parser,然後在上面實現了 PySonar。整個對付 parser 的過程只花了我兩天時間,剩下的時間我都在研究和實現最關鍵,最有趣的部分。我拿了別人已經做好的,自己不想做的東西來,然後加上自己的核心思想,達到了最終的目的。最後,我不但在三個月的時間裏完成了 PySonar,而且把它集成到了 Grok 項目裏面。

在這個例子裏,現實理想主義者幫助了現實主義者,完成了他們以為不可能的事情。本來 Grok 項目在 Google 處於瀕臨滅亡的境地,由於 PySonar 的成功實現增大了項目的影響力,團隊在 Google 存活了下來,並且開始受到公司的重視,相關人員也獲得了提拔。今天 PySonar 仍然在為 Google 的 Python 程序員提供高質量的索引服務,它生成的數據在背後默默支持著 CodeSearch 等內部代碼搜索服務。

個人興趣與企業興趣

最後,我想再講一個跟這個話題相關的故事,它說明現實理想主義者不但是一種個人技術財富,而且是企業的財富。他不但與“企業的興趣”一點矛盾都沒有,反而在很多時候可以幫助甚至拯救公司和團隊。這個故事很有趣,但中間部分技術性有點強,看不懂的人可以跳過。

我曾經在職的某公司,邀請了某位“大牛”來做 VP。經過一段時間的接觸,我發現這個人不懂很多東西,盡在瞎指揮。很明顯,他並沒有把公司的利益放在心上。在多次的瞎指揮之後,有一天他又提出一個“新想法”。他說,我們團隊的代碼應該實現“模塊化管理”。如何實現模塊化管理呢?我們把代碼按目錄結構切分開,分成 30 個“模塊”。把每個模塊做成一個 Git 代碼庫(repository),代碼庫之間通過 Maven 的版本號依賴關系進行連接。每個人負責一兩個模塊,使用“語義版本號”(semver)標註模塊的版本。如果修改了代碼,就更新對應的版本號,這樣依賴於這個模塊的代碼庫就必須做出相應的修改,才能連接到新的模塊代碼,不然它們就可以繼續使用舊的模塊代碼……

這個新想法沒有經過團隊的集體討論研究,就被 VP 的一個親信動手實現了。一夜醒來,我們發現代碼庫被他分成了 30 多個,制定了一系列規章條款,要我們遵守。接下來的事情,我發現自己沒法工作了。一天當中有超過半天的時間,我發現自己在為那些 semver 傷腦經。你剛剛更新了所有的代碼,才工作了個把小時,正要提交的時候,卻發現另外幾個模塊的版本號更新了!你得手動去看是哪些代碼庫發生了改變,更新自己 maven 文件裏的依賴關系,然後才能進行測試,提交自己的代碼。有時候當你提交之前,忽然又有其它的模塊版本號發生了改變,所以你前功盡棄,又得去查到底是誰改了他的模塊版本號。有很多次,有人沒有把版本號完全搞對就提交了代碼,結果導致項目 build 失敗。

後來我發現,這種所謂的“模塊化”,根本就不是真正的模塊化,而 semver 版本號,在這裏也並不比 Git 的 hash 更好。模塊不應該是按目錄結構劃分的,而應該是按代碼的邏輯結構,而且模塊之間不應該有“循環依賴關系”,否則這些模塊就不應該被分成模塊,而應該合並在一起。另外,semver 根本不是用來幹這個事情的,它根本不應該被用於連接同一個項目裏的多個模塊,它只能被用來引用庫代碼。每一個 Git commit 的 hash,本身就是一個“全宇宙唯一”的版本號,它包含了代碼所處的獨一無二的狀態。所以 Git 其實自然而然的解決了這種“模塊”間版本依賴的問題。所以把代碼拆分成 30 多個 Git 代碼庫,使用 semvar 連接它們,完全是多此一舉,而且嚴重的損害了開發效率。

觀察到這個問題之後,我向團隊群發了郵件,告訴他們我覺得這樣的做法已經造成了我工作效率嚴重打折,並且指出了問題的要害。一個來自法國的資深工程師深有同感,也開始抱怨,說自己花了超過一半的時間來折騰這些版本號。然而 VP 聽了這些意見,卻堅持認為自己的“創新”是有價值的,對我們說:“任何一項偉大的創新,都會受到不理解它的舊勢力的阻礙。同誌們,困難是暫時的,適應是必須的!” 為了這個問題,我們在 email 裏面吵了兩個星期之久。任憑我們據理力爭,拿出具體的證據證明這種做法不可行,嚴重的傷害了團隊的開發效率,VP 憑著自己的名氣和地位,毫不退縮。

最後無賴之下,我決定采取實際的行動。我寫了一個 Python 腳本,它調用 Git 的一些罕見命令,可以自動把多個 Git 代碼庫合並成一個,並且保留所有的歷史 commit 信息。有了這個腳本之後,我可以隨時制造出一個合並的代碼庫。我把這個腳本分享給了團隊,告訴他們我隨時可以把代碼庫合並在一起,而且給了他們一個合並後的代碼庫,作為試驗用。我告訴他們,可以試用這個代碼庫,看它是否解決了 30 個代碼庫帶來的問題。最後法國同事和其它幾個人采用了我的代碼庫,發現不再有之前的頭痛問題。

我們用理論和切實的證據證明了所謂的“模塊化代碼管理”的不可行。通過對其它公司代碼的觀察,我們發現 Google 的 Chrome 項目有三千多萬行代碼,卻全都存放在同一個 Git 代碼庫裏。這說明一個 Git 代碼庫足以支持管理 Chrome 那麽大的項目。我們的團隊總共才 20 多人,代碼不超過十萬行,卻被強行切分成 30 多個代碼庫,這是非常荒唐滑稽的。

最後在工程師們的一致同意下,再加上團隊 director 委婉的支持,我用腳本將 30 個代碼庫合並在了一起,結束了大家的痛苦…… 在此之後,VP 的親信們還不死心,在合並後的代碼庫裏又做了一些手腳,故意加大工作的復雜性,讓我們依賴於他們的“工具”,這些我就不細說了。總之你看到了,這位 VP 的瞎指揮,導致團隊浪費很多的時間和精力。如果這種情況不受控制繼續下去,整個團隊甚至整個公司,都有可能因此走向滅亡。

我發現很多所謂管理人物,他們到一個新的公司出任要職,其實並沒把公司的利益放在心上。他們不是為了公司的發展和成功做出決定,而是為了自己的“仕途”。這些管理者明白,公司就像一艘船,自己表面上在為公司服務,而其實是在利用公司的資源達成自己的目標。由於自己揮霍公司的資源,而不作出實質的貢獻,甚至瞎指揮幫倒忙,這艘船在將來很可能會沈沒。但作為管理者,自己總是可以在沈船之前跳到另外一艘船上,靠著自己的關系網,不斷找到高薪的職位……

像這樣的例子我還有很多。為了團隊,為了公司能夠達成自己的目標,我多次頂著壓力,幫助團隊和公司避免不必要的浪費,甚至懸崖勒馬。當然很多時候團隊在錯誤的道路上走得太遠,看清真相的我卻受到壓制,沒有話語權,所以也愛莫能助,只能聽之任之。註意我在這裏談“企業利益”,並不是說我喜歡為資本家賣命。這裏的“公司”和“企業”,只是代表一個集體,它包括了公司裏所有的員工和股東。

從這樣一個例子,你也可以看到我作為一個“現實理想主義者”的特征。這個 VP 可算是“理想主義”了,他一拍腦袋提出了“新穎”的,其它公司都沒想到的工作方式,結果卻給大家帶來了災難。我從現實和理性的角度,分析得知這種做法的荒謬,論證了“傳統做法”的和理性,與他據理力爭,維護公司和團隊的利益,再加上團結大多數有職業素養的工程師,最終我們合力戰勝了 VP 的瞎指揮,逆轉了他給團隊和公司帶來的傷害,避免了災難性的後果。

當我離開這個公司的時候,我收到了這樣一封來自團隊成員的感謝 email:

技術分享圖片

他說:“謝謝你幫助我們保持了常理和理智,把事業推向前進。我們會懷念你的!”

這樣的現實理想主義者,不管是作為員工,作為團隊的領導,還是作為公司的統帥,都會身體力行,給他們帶來幫助,避免不必要的浪費和彎路,引導企業走上正軌,走向興旺繁榮。我希望廣大 IT 工作者能理解我這裏說的東西,把自己的“偉大理想”植根於現實,避免因為自己的輕狂而走向歧途。

【轉】什麽是現實理想主義者