您當前的位置:首頁 > 收藏

軟體隨想錄

作者:由 陳天 發表于 收藏時間:2015-08-18

(一)

軟體領域有個叫格林斯潘的哥們,估計大家都不怎麼熟悉,但下面這句話寫過程式碼可能沒幾個不知道:

Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp。

任何C或Fortran程式複雜到一定程度之後,都會包含一個臨時開發的、不合規範的、充滿程式錯誤的、執行速度很慢的、只有一半功能的Common Lisp實現。

這便是所謂的「格林斯潘第十定律」(不用找了,沒有前九個定律)。這老兄一輩子也沒特別NB的作品,但卻有這麼一段註定要在程式設計師鄙視鏈上流芳千古的定律。

作為一個C程式設計師,在數次領教了這句話的威力後,我終於在去年末殺入Lisp陣營,首先拿了racket開刀,學得如痴如醉,隨後又禁不住誘惑,跳入clojure這個golden club,接受Rich Hickey和David Nolen等牛的醍醐灌頂。雖然殺進來前有個evil的私心:想讓自己站在鄙視鏈的頂端傲倪四方;殺進來後卻是戰戰兢兢,汗不敢出,學到的東西越多,自己越是把自己鄙視得一無是處。

學習一門對你而言「離經叛道」的語言相當於為自己開闢了一個全新的天地,讓你走出達克效應(D-K effect)。那感覺,就像C程式設計師第一次使用python的repl,第一次看見list,dict優美地想要哭。當然,語言有各自的適用場景,高下並不能以是否有repl論斷,而在於你能從中得到多少你本不知道的智慧。一個python程式設計師,學習C程式碼,弄明白了preprocessor,compiling,linking,loading,在disassemble的過程中如庖丁解牛般「看」到了系統的脈絡,也會幸福地哭。

這便是

學習對自己而言是離經叛道的語言

的好處。python程式設計師學C,學erlang,學clojure,學haskell,都屬離經叛道;學ruby卻不是。這哥倆需要paradigm shift的地方著實不多,連Cython和MRI的GIL(Global Interpreter Lock)都親如一家人。學任何東西,paradigm shift非常重要,有點像我們常說的「破而後立,敗而後成」的意思。它是讓人不斷成長的一個關鍵。

(二)

C和彙編有如太祖長拳。無名小廝耍起來也就是小朋友亂斗的效果,在蕭鋒手上,卻是招招致命。語法本身極其簡單,關鍵詞手腳並用都能數得出來,寫個hello world更是兩分鐘就能搞定,但只有你對系統融會貫通,練好各種內功心法,才能發揮其巨大威力。

PHP/javascript 是吸星大法。練起來不難,沒內力的入門很快,網上到處是現成的模組,據為己有後立刻等級提升。不過其致命的缺陷導致你只能在準一流遊走,用不好關鍵時刻還會反噬。

Python/Ruby是太極劍,變化多端,小到一個卑微的指令碼,大到高逼格的機器學習,都能輕鬆對付。可是performance和直譯器實現上的先天不足(Guido/Matz其實挺冤:我給你們個電鑽,你們非要用它來鑽鋼板,效能不好,怪我咯)是其破綻,導致遇到計算密集/IO密集型的問題,處理起來很是傷腎。

Erlang/Elixir像是降龍十八掌,大開大闔,剛勁有力。可是入門不易,思想深邃,會的人不多,只能靠自己苦苦鑽研。actor model,supervision tree,messaging passing,pattern matching,光理解透了,便是半載光陰,練出名堂,那出手便是大師風範。

clojure好似獨孤九劍,「風雷是一變,山澤是一變,水火是一變」,變化多端,核心是以不變應萬變。需求縱使千變萬化,提綱攜領,找到破綻,然後以macro和polymorphic化之。程式碼即資料,資料即程式碼,以輕御重,化煩(object)去簡(function),退則滴水不漏,進則攻無不克。

Haskell像是乾坤大挪移,沒有深厚的內力修為很難參透。lazy computation/monad乾的就是牽引挪移這樣匪夷所思的事情。一個程式,不過是從輸入到輸出中間經歷的一系列transformation,你是一招一式傳遞資料,還是傳遞運算,斗轉星移?回答了這個問題,haskell也就算是入了門。

(三)

