編程難在哪裏?一個美國實習生的故事

作者:阿萊克西斯

來源:

https://www.zhihu.com/question/22508677/answer/276595266

記得之前組裏來了一個美國實習生小夥子,很geek的那種,幹活快,一天能給你寫2000行代碼(我code review的速度跟不上他寫的速度),讓做什麼東西,上午告訴做個這個功能,下午就能在測試環境跑起來演示了。

跟他單獨開會的時候,他說覺得普通的編程沒什麼意思,太簡單了,寫程序這方面已經沒什麼追求了,他比較想跟我研究大數據的框架,數據庫,或者機器學習之類的工作,做設計,早日脫離代碼這種無腦工作。

我足足花了1周時間,每天review他的代碼到凌晨。給他寫的comments反饋快趕上我在知乎寫的答案文章之和了。。。期間幾小時幾小時的開會論戰,孩子狂,語速快,腦力靈,辯論角度刁鑽。他天天要與我論戰,看我的評語,速度還算慢下來了。

沒來得及討論完,隔週我要休假了,2周。交代了些他要做的工作。

2週迴來,讓他改的那個java包爆炸了,本來我們一個支持了7個功能的框架包,總代碼量也就5k吧,等我回來這包代碼量1w5+。也就是說他爲了一個小功能加了1w行代碼。

這沒法review,只能跟他坐一塊,先讓他給我講講這代碼都幹什麼的,然後他說:

(沉思+100)這塊我現在也看不太懂當時爲什麼這麼寫了。。。

(沉思)這邊寫的比較複雜是因爲當初那邊是那樣寫的,所以這邊沒辦法才只能這麼寫。

(沉思)把當初那邊改好很麻煩,影響也很大,不如就這樣吧。

(沉思)這裏這麼寫是因爲你看着是這樣的, 然後這裏有這個邏輯,然後這裏。。。

(來來回回翻n個類之後)。。。 所以你看我這裏雖然寫的比較詭異,但是完全沒問題的!(得意ing)

(沉思)這邊做得這麼奇怪是因爲有個bug,通過這麼寫,這個就bug沒了,我也不知道怎麼回事。。。所以你看我在這邊註釋,這行不能刪了。。。

(沉思)我覺得這個功能很酷,你們雖然現在不需要,不過有總比沒有好吧,將來如果……%¥……&%&……%*7&%……*%…(我沒聽懂)的話,這個就很有用!!...

一次一次被我打回去重寫,後來總算簡化成大概5k行了。臨走時候跟我說:你這樣編程也太難了。。。

再後來由於一些額外複雜的代碼造成我們實現新東西會很複雜,我又重寫了一遍,總共大概不到1k行代碼。

這裏邊有幾件事情我想說:

  • 1. 做出來容易, 做正確難,這裏做出來指沒bug且完成需要的功能,這是最基本要求,不多加討論。這裏正確,不是指功能正確,而是指程序可以很容易理解,理解意圖, 理解如何做到的,理解爲什麼系統不會出錯。理解爲什麼要這麼做。正確是現在怎麼寫不會挖坑害將來的人,現在怎麼寫能讓別人1年後看你代碼時候不可能理解錯你現在的意圖,現在怎麼寫能在別人將來犯錯的時候提示他你錯了。
  • 2. 編程是給未來的未知人講故事,你無法知道將來這個人是誰,他都懂什麼,他經歷過什麼,這個系統將來已經是什麼樣子了。我們需要在這種無知,缺乏信息的情況下做決定,從千萬種把這件事做出來的方法裏,選出你覺得最能把這個故事給講好的那種方式,把故事寫下來。編程是一種溝通,溝通是一種藝術,用程序跨越時空之溝通則是一門屬於程序員的特有的藝術,就好比數學家用數學公式來溝通。 coding is all about the art of communication(引用)。
  • 3. 壞的決定會導致壞的決定,甚至導致人們去扭曲一個好的決定去迎合壞的決定。垃圾會製造垃圾,一個放在系統裏不經清理的額外複雜度,會導致更多的額外複雜度的生成。
  • 4. 每個人甚至同一個人的不同時刻都有自己的不同的製造額外複雜度的缺陷,比如我每年去看去年自己寫的代碼,覺得都是垃圾。

然後我又想問幾個問題:

我們所在的部門,所在的組,公司,它們的文化,到底是關心作出了一個東西,還是關心做好了一個東西。一個總是給系統添加垃圾,留坑給後人,但是能很快做出能跑起來的系統的程序員,我們到底認爲他是做了好事還是做了壞事?我們到底認爲他很強,還是他很弱?用超過必要而爲了突顯技術實力(或者練手)的複雜工具,技術框架搭建系統,做完跑路,在一個組,一個部門,一個公司,那裏的文化,到底應該是鼓勵還是抑制這種行爲?我們又應該如何在一個環境中,去倡導推崇什麼樣的文化,相遇什麼樣的人?

人與文化,決定了什麼人留在這裏,什麼人離開,什麼人吸引什麼人,什麼人成長成什麼樣子。而設計/技術這些枝末細節則必順應此中的人與文化而自然變化,或自愈,或走向毀滅;哪怕在惡劣的環境中,向下引導,向上規諫,潛移默化,最終改天換日,此爲編程之大道也!

