天天看點

Jamie Zawinski訪談:在折騰中成長 (zz.IS2120)

//z 2012-09-26 22:24:35 [email protected][T51,L1560,R32,V428]

Zawinski:那終究隻是理論。

Seibel:是的,不過有時這個理論也能成真,隻要主事者有良好的判斷力,架構也不是太過精緻,的确能節省時間。你能講講自己屬于哪一類嗎?

Zawinski:雖然是陳詞濫調,不過我還是要重提:更差就是更好(worse is better)。假定你花時間建構了完美的架構,滿足了你的全部需求,從1.0版一直用到5.0版,一切都很棒;猜猜結局如何:1.0版釋出用了三年時間,而你的競争對手隻用六個月就釋出了他們的1.0版,結果就是你出局了。

你的競争對手六個月就推出的1.0版,代碼品質低劣,他們可能得花上兩年時間重寫代碼,那又怎樣?他們有機會重寫,而你早就丢了工作。

Seibel:很多時候,也許是期限逼近,時間緊迫,你扔掉了大塊代碼,因為你認為另起爐竈反而更快。

Zawinski:是的,一定會碰到,那時你得趕緊脫手,避免更多損失。

Seibel:就是這一點導緻開源軟體開發中你深感遺憾的無休止的重寫?

Zawinski:是的。但撇開效率不談,從另一角度來看,寫自己的代碼遠比弄清楚别人的代碼來得有意思。這一切就那麼自然而然地發生了。其中一點就是所有東西總是在不停重寫,結果一樣都沒完成。如果你是那些開發人員之一,那也不錯,因為總有東西折騰,當然前提是你更熱衷于搗鼓計算機,而不是将其作為達成目的的手段。

以程式設計為樂

Seibel:說到搗鼓計算機本身,你現在是否仍以程式設計為樂?

Zawinski:有時。我現在淨幹些系統管理者的髒活,我很受不了,也從沒喜歡過。我喜歡做XScreenSaver的相關開發,從某些角度來看,螢幕保護程式(即實際的顯示方式而非XScreenSaver架構本身)堪稱完美程式,因為它們基本上都是從頭寫起,養眼好看,絕無所謂的2.0版本。

Seibel:你喜歡做數學計算、求解幾何和圖形之類的謎題?

Zawinski:是的。以這種方式顯示,這個抽象的小方程式會是什麼樣?或者,怎樣才能讓這些方塊移動時更生動,不那麼生硬,就像是計算機在正常搬移東西?諸如此類的問題。還有,怎麼處理這些正弦波,才能讓它看上去更像是在彈跳?此外,我還會寫些簡單蹩腳的shell腳本,聊以自用。

Seibel:除了有兩百萬人用上你的軟體,你還以程式設計的哪些方面為樂?

Zawinski:這個問題有點難。我想大概是解決問題的過程。

Seibel:你感覺到代碼的美嗎?美感是否在可維護性之上?

Zawinski:是,當然是。任何東西隻要表達恰當,不論精煉,抑或平淡,都是具有美感的。

Seibel:你認為程式設計和寫作是類似的智力活動?

Zawinski:從某些角度看,我認為是這樣。當然,程式設計要嚴格得多。但就表達思維的整體能力而言,兩者非常相似。不要不着邊際,說出口之前先想一下準備說什麼,然後盡量言簡意赅。我認為這正是程式設計和寫文章的共同點。

優秀和差勁程式員的差別

Seibel:接下來我們聊點程式設計的細節。你怎麼設計自己的代碼?如何組織代碼的結構?不妨以你最近OS X上的XScreenSaver移植工作為例講講。

Zawinski:好,首先我會随意鼓搗一番,寫些短小的示範程式,最後不會再用。

接着,為每個X11調用建立一個空函數(stub),然後再開始慢慢逐個實作,弄清楚自己準備如何實作這個,怎麼實作那個。

另外,在Mac平台上,需要編寫啟動代碼。這有點像粗線條繪制,搭建基礎架構。剩下的就是一個接一個地移植屏保護程式。

Seibel:這麼說來,針對每個X11調用,你都要編寫相應的實作。你有沒有發現自己累積了大量非常相似的代碼?

Zawinski:有,當然有。通常當你第二或第三次剪切和粘貼相近代碼時,就不能再剪切和粘貼了,而應抽取成子程式。

Seibel:假定再次開發規模與郵件閱讀器相當的軟體,你前面提到開始會寫下幾段文字,列出一組功能特性,這是你着手編寫代碼之前所做的最細粒度的準備嗎?

