軟體隨想錄
(一)
軟體領域有個叫格林斯潘的哥們,估計大家都不怎麼熟悉,但下面這句話寫過代碼可能沒幾個不知道:
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點左右與您相會。
推薦閱讀:
※閑扯技術合伙人
※Soylent, 食物的未來 (food 3.0)?
※AWS 及 IAM 答疑
TAG:迷思 |