下邊是定理證明

  • 最小垃圾存在定律:定義垃圾爲系統的總複雜度減去系統的本質複雜度;那麼得到:如存在多種方法可以設計與實現一個系統或功能,存在且只存在一種實現會引入最少的垃圾。
  • 垃圾與複雜度正比定律:根據定義可得,系統存在的垃圾越多,系統越複雜。
  • 垃圾倍增定律:基於已有垃圾量a的現狀來演化,進化此係統,增加的新垃圾量與已有垃圾量a成正比。
  • 系統腐敗定律:當基於垃圾量a來實現新功能的cost大於新功能本身的價值時,系統腐敗,需要重構。
  • 戰鬥人員負戰力定律:如果程序員a引入的垃圾,在n次迭代中經過倍增所造成的成本,大於其所清掃的垃圾經過倍增所獲得的機會成本,和其實現的新功能價值之和。此時,我們稱此程序員戰力爲負值,其戰力絕對值與其引入垃圾的能力和其清掃垃圾的能力的差值成正比。
  • 以一敵百存在定律:由負戰力定律可知,對所有的自然數n,一個正戰力的戰鬥人員的戰力 > (負戰力戰鬥員1+負戰力戰鬥員2+ … 負戰力戰鬥員n)的戰力和。
  • 系統本質複雜度不可知定律系統表徵複雜度無限接近本質定律:取決於戰鬥人員的知識量,經驗,天賦等,對於任何戰鬥人員n,都必定存在一個戰鬥人員m(考慮歷史長河)使得戰鬥人員n觀察系中的純淨無垃圾系統(複雜度總爲1)是戰鬥人員m觀察系中的含垃圾系統(複雜度爲1+x),這使得在所有觀察系中(包含外星生物),系統的表徵複雜度(或者說觀察複雜度)無限趨近與本質複雜度。然而我們只能通過觀察來感知事物的本質複雜度,卻永遠無法得知我們離本質複雜度還有多遠。

以有限的生命去追求可以無限的提升的淨化方法與視野,我們稱之程序藝術家,也就是SDA(Software Development Artist)。

寫程序,你快樂嘛?

寫在最後,看到大家最關心的是他拿到正式錄取資格了麼?還有也許通過我的描述關於他的這個側面,你會覺得他很不稱職。其實不是的,他代碼寫的絕對是平均值往上的水平,他的問題在於:

  • 1. 他根本沒有想過去簡化業務邏輯,所以很多符合最初需求的代碼在簡單優化業務邏輯之後完全不需要。
  • 2. 他自己加了很多功能。
  • 3. 他自己加了很多自以爲是的優化,比如用一個算法估算某個函數的輸入數組的最大可能值,然後用那個值來初始化一個數組,因爲這樣就不會重新分配內存了(他原話)。
  • 4. 抽象能力有限,這個畢竟經驗少, 年輕。
  • 5. 濫用設計模式。關於設計模式,最多程序員被絆住的一關:設計模式是面向對象編程模型中,應對經典問題的經典解決方案。這裏就有兩個問題:第一,設計模式的場景用對了麼?第二,爲什麼要用面向對象範式?選擇編程語言範式時,要從表達力最弱最簡單的語言範式開始選擇。這叫做最弱表達力原則。而面向對象範式作爲最複雜,表達力最強的語言範式,在大多數時候都可以避免使用。關於第二點的論述證明,你可以看《concept techniques and models of computer programming》這本書。注意,這裏說的是語言範式,而不是語言。即使你用java,如果你從來不使用mutable(專業詞彙)的功能和繼承,那麼你就沒有使用面向對象範式。

他其實有非常強的解決問題的能力,想法天馬行空,通過自己設計算法來猜函數可能需要的數組大小就可見一斑,還有一個從s3(專業詞彙)讀數據的需求,他不是簡單調api完了,而是寫了一個環狀buffer(專業詞彙),使得網絡、硬盤、app可以在理論上最大效率的適應程序當時的場景。爲了協調異步,他自己發明了一個很笨拙的promise(專業詞彙),這非常厲害,一般的實習生哪怕sde1可能都寫不出來。可惜的是場景會隨業務邏輯激烈變化,今天的優化可以是明日的累贅,這就叫做過度優化,過度優化是一種強耦合,會把你的系統死死地釘死在當前版本。

他只是不明白簡單是美這件事情而已。如果能有人幫他斧正,日後必成大器。

他最終拿到了正式錄取資格,這其中還有個小波折,終審的bar raiser(amazon內部的一個可以一票否決招聘結果的角色)看到他在代碼複查系統裏跟我的各種激辯,覺得這人不能留。好說歹說纔給了正式錄取資格。不過最後人家沒接,去讀博啦。

最後:在一個相對乾淨的環境寫程序,不斷找出新的本來以爲不是垃圾的垃圾,對我來說,是一件非常愉快的事情。然而幫別人打掃他本就不該製造的垃圾則是非常痛苦的一件事。 寫程序,本應是多麼快樂的一件事啊!

相关文章