Zawinski:是的。也許還會簡略描述庫和前端的差别。不過也可能不會。如果是獨自一人開發,我不會關心這些,畢竟那部分我再清楚不過了。随後,我采取的第一步是自頂向下或自底向上開始。無論采用哪種方式,我都會先在螢幕上顯示一個視窗,包含若幹按鈕,然後逐漸深入,開始建構那些按鈕的功能。

我發現盡早在螢幕上顯示一些東西,有助于集中注意力去解決問題。這能幫我決定下一步做什麼。

Seibel:你是否經常重構以保證代碼内部結構的一緻性?或者你一開始就很清楚如何将代碼各部分整合在一起?

Zawinski:通常我一開始就很清楚。編寫程式的第一個版本時,我一般會把所有代碼寫在一個檔案裡。随後,我開始分析那個檔案的結構,比如有幾段代碼非常相似。那些代碼已有上千行,何不把它們挪到另一個檔案裡。另外,隻有真正開始編寫代碼,發現情況不受自己掌控,你才能體會到這點。

Seibel:我注意到優秀程式員和差勁程式員的差別之一是,優秀程式員在不同抽象層之間切換自如,遊刃有餘,修改時仍能保證各層獨立,并且選擇最合适的那一層進行修改。

Zawinski:顯然決定在哪裡進行修改很有講究,而且可能關系非常大。

對我而言,我認為最重要的一點是在建構全新的程式時,應當想辦法盡快寫好自己能用的程式,哪怕隻實作一點功能。這樣你就能真正知道下一步做什麼。

調試經驗談

Seibel:下面,我們再談談調試相關的話題。對初學者而言,你推薦哪個調試工具?列印語句?符号調試器?形式證法證明程式正确性?

Zawinski:過去這些年裡變化太大。當初使用Lisp機器時,我做了一些修改,基本上Lisp偵聽器(listener)就成了檢查器,隻要它列印出對象,就會出現一個上下文菜單,你可以點選菜單項,傳回指定的值。這麼一來,很容易跟蹤一組相關對象和類似的東西。這就是我早期思考問題的方式。深入代碼内部,來回修改,不斷試驗。

後來,我開始編寫Emacs内部的C代碼,使用GDB時,我試圖沿用同樣的方式。不過一直沒有取得很好的效果。随着時間的推移,我漸漸地不再使用這類工具,而是直接插入列印語句,重新運作程式。如此反複,直到搞定為止。如今,人們似乎不清楚何謂調試器,大多數人都用列印語句。

Seibel:這其中有多少是因Lisp和C語言的差別而非工具的差別所緻?一處差別是在Lisp裡你可以測試一小部分,調用你不确定工作正常與否的小函數,然後中斷函數執行,檢查運作狀态。而C代碼呢,複雜之極,必須運作整個程式,然後在某個地方設定斷點。

Zawinski:與C語言相比,Lisp這類語言本身更适合調試。另外Perl、Python和類似語言在這方面也更具Lisp的特質,不過我還沒看到多少人真的按照Lisp的方式去做。

Seibel:你們是否使用斷言,或其他比較正式的文檔編寫方式或真正檢查不變量的方式?

Zawinski:顯然,插入斷言語句對調試而言是個好主意,如你所說,還有利于文檔化。但随之而來的問題是,在産品代碼中,斷言失敗時會怎麼樣?你會怎麼做?我們商定的做法是傳回零,好讓它繼續運作下去。

許多程式員都有種本能:“我必須呈現錯誤消息。”不,根本不用。沒人會在意那個。

Seibel:你會為了調試而逐句檢視程式,或者,如有些人建議的那樣,寫好程式之後,把逐句檢視作為檢查代碼的方法?

Zawinski:不會。隻有調試程式時,我才單步檢視代碼。有時是為了确認代碼寫得沒問題。但很少這麼做。

Seibel:那你是怎麼調試代碼的?

Zawinski:我會先審讀代碼。然後插入一些代碼,嘗試解決存在的問題。總之視情況而定,很難一概而論。

Seibel:與調試相關的,是測試。在Netscape,你們有專門的QA組,還是你們自己測試所有項目?

Zawinski:兩個都有。我們會一直運作軟體,那是最好的一線QA。另外我們有個QA組,他們會從頭到尾做詳細的正式測試。

Seibel:那麼開發人員那一級的測試呢,比如單元測試?

Zawinski:沒有,我們從來不做那類測試。

軟體維護

Seibel:說到這個,正好聊聊軟體維護相關的話題。你怎樣設法了解别人的代碼?

Zawinski:我會直接一頭紮進去,開始閱讀代碼。

Seibel:那麼你會從哪裡開始呢?從第一頁開始,按順序讀下去?

