1947年9月9日,美國海軍準将 Grace Hopper 在哈佛學院計算機實驗室裏使用 Mark II 和 Mark III 計算機進行研究工(gōng)作。她的團隊跟蹤到 Mark II 上的一(yī)個錯誤,操作人員(yuán)發現是由于一(yī)隻飛蛾鑽到了 Mark II 的繼電(diàn)器裏導緻的。團隊清除了這隻飛蛾,一(yī)切恢複正常。當時的工(gōng)作人員(yuán)記錄了這樣一(yī)句日志(zhì):" First actual case of bug being found. " 這次著名的事件,猶如潘多拉打開(kāi)了魔盒,從此,程序員(yuán)的世界裏,bug 滿天飛。
世界上第一(yī)個 bug
在我(wǒ)(wǒ)所擔任過的角色中(zhōng),有一(yī)個崗位叫做 Development Manager,通常簡稱 DM. 記得在一(yī)次基于一(yī)款平台的二次開(kāi)發項目中(zhōng),因爲 bug 實在太多,我(wǒ)(wǒ)們幾乎拿出了一(yī)整個裏程碑的周期來 debug,于是我(wǒ)(wǒ)這個DM有了新的解釋:Debug Man.
沒有人喜歡 bug,bug 意味着錯誤、不确定性、加班、交付風險,等等…… 負面的詞語怎麽堆砌都不冗餘。随便找個有過一(yī)、兩個項目經驗的開(kāi)發者,問問他 debug 的回憶,那氣氛就跟上墳一(yī)樣。
對于 bug,開(kāi)發者的神經往往也很敏感。有個段子很有趣 —— 說的是“應該如何向程序員(yuán)反饋一(yī)個 bug ” ——
你不能直接跟他說:“這裏不對啊,是不是你程序有 bug 啊?”,要這麽說的話(huà),會直接被怼回來:“你丫的自己不會用吧!”。
你可以換個說法:“咦,這裏好像不對,是我(wǒ)(wǒ)操作錯了嗎(ma)?”,這時程序員(yuán)心裏就一(yī)咯噔:“Shit.. 不會是我(wǒ)(wǒ)代碼有 bug 吧?”
從業多年,發現有個現象還蠻有趣的:有時候,當某個 bug 被發現時,犯下(xià)這個錯誤的始作俑者會開(kāi)玩笑地爲自己解脫:“誰沒寫過 bug 啊,Windows 還有 bug 呢。” 這句托詞我(wǒ)(wǒ)也用過,感覺挺好用的,就好比:梅西都能罰丢點球,我(wǒ)(wǒ)空門沒進,也是可以理解的嘛。
但其實吧…… 這邏輯經不起推敲的。
Windows 操作系統,一(yī)款長達30多年,裝機量估計都超過了地球人口數量的巨型工(gōng)程,複雜(zá)度基本隻能靠猜。以微軟公布的資(zī)料來看:
Windows 95 代碼量約 1500 萬行
Windows XP 代碼量約 4500 萬行
Windows Vista 代碼量約 5000 萬行
Windows 7 代碼量 5000+ 萬行
以 Windows 7 爲例,超5000萬的代碼量,23個小(xiǎo)組,共1000多人的開(kāi)發團隊。如此規模下(xià)産生(shēng)的bug,和一(yī)個在辦公室裏上了1天班,寫了200行代碼,就鬧出一(yī)堆bug,搞得項目亂七八糟的,能同日而語嗎(ma)?最後再輕描淡寫地來句 “微軟也有 bug ”,不害臊?
所以我(wǒ)(wǒ)後來不用這句了,如此開(kāi)脫,水平太low。其替代方案容我(wǒ)(wǒ)稍後再講。
爲了對抗 bug,人們發明了各種各樣的工(gōng)具和手段,上至方法論,下(xià)至生(shēng)産工(gōng)具。越來越先進的 IDE, 複雜(zá)的代碼審查制度,從單元測試到集成聯調,再配上 beta 版,試用,公測,等等。凡此種種,其目标無一(yī)不是消滅 bug 。可這些琳琅滿目的解決方案的存在,反倒證明了一(yī)個悲劇:人類,實在是太容易犯錯了。
如果說凡事都有正反兩面的意義,那麽 bug 的正能量就是硬生(shēng)生(shēng)造就了大(dà)量就業機會,進而維護了社會穩定。
那麽,爲什麽我(wǒ)(wǒ)們總是無法避免 bug 的産生(shēng)?我(wǒ)(wǒ)們能不能杜絕 bug ?
答案當然是不可能了。因爲那樣一(yī)來,程序員(yuán)的日子豈不是太舒服了?不符合苦逼的定位。而且,我(wǒ)(wǒ)們所處的這個世界,但凡越是高呼要消滅的東西,越是會普遍地存在。就像蒼蠅、蚊蟲、污染、犯罪、戰争,不一(yī)而足。
按照常識,經驗越豐富的老手寫出來的代碼,一(yī)次通過的幾率更高,比如他們思考得會更周全,對異常的判斷和處理更老練,邊界條件把握得更精确,等等。所以我(wǒ)(wǒ)們可能會幻想:是不是隻要我(wǒ)(wǒ)們足夠仔細,并努力磨練技藝,通過讓一(yī)部分(fēn)碼農先老練起來,然後實現共同老練,最終就可以達到全世界開(kāi)發者聯合起來消滅bug的大(dà)解放(fàng)了?
很遺憾,這隻是一(yī)個治标不治本的思路,因爲bug是有階級的。老手們的bug相對少,隻是低級錯誤少,他們也會遇到bug,而他們的bug,往往都是一(yī)眼蒙逼的難度系數N.x的難題,不發生(shēng)在代碼層面,大(dà)多在業務層面,甚至需求設計層面,或者直接是一(yī)些不可抗拒因素(做過政府項目嗎(ma)?)。總之,萌新有萌新們的秀逗,大(dà)叔有大(dà)叔們的短路,老杆也會有自己的滑鐵盧。
bug 這個概念的起源,就預示着它的不可避免性。世界上第一(yī)個 bug 是一(yī)隻飛蛾,這劇本,誰能料到?某種意義上說,bug 就是不可預見的錯誤,能被預估并且提前做好準備的,那叫 exception, try catch 是他們的朋友。
對于爲什麽會産生(shēng) bug 的原因,著名的荷蘭計算機科學家 Edsger W. Dijkstra 有過一(yī)句經典名言:
If debugging is the process of removing software bugs,
then programming must be the process of putting them in.
這就是上文提到的那句托詞 “ Windows 也有 bug. ” 的替代方案。:)
設想一(yī)下(xià),當你從無到有的寫下(xià)一(yī)句句代碼時,中(zhōng)間的任意一(yī)個時刻,你的程序都是運行不起來的,至少也是達不到目标效果的。從效用上完全等效于充滿 bug 的一(yī)堆代碼。你可能會辯解,程序還沒寫完呢,隻是功能還沒實現,并沒有 bug 。事實上,換位思考一(yī)下(xià),缺失某個功能和包含一(yī)個有故障的功能,對于用戶而言,都是無用的。一(yī)個處于開(kāi)發階段尚沒寫完的代碼和開(kāi)發結束但寫得有缺陷的代碼,是一(yī)回事。
由此可以引申出了一(yī)個著名的命題:
Thats not a bug, its a feature request.
有時候,我(wǒ)(wǒ)們很難分(fēn)清楚一(yī)個問題到底屬于 bug 還是 feature request . 文中(zhōng)作者抛出了一(yī)個案例:用 Visual Studio 構建一(yī)個 Windows GUI 程序時沒有采用系統默認字體(tǐ)。這個算不算一(yī)個 bug 呢?
不好說。畢竟,随着軟件應用越來越普及、越來越追求所謂人性化的趨勢,傳統意義上的隻要程序能運行就不算 bug 的觀點,也在慢(màn)慢(màn)發生(shēng)改變。對于一(yī)個強迫癌用戶來說,UI 上有缺陷,那基本上整個軟件就不能用了。事實上,在當今各類 app 競争白(bái)熱化、同質化的時代,用戶體(tǐ)驗上的問題,往往是緻命的。以前大(dà)家沒得選,所以沒那麽挑剔,隻要程序能幹活就行了。如今的計算機用戶已經被寵壞了,在這樣的時代下(xià),bug 早已悄悄地泛化了。
所以,到底如何才能寫出沒有 bug 的代碼呢?
答案: 不寫代碼。
一(yī)個悲觀又(yòu)絕望卻正确的唯一(yī)解。
試着在這絕望裏挖掘一(yī)點希望吧。這個答案隐含了一(yī)個方法論: 盡可能少寫代碼。因爲 Dijkstra 大(dà)師已經說得很清楚了,編程就是制造 bug 的過程。那麽,代碼寫的越少,犯錯的幾率就越小(xiǎo),這個道理顯而易見。維護一(yī)段300行的代碼,我(wǒ)(wǒ)們很容易有信心;接手一(yī)段3000行的代碼,什麽反應就看各人素質了。
現代的開(kāi)發方式也都包含有這個思路,從 IDE 的智能提示,代碼補全功能,到每門語言都會有的各種“21天從入門到精通”的開(kāi)發框架,以及很多實戰層面的約定俗成,都是在幫助開(kāi)發者減少不必要的編碼。框架化、規範化思維能降低出錯的可能性。
事實上,就連編程語言本身的曆史發展都是按照這個思路在進行。從底層的彙編語言,到C/C++,再到Java/C#/Python……等各種高級語言,語言演化的目的之一(yī)就是爲了把程序員(yuán)從髒活、累活的工(gōng)作中(zhōng)解放(fàng)出來。
“不要重複造輪子”的精神,一(yī)方面是在指導我(wǒ)(wǒ)們提高效率,不要重複勞動成本,另一(yī)方面也是減少重複犯錯的幾率。
當代 Web 開(kāi)發中(zhōng)的各種包管理概念正深刻地踐行着這條精神,以至于在2016年3月爆發了著名的 NPM & left-pad 事件: 一(yī)段區區11行的字符串填充函數模塊,被全世界依賴,結果作者 Azer 下(xià)架模塊包的那一(yī)天,全球前端大(dà)崩潰。受波及的産品和團隊中(zhōng),甚至包含著名的 React !
這個事件讓人們開(kāi)始反思:我(wǒ)(wǒ)們是不是忘了該如何編程了? 一(yī)個功能簡單到人人都會寫的函數,卻都不約而同地選擇引入,而不是自己實現。最終,過猶不及。
寫代碼,真的很難。
NO CODE , NO BUG .
可是,如果真的隻能不寫代碼了,那麽本身就已經沒有女朋友的程序員(yuán)們,現在連代碼也沒有了,這還讓不讓人活了?
不能這樣把程序員(yuán)們給逼死了,要講人權。
有時候,當答案實在不可接受的時候,我(wǒ)(wǒ)們就該思考是不是問題問錯了。
所以,換個角度,爲什麽要追求無 bug 呢?也許我(wǒ)(wǒ)們根本就沒必要害怕 bug.
有 bug 的地方就有麻煩,有麻煩就有解決麻煩的需要,客戶就是給那些能解決麻煩事的人支付報酬的。隻處理簡單的問題,是沒有價值的,市場隻認可那些面對困難能提供解決方案的人。簡單來講,想賺錢,就别怕麻煩。
對于客戶來說,不管是 bug 或是 feature request,都是一(yī)個需要解決的問題。一(yī)個優秀的PM,可以把客戶反饋的 bug,包裝成 feature request,返回一(yī)套解決方案。然後,優秀的商(shāng)務代表出馬,簽訂補充協議。恭喜,你們的項目經費(fèi)增加了一(yī)點點。
英格蘭有句諺語:
Where theres muck, theres brass.
如此看來,“ 如何寫出沒有BUG的代碼?” 這問題,恐怕确實問錯了。