Professor Randy Pausch(是的,這個名字經常看我文章的都耳朵起繭子了)講過一個故事。他小時候打橄欖球,教練在讓大夥做對抗訓練的時候卻並不把球給他們。有個孩子不爽:教練大人,我們這是在打橄欖球呢還是在打橄欖球呢?教練讓孩子們停下來,問:

「一場比賽有多少球員參賽?」

「22人」

「有多少人手裡拿著球?」

「1人」

「我就是教你們剩下21人的打法」

Randy在回顧這個故事時說:fundementals,fundementals,fundementals。酷炫的東西就像冰山浮起的部分,我們只是看不見那更為關鍵的底部。

所以學一門語言,語法只是那飛來飛去的橄欖球。你接得住球,扔得遠,並不代表你會無球跑動, 防守時巧妙卡位,進攻時神出鬼沒。學一門語言沒有領會其基本思想,也只能流於表面。

(四)

我們寫程式碼寫久了,有些東西總是繞不過去:流入系統的請求(Request)首先是要被授權(authorize)和鑑定(authenticate)的,然後要被驗證(validate)的,接下來是要被路由(route)的,然後是就是各種各樣的變換(transform),如有必要,記錄(persist)需要儲存的中間結果,最後輸出(Response)。

所以,格林斯潘說的其實不完全對,對於大部分人而言,寫一個軟體,就像在寫一個臨時開發的、不合規範的、充滿程式錯誤的、執行速度很慢的、只有一小部分功能的編譯器。我們只是使用未經良好設計的,原始而粗糙的手段,用拼湊出來的類,函式,if-else攢了一個只能用在特定場景的編譯器而已。

或者資料庫。其實資料庫也是編譯器,編譯器也是資料庫。看你怎麼理解。

(五)

現在似乎已經不是lex/yacc 或 bison/flex的時代了。我親眼看見一個同事在費力地用perl一行行解析某個系統的資料檔案,卻壓根沒想到寫個BNF。BNF對他來說,不是一種選擇。

資料庫也漸漸沒有store procedure,trigger什麼事情了。生在web下,長在創業潮的新一代已經把這些勞什子定性為vendor lockin的髒東西,輕易不碰。我自己也有很多年沒寫過trigger了。最近對付一個沒有hook介面的第三方的老java系統,為了追蹤某個表下的特定的列的更新,好讓我的程式碼能夠不修改這系統(我也沒能力改一個複雜的EJB系統),我又重抄舊業,耍起了trigger和temp table。同事看到,說:哈?這玩意怎麼用在ORM裡?

rich hickey談到tradeoff時說,你得先至少有兩個solution,才談得上tradeoff。然而,大部分時候我們找到一個solution都不容易,何談兩個三個,可不幸的是,幾乎每個人寫程式碼的人在做design的時候都會把tradeoff掛在嘴邊。

(六)

我們在選擇技術,完成工作的時候,忘記了軟體其實是在為商業目標而打工。一切不以實際商業目標而最佳化的程式碼都是在耍流氓。作為程式設計師,我們很容易進入到programmer-centric的境界:

這特麼不是bug,使用者用錯了

提這需求的客戶太2B了

要的功能已經實現了,沒人用不是我的錯

商業上看中的是 cost/benefit,ROI,time to market,profit;程式設計師看中的是測試透過,程式碼提交,沒事少改需求。

我朋友在的一家創業公司,研發狀態混亂無比,程式碼沒有review,沒有UT,沒有CI,開發人員自己測吧測吧就push production,也不寫log,系統區域性癱了都要使用者發現才知道。但人家業務做得好。軟體爛,欠了一屁股技術債,總是能透過招入更好的人進來慢慢彌補的;業務爛,軟體再NB,CI pipeline輕舞飛揚,又如何?

我最近研究的一個產品instavest,UI簡陋地連我都想幫他們改改 —— 同樣是用bootstrap,我覺得UI水平爛如我這程度,都能勝過他們。然並卵。

所以程式設計師別抱怨自己不受重視,沒有話語權。business vision才是核心。你不鍛鍊business vision,找不到產品能被人使用,客戶願意購買的點,只能是打工的角色(做到CTO也是打工的角色);即便創業,也是一個理論上來說容易被替換的角色。

如果您覺得這篇文章不錯,請點贊。多謝!

歡迎訂閱公眾號『程式人生』(搜尋微訊號 programmer_life)。每篇文章都力求原汁原味,北京時間中午12點左右,美西時間下午8點左右與您相會。