Zawinski:有時會這麼做。更常見的是學習如何使用某個新的庫或工具包。幸運的話,你能找到一些文檔,還有API。最後弄清楚自己可能會用到的那部分,或者弄懂它是怎麼實作的。按這個思路一直做。或者,對于諸如Emacs的程式,有可能從底層着手。基本上就是不斷抽取,直到現出骨架。

Seibel:我們讨論過重寫為何比修正更有趣,但重寫并不總是好主意。我想知道你怎麼拿捏兩者之間的界限?

Zawinski:一開始我隻是修正缺陷,并試着做些優化,結果原有代碼不見蹤影,後來幾乎變成了重寫。

當然,我在Lucid Emacs裡添加的許多東西不像重寫位元組碼編譯器那樣有充足的理由。實際上,我做許多東西的動機在于讓它更像Lisp機器,更像我熟悉的Emacs,而那其實是我熟悉的Lisp環境。于是我添加了大量東西,設法讓Emacs在許多方面不再是半吊子的Lisp,比如應該用事件對象取代包含數字的連結清單。現在回想起來,那些修改當屬最大的問題。那類修改導緻與第三方庫的相容性問題。

Seibel:當然,那時你不知道會出現兩個Emacs。

Zawinski:的确。不過即使沒有XEmacs,也不會隻有一個Emacs,也無法避免相容性問題。事後看來,如果我早意識到那些修改有那麼大的影響,我也許會采取不同的做法。或者多花些時間,讓原來的方式也能工作。

Seibel:代碼可讀性事關維護,前面你談到一些編寫易讀代碼的做法,有哪些特性可以讓代碼更易讀?

Zawinski:嗯,顯然是注釋。寫下期望是什麼,實際又做了什麼。如果是建立資料結構,那就描述其成員布局。

Seibel:那結構呢,你是以自頂而下還是自底向上的方式組織自己的代碼?

Zawinski:通常我會把葉節點放在檔案頂部,盡力保持那種基本結構。然後,通常是在頂部之上,編寫API注釋說明。

Seibel:那麼假定為了進一步論證,你準備重新出來工作,組建一個開發團隊。你會怎麼組織安排?

Zawinski:在我看來,最好的安排是一個團隊不超過三個或四個人,成員之間緊密合作,每天一起共事。這種做法可以按比例放大很多倍。

Seibel:怎麼協調這些團隊呢?

Zawinski:我們會約定好各子產品之間的接口。就我的了解,要讓子產品之間互動自如,最佳做法就是保持子產品本身真正簡單,減少可能出錯的途徑。

至于怎麼劃分則完全視項目而定。

Seibel:這麼說來,在Netscape,你們會把事情分割開來,每個人負責軟體不同的子產品。有的人認為那麼做很重要,其他人則認為團隊共同擁有所有代碼的做法更好,你怎麼看?

Zawinski:兩種方法我都用過,各有優勢。讓每個人都擁有全部代碼,我認為不切實際,因為代碼實在太多。

Seibel:當還是資曆尚淺的程式員時,你導師做了哪些對你有幫助的事情?

Zawinski:我覺得關鍵在于他們能意識到什麼時候該提升員工的等級。

Seibel:你閱讀代碼主要是因為你正在開發相關功能,抑或你隻是想探究它是怎麼工作的?

Zawinski:隻是到處看看,我想知道那是怎麼工作的。把東西拆開的沖動是将人們帶入這一行當的一大原因。

對自學的程式員的建議

Seibel:你覺得自己自學了計算機科學,抑或隻是學習程式設計?

Zawinski:嗯,這些年我的确學了些計算機科學,但目标是學習程式設計。讓機器做事情是目标,計算機科學則是達成目标的手段。

Seibel:你是否認為那是種缺失,有沒有想過但願自己曾以更系統的方式學習過計算機科學?

Zawinski:有時的确會有那種想法,特别是早期,我會想:“天哪,我什麼都不懂。”這隻會叫人窘迫,還會讓人缺乏安全感。要是多花點時間在求學上,我的生活的确會全然不同,不過那時我做了我該做的。

Seibel:你有過相反的感覺嗎,覺得身邊的計算機科學家并不像自己那樣懂得程式設計的真谛?

Zawinski:我經常有那種感覺,不過,“哇,你們這些家夥搞錯對象了。”這種想法其實并不多,更多是覺得:“哇,我們隻是興趣不同。”

Seibel:你主要是靠自學的。對那些自學的程式員,你有什麼建議?

Zawinski:我不知怎麼就跌跌撞撞做了程式員。我當時做的一些決定,導緻了其他決定,最終成了現在的我。

