Bernie Cosell#
1969 年,ARPANET(後來成為網際網路核心的網路)的前兩個節點上線時,每個封包都透過名為 Interface Message Processors (IMPs) 的特殊電腦路由。IMP 由 Bolt Beranek and Newman (BBN) 設計建造,其軟體由三位程式設計師撰寫,Bernie Cosell 便是其中之一。他在 MIT 大三時離開學校,加入 BBN 全職工作。
Cosell 原本被聘為應用程式設計師,參與建造最早期的分時系統之一,很快就轉向系統程式設計,成為 PDP-1 分時系統的「沙皇」,負責完成作業系統程式碼並維持系統運行。在 BBN 的 26 年職業生涯中,他以大師級除錯者和「修理工」(fixer)聞名——專門被指派到陷入困境的專案中讓軟體能運作。他還為了磨練 Lisp 技能,基於 Weizenbaum 的論文寫了 DOCTOR(ELIZA 的一個版本),該程式隨著 ARPANET 和 TENEX 作業系統傳播,比 Weizenbaum 的原版散布更廣。
1991 年 Cosell 離開 BBN,在維吉尼亞州買了一座牧羊場,不再全職寫程式,但教授程式設計和電腦安全課程。
背景與學習程式設計#
高中時期#
- 就讀紐約市 Bronx High School of Science
- 該校可能是全美第一所擁有自己電腦的高中——IBM 捐贈了一台 IBM 1620
- 數學系主任正在寫一本程式設計書籍,Cosell 最終負責除錯書中所有範例
- 高中時期幾乎唯一記得的事就是學習程式設計
- 1620 使用打孔卡片和一台 IBM Selectric 打字機作為輸入/輸出控制台
- 1620 沒有內建算術硬體,使用查表算術(table-lookup arithmetic)
- 也學會了如何接線插板(wire plug boards)——十年後在 BBN 這項技能居然派上用場
MIT 時期#
- 1963 年進入 MIT,主修數學
- 當時無法主修電腦科學,計算機課程由電機工程系零星開設
- 加入了 Tech Model Railroad Club,被繼電器邏輯和步進開關深深吸引
- 在 Tech Square 接觸到 Richard Greenblatt、Bill Gosper 等人
- 參與了 Project MAC,被 PDP-1 上的 Spacewar! 遊戲迷住
- 大二時寫小型程式(如迷宮求解的青蛙程式),但尚未真正理解程式設計的深層邏輯
Cosell 回顧大學時期:「當時我在學習程式設計的手藝。我可以讓電腦做我想要的事,但靈光還沒有亮起來。我沒有內化它、沒有真正理解正在發生什麼。一切都有點魔幻和陌生。」真正讓他成為程式設計師的是加入 BBN 的經歷。
加入 BBN#
- MIT 的一位朋友某天晚上帶他去 BBN 參觀
- BBN 是一個 24 小時、7 天運作的地方,像 MIT 實驗室的延伸
- 人們帶著寵物來上班,走廊上日夜都有人走動
- 大三開學時被 BBN 兼職錄用,約一個月後就從 MIT 退學全職工作
- 後來重新註冊 MIT 完成了學業
在 BBN 的職業生涯#
PDP-1 分時系統#
- BBN 正在與 Massachusetts General Hospital 合作,使用分時系統自動化醫院流程
- 每個病房、醫生辦公室、藥房、入院處都放置 Model 33 Teletype
- Cosell 從應用程式設計師起步,三週內就轉為系統程式設計師
- 兩位系統大師離開去讀研究所,留下他成為「PDP-1 分時系統的沙皇」
Cosell 在那段時期的頓悟:「突然間,一系列燈泡亮了起來。我一下子理解了分時系統、即時系統。一旦我理解了,我就吸收了整個分時系統。之後一切都是下坡路了。」
- 決定系統絕對不能當機——如果醫院系統崩潰,病人可能無法按時服藥
- 當時沒有即時除錯工具,系統崩潰後只能透過控制面板的開關讀寫記憶體
- 隨身攜帶呼叫器(pager),可以在停車場透過公共電話遠端修復系統
- 維護系統約兩到三年
「大師級除錯者」的名聲#
- Cosell 認為自己的除錯名聲其實是「不應得的」
- 真正的做法:遇到看不懂的程式碼時,直接丟掉並從頭重寫
- 堅信電腦是確定性的,程式應該能被理解,不存在電腦不正常運作的藉口
- 很少真正「除錯」——而是看到程式碼太複雜時就重寫
Cosell 的除錯哲學:「如果我無法理解一段程式碼,我就會嘗試閱讀它。如果能理解,通常就能看出哪裡有問題並修復。但有時候我會拿到其他人搞不定的程式碼,然後我會說:『這太複雜了。』所以我會想清楚它該做什麼,然後從頭寫。」幸運的是,別人從不問他是怎麼修好 bug 的——因為答案是「我看不懂程式碼,所以重寫了」。
ARPANET IMP 專案#
專案背景#
- Frank Heart 的團隊負責 IMP 開發
- 1968 年秋天合約獲批,Cosell 在一月加入,成為團隊中第三位程式設計師
- 另外兩位是 Will Crowther 和 Dave Walden
- 三人都了解每一行程式碼——程式複雜但並不龐大
開發工具與環境#
- 最初使用離線組譯(offline assemblies),要在 Honeywell 房間用 516 讀取紙帶
- Cosell 為 PDP-1 寫了一個交叉組譯器(cross assembler),大幅改善開發流程
- 在 PDP-1 上可以編輯、組譯、用 TECO 巨集處理,最後只需要將小型二進位可執行檔打到紙帶上
主要技術挑戰#
- 速度與優雅退化:系統需跟上頻寬,並在負載過重時優雅降級而非完全崩潰
- 未經驗證的理論:路由演算法、壅塞控制等都只存在於論文中,需要在實際環境中驗證
- 遠端除錯:機器在辛辛那提,半夜出問題只能打電話給守夜人
除錯故事#
- 有一個 Will 找不到的 bug——數據機協定處理問題,在錯誤的時間傳送錯誤的封包
- Cosell 設計了一系列連鎖修補程式(patches):當看到特定封包時安裝第一個修補,觸發第二個修補監控另一段程式碼,發現異常時凍結系統
- 系統凍結後兩分鐘就找到了 bug
- 另一個記憶體損壞問題:壞指標在數千個機器週期後才導致崩潰,無法直接在資料結構上設斷點,因為它一直在被使用。Cosell 設計了一個兩三階段的動態修補方案來延遲和捕捉問題
團隊協作風格#
- Will Crowther:直覺型天才程式設計師,擅長解決最困難的問題,但程式碼往往只完成 75-80%
- Cosell 負責將常量整理成符號定義(
#define),使逾時值之間的數學關係清晰化 - Will 堅持使用二進位修補而非重新組譯——在筆記本中記錄修補層疊
- Cosell 堅持每天晚上重新組譯,從乾淨的程式碼開始新的一天
Cosell 與 Crowther 在修補 vs. 重新組譯的哲學上嚴重分歧。Crowther 認為重新組譯會引入更多 bug;Cosell 認為修補上疊修補本身就是自我實現的預言。最終 Cosell 的做法勝出——每晚重新組譯後,幾乎不再出現因修補錯誤而產生新 bug 的情況。
對 IMP 專案的反思#
- Cosell 認為 IMP 並非他最驕傲的技術成就——「只是下一個程式」
- 更自豪的是:理解 PDP-1 分時系統的每一行程式碼、寫 DOCTOR、擔任醫院系統的沙皇
- 專案管理者 Frank Heart 的管理風格令人佩服:沒有每週會議、沒有 PERT 圖、幾乎不催促文件——完全信任三人團隊
設計審查與軟體品質#
BBN 的設計審查文化#
- Frank Heart 在其他專案中推行設計審查(design reviews),Cosell 將這個做法帶到自己的專案中
- 設計審查像博士口試——精心挑選的聰明人組成的聽眾,你需要展示你的設計
- 做得好的部分幾乎不會被提到;重點放在你最不確定的部分
- 很多人害怕設計審查,認為是被攻擊
Cosell 在 20-21 歲時就領悟了設計審查的真正目的:「設計審查是要幫助你確認你認為做對的部分確實做對了,並為你不確定的部分提供洞見。」它不是攻擊,而是讓四位 BBN 最聰明的人花 15 分鐘幫你解決你想不通的問題。
程式碼品質哲學#
- 程式應該合理——很少有真正本質上很難的程式設計問題
- 如果程式碼看起來很難或很 tricky,很可能是程式設計師沒有完全理解問題就用蠻力寫出來的
- 永遠不要在發現 bug 的地方修復 bug:應該思考「如果我早知道這段程式碼會壞,我當初會怎麼組織?」然後重寫整個常式
- 修完 bug 後,程式碼應該看起來像一開始就寫對了一樣
漸進式重構策略#
- 每修一個 bug 時,不只用最短路徑修復,而是朝「未來理想版本」的方向改進
- 修 bug 時有三個時間估算:
- 最快的一行修復
- 如果重寫此常式使其不會犯這個錯需要多久
- 如果在程式的更好版本中修此 bug 需要多久
- 實際估算取後兩者之間,每次修 bug 都讓程式朝更好的架構前進
- 這其實就是 refactoring——但 Cosell 在知道這個詞之前就已經在做了
DOCTOR (ELIZA 的 BBN 版本)#
- 在 PDP-1 分時系統上工作時,為了學 Lisp 而寫
- 根據 Weizenbaum 在 Communications of the ACM 上的文章實作
- 用 BBN-LISP(PDP-1 Lisp)寫成,成為整個 BBN 的玩具
- 同事們會留下改進建議:「如果你這樣做會更好」或「我試了這個,沒用」
- Danny Bobrow 曾寫下「A Turing Test Passed」——因為一位 BBN 主管走進電腦室,以為正在跟 Bobrow 聊天的人其實是在跟 DOCTOR 對話
- DOCTOR 隨著 ARPANET 和 TENEX 傳播,影響範圍比 Weizenbaum 的原版更廣
程式設計風格與哲學#
程式碼可讀性#
- 原始碼是寫給人看的,不是寫給電腦的
- 電腦不在乎的是文字檔案;人才是需要理解程式碼的人
- 喜歡 Perl 同時有
if和unless——因為「if not some condition」和「unless the condition」傳達不同的心理意象 - 讚賞 Python 消除了大括號之爭
註解風格#
- 不放太多註解——程式碼本身應該清楚表達意圖
- 在常式開頭放功能描述、例外處理說明、參數順序等
- 當直覺告訴他程式碼不能清楚表達意圖時才加註解
子程式的使用#
- 大量使用 call-once subroutines(只被呼叫一次的子程式)
- 目的是從父常式中抽離複雜的細節,讓讀者在閱讀主流程時不被分心
- 即使某個子常式只在一處被呼叫,也值得抽出來,因為它隔離了演算法並使輸入輸出清晰
程式碼風格偏好#
- 偏好大括號對齊(aligned open and close braces)
- 閱讀程式碼時理解其整體意圖,而非逐段分析每個語法元素
- 如果開大括號和閉大括號距離太遠,就拉出一個子常式
對新進程式設計師的要求#
- 告訴剛畢業的優秀程式設計師:「我不在乎程式能不能跑。能寫出會跑的程式是一種熟練的技藝,你很擅長。現在,你要學會程式設計。」
- 程式能跑只是基本門檻,不是終點
- 要求他們閱讀別人的程式碼——有些人從未讀過別人的程式碼
- 有個程式設計師起初反抗他的風格要求,後來在閱讀另一位同事 42 頁長的單一 C 函式後頓悟,成為他期望中的程式設計師
閱讀程式碼的方法#
- 採用由上而下(top-down)的方式閱讀程式碼
- 試著理解程式應該做什麼,然後尋找特定的程式碼片段來驗證
- 同時思考「如果是我,我會怎麼解決這個問題」,然後對照實際程式碼
- 有時遇到程式碼做法與預期不符的地方——要嘛是原作者做錯了,要嘛是用了不同方式
- 承認自己閱讀程式碼的能力並不特別好,這也是為什麼經常選擇重寫
兩種除錯風格的對比#
- Steve Butterfield 是另一種風格的修理高手——可以在完全不理解程式運作的情況下深入程式碼內部修改一個小醜陋的片段
- Cosell 總是試圖理解整個程式,從頂部推理找到問題
- 兩種方式都有效,但 Cosell 認為自己的方式讓程式整體變更好
招聘與人才識別#
- 不使用標準面試題目或謎題
- 採用直覺式方法:看履歷只是為了判斷「這像不像我的人」
- 尋找的特質:好奇心、學習敏捷度、對多元事物的興趣、精確的思維方式
- 心中有一個模糊的「BBN 品質人才」形象
- Rubik’s Cube 的例子:當魔術方塊剛從英國被帶回時,BBN 的人都能以不同方式解開——這不是特定技能,而是一種面對未知問題的態度
對 C 語言的看法#
- 在電腦安全課上說:現代電腦面臨的最大安全問題就是 C 語言
- C 是一種系統程式設計語言,寫出好用的系統,但幾乎不可能寫出有一定複雜度又沒有安全漏洞的 C 程式
- 記憶體的每一次讀取都需要手動確保不超出緩衝區、每次釋放記憶體都需要確保沒有殘留指標
- 回憶 Pascal 時代的爭論:Wirth 和 Dijkstra 認為 C 太危險,但系統程式設計師(包括他自己)都用 C
- 現在更偏好寫 Perl——雖然慢,但讓他不用擔心記憶體安全問題
IMP 的組譯器巨集系統#
- 為 IMP 寫了一套複雜的組譯器巨集,可以宣告程式碼運行在哪個中斷層級
- 組譯器會標記每條指令的中斷層級,搭配 TECO 巨集後處理器可以自動偵測分時衝突
- 這讓不完全理解即時問題的程式設計師也能寫出正確的即時程式
程式設計是年輕人的遊戲?#
- 認為可能是——回顧 BBN 職業生涯末期,手下的人在做他已經做不到的事
- 實際產出好程式碼需要的強度和心理敏捷度,隨年齡增長會減退
- 但年長者有年輕時沒有的智慧——更適合指導年輕人
- 類比數學家的說法:大多數數學家在 30 歲前完成最好的工作
- 現在的程式設計以爆發式(in bursts)進行,無法再維持年輕時的持續高強度
自我認同#
- 不認為自己是科學家
- 自認是藝術家和工匠的結合
- 工程部分:知道什麼能做、什麼快、什麼慢、各部件如何組合、建立系統的心理模型
- 藝術部分:設計應該優雅——程式的藝術性影響其壽命
- 程式碼之美在於未來的人能多容易地修改它而不破壞它
- 大多數他寫的程式碼都是讓很多代人能持續修改而不會一直破壞的那種
對現代程式設計的觀察#
C 語言的安全問題#
- C 是一種適合系統程式設計的語言,用它寫了很好的系統
- 但幾乎不可能用 C 寫出有一定複雜度又沒有安全漏洞的程式
- 回憶 Pascal 時代 Wirth 和 Dijkstra 的警告:C 太危險了
- 但當時所有系統程式設計師(包括他自己)都選擇了 C
- C 可能是好系統程式設計師的完美語言,但不幸的是「不那麼好的系統程式設計師」也在用它寫應用程式
對 Fran Allen 觀點的回應#
- Allen 從編譯器角度認為 C 是一種笨拙的退步
- Cosell 認為那是因為她在做編譯器——從組合語言轉到 C 對系統程式設計師來說是解放
- 從組合語言寫陣列迴圈的人看來,C 的陣列檢查問題根本不算什麼
- 問題不在 C 本身,而在於太多「不夠好的程式設計師」在用它
Java 的矛盾#
- 第一次接觸 Java 時覺得:「又一種限制不夠好的程式設計師的語言」
- 但世界上現在有 7500 萬普通程式設計師在建複雜應用——他們確實需要更多幫助
- Java 感覺太「獨裁」——Perl 給他更多自由和表達空間
每一代程式設計師的挑戰#
- 每一代程式設計師離底層越來越遠,工具越來越高級
- 好處是能做更聰明的事;壞處是基礎越來越複雜
- PDP-1 的指令集簡單得像在公園散步
- 現代的多核處理器、複雜的顯示卡管線、3D 圖形環境——光是掌握這些就令人生畏
- Google Maps 的路由計算讓他震驚——他認為這些程式設計師一定比他那一代更優秀
Cosell 對現代程式設計的感慨:「這是一個當退休程式設計師榮譽教授的好時代,因為你有一些往日的光環,但世界如此奇妙,你甚至可以不用再親自做就能偶爾得到一點讚賞。而如果你是大學裡學電腦科學的學生,要出去面對這堆東西——救救我吧。」
重要觀點總結#
關於程式複雜度#
「很少有真正本質上很難的程式設計問題。看起來真的很難或很 tricky 的東西,很可能是程式設計師沒有完全理解他們需要做的事,然後用錘子敲打直到看起來像是對的。」
關於程式碼標準#
「我不在乎程式能不能跑。你工作在這裡的事實就意味著我期望你能寫出會跑的程式。能寫出會跑的程式是一種熟練的技藝,你很擅長。現在,你要學會程式設計。」
關於清晰度與效能#
「程式設計師是世界上最差的最佳化器。他們總是最佳化程式碼中最有趣的部分,卻幾乎從不處理真正需要最佳化的部分。所以我總是告訴跟我工作的人:『把程式碼寫得像水晶一般清澈、像白天一樣明亮。用簡單的方式做。之後如果需要加速,我們可以在那段程式碼周圍畫個小框框。』」