我不時收到郵件,内容大體是“我想成為程式員,我該怎麼做?”或是“我該不該上大學?”我怎麼回答得上?要是在1986年問我,我也許還有不錯的答案。現在人們已不可能循着我當年的路走,因為那條路已經無迹可尋。

Seibel:計算機方面的書呢?哪些計算機科學或程式設計書籍是必讀的?

Zawinski:老實說,計算機書我讀得并不多。我一直都推薦的書是《計算機程式的構造和解釋》(Structure and Interpretation of Computer Programs),許多人都怕讀這本書,因為Lisp味太濃。不過,這本書在不教語言的前提下教授程式設計,做得非常到位。

還有一本書,是關于調試的,微軟員工寫的【譯者注:疑為微軟出版社1993年出版,Stephen A. Maguire的Writing Solid Code - Microsoft Techniques for Developing Bug-free C Programs,圖靈影印版《程式設計精粹:編寫高品質C語言代碼》】,主要介紹如何有效使用斷言。還有本書叫《設計模式》,人人追捧,稱其為當時最好的作品,不過在我看來,這本書一派胡言。

搗鼓折騰很重要

Seibel:有程式員必須具備的關鍵技能嗎?

Zawinski:嗯,好奇心,把東西大卸八塊的好奇心。好奇心是擷取知識的主要途徑。把東西拆開,用心研習,才能做好自己的東西。至少我是這麼做的。計算機的書我讀得很少。我的經驗主要來自不斷挖掘源代碼和參考手冊。

Seibel:你讀過Knuth的《計算機程式設計藝術》【編者注:即将由人民郵電出版社出版】(The Art Of Computer Programming)嗎?

Zawinski:沒讀過。這也許是我真正該讀的一本書。

Seibel:這本書很難讀,需要很好的數學功底才能真正讀懂。

Zawinski:數學我可不在行。

Seibel:很多程式員都有數學背景,大量計算機科學理論都離不開數學。這麼看來,數學也并非不可或缺,你就是很好的例子。想成為優秀程式員得具備多少數學知識或數學化的思維?

Zawinski:嗯,那要看你怎麼劃分,什麼算是數學的,什麼不算。

不過,我還不至于認為做程式員不需要數學。但是,我總覺得比起數學,程式設計與寫文章有更多共通之處。這就如同你正在寫故事,嘗試向非常愚鈍的人——詞彙有限的計算機——表述觀念。你已經掌握自己想表達的觀念,以及用于表達的那些工具,你會使用哪些詞,序論和總結陳述會寫成什麼樣?程式設計也大抵如此。

談到文章,口味問題就凸顯出來了。用一段文字描述某樣東西,可以描述得體,也可以描述出彩,很有特色。這些同樣适用于程式。程式可以隻是完成任務,或者,隻要組合得好,還能做到容易了解。

Seibel:為什麼口味很重要?隻是為了讓自己滿意,還是從實踐角度來說優美的代碼更好?

Zawinski:很大程度上,優美和易維護是相似的,或者說息息相關。

Seibel:你是否認為,如今在程式設計上能獲得成功的人已不同往常?

Zawinski:當然,現在已不太可能從無到有編寫沒有任何依賴的程式。

Seibel:要是你現在隻有13歲,看到現在程式設計的方式,你還會被吸引嗎?

Zawinski:這太難回答了。我不認識13歲大的孩子,也不知道當下的世界變成什麼樣了。

我覺得正是折騰搗鼓,比如拆開錄音帶倉背部,檢視齒輪是怎麼齧合在一起的,這種探索吸引我走上了程式設計之路。除了樂高機器人(LEGO Mindstorms),在我看來,現在人們已經沒什麼機會循着我當年那條路成長。不過,也許我是錯的。

Seibel:另一方面,程式設計本身也變得更容易。隻是讓計算機做些正常任務的話,你根本不必一開始就掌握晦澀複雜的彙編語言。

Zawinski:沒錯。我覺得今天的孩子想要程式設計可以從搭建Web應用或編寫Facebook插件等開始。搭建LiveJournal的Brad Fitzpatrick是我朋友。當初寫LiveJournal時,他隻是随便玩玩,寫了個Perl腳本,以便他和朋友可以用它留言:“我準備吃午飯去了。”他開始的方式就是寫個簡短的Perl腳本,然後放在Web伺服器上。這種現象也許會愈演愈烈。

@IS2120#CNBLOGS.T2169364049[T1,L65,R1,V259]:備忘$ € ₤ ₭ ₪ ₩ ₮ ₦ ₱ ฿ ₡ ₫ ﷼ ¥ ﷼ ₫ ₡ ฿ ₱ ₦ ₮ ₩ ₪ ₭ ₤ € $

繼續閱讀