Peter Norvig#

Peter Norvig 是一位兼具廣泛思維和 hacker 精神的人物。他曾在 Google 的搜尋日誌中找到同一使用者連續三次搜尋形成俳句的模式。他的網站上有書籍、論文、演講投影片,還有在 McSweeney’s Quarterly Concern 發表的作品、生成世界最長迴文的程式,以及被 Edward Tufte 引用的 “Gettysburg Powerpoint Presentation”

他是 Google 研究總監(Director of Research),此前擔任過搜尋品質總監。更早之前,他是 NASA Ames Research Center 計算科學部門的主管,以及 90 年代末網路新創公司 Junglee 的早期員工。他獲得了 2001 年 NASA 傑出成就獎,是 AAAI 和 ACM 的 Fellow。

Norvig 同時擁有 hacker 和 engineer 兩種方法論的經驗。他的著名文章 “Teach Yourself Programming in Ten Years” 強調程式設計是需要十年才能真正精通的技能。


程式設計啟蒙#

高中時期:PDP-8 與 BASIC#

  • 高中時(約 1972-73 年)在學校的 PDP-8 上用 BASIC 開始程式設計
  • 老師教洗牌演算法:用隨機數產生器挑兩個位置交換,用 bit vector 記錄已交換的,直到全部完成
  • Norvig 立刻意識到這個演算法很糟——「可能永遠不會終止」
  • 他自己想出了類似 Knuth 洗牌演算法的 O(n) 方法——從 0 到 52 交換,然後從 0 到 51,依此類推
  • 這個經歷讓他覺得「也許我有程式設計的天賦」,同時也學到「老師不一定都是對的」

早期閱讀程式碼#

  • 在父親的舊 Scientific American 雜誌中發現 Christopher Strachey 關於軟體工程的文章
  • Strachey 發明了一種語言(從未有編譯器),並用它寫了一個西洋跳棋程式
  • Norvig 閱讀時注意到程式有 bug:文章描述 make-move 接受棋盤位置並回傳一步棋,但程式碼中的 make-move 多了一個額外參數(搜尋深度),顯然是後來加的但文件沒更新

第一個有趣的程式:Game of Life#

  • 高中課堂作業,在沒有好顯示器的情況下,用電傳打字機的黃色紙張輸出
  • 因為記憶體不足無法用二維陣列,自己發現了 bit fields 來壓縮資料
  • 印出五個世代並排的輸出格式

教育與職業生涯#

大學:數學主修#

  • 在大學期間修了電腦課程,但最終選擇數學主修
  • 感覺電腦科學的課程要求太像「主修 IBM」——要學他們的組合語言、作業系統等
  • 有些課很有趣,但不想走完所有必修課程

工業界與研究所#

  • 大學畢業後在 Cambridge 的一家軟體公司工作兩年
  • 公司的主要產品是軟體設計工具集,也做軟體顧問
  • 有一個有趣的專案:寫一個流程圖產生器,可以解析程式並生成流程圖
  • 用 Unix 上的 yacc 等工具完成,但客戶臨時要求改在 VMS 上運行,沒有 yacc——只好手動修改解析表
  • 之後去讀研究所,因為「花了四年厭倦學校,只花了兩年厭倦工作,也許我喜歡學校多兩倍」

從個人到團隊#

  • 進入工業界最大的學習是時程管理、團隊合作、讓客戶和經理滿意
  • 從單打獨鬥轉變為團隊協作:必須依賴他人建立的抽象,理解介面如何組合
  • 現代程式設計更像是組裝零件而非從零開始——理解介面比了解套件內部更重要

Norvig 觀察到現代程式設計的根本轉變:「現在更像是組裝各種零件而非從零開始寫所有東西。理解介面以及它們如何組合在一起,比了解所有套件的內部細節更重要。」


程式設計方法與設計#

Sudoku 求解器的故事#

  • Norvig 寫了一個著名的 Sudoku 求解器
  • 有位 test-driven design (TDD) 大師也嘗試用 TDD 解同樣的問題——先寫測試類別再寫程式碼
  • 那位大師寫了五篇部落格文章,每篇都寫更多測試和一點程式碼,但從未真正解出來
  • Norvig 因為 AI 背景,知道 constraint propagationrecursive search 的結合可以解決 Sudoku
  • 教訓:「你可以寫所有想要的測試,但如果不知道如何處理問題,你不會得到解答」

知道你不知道什麼是關鍵技能。Norvig 建議兩種思路:(1) 認識到可能存在已知解法——搜尋看看;(2) 即使不知道具體演算法,一些基本概念如 dynamic programmingsearch with backtracking 是 60 年代就發現的基礎,每個程式設計師都應該知道。

設計大型軟體#

  • 重視整體系統設計的文件,而非每個方法的文件
  • 每個方法的文件通常只是重複函數名稱和參數就能看出的資訊,相當冗餘
  • 成功專案最重要的因素是有經驗的人建造正確的東西;如果沒有經驗,次好的方案是保持足夠的彈性以便調整

測試哲學#

  • 測試更多是修正錯誤的方式,而非設計的驅動力
  • 不認同極端 TDD:「先寫一個測試說我最後要得到正確答案,然後讓它失敗,然後問下一步需要什麼——這不像是設計的正確方式」
  • 應該先思考有哪些部件,然後為每個部件及其交互寫測試
  • 在 Google,不滿意簡單的布林測試模型——希望有 assertAsFastAsPossible連續數值最佳化(precision、recall 等),而非只是「對或錯」

原型的價值#

  • 在寫之前先想像解決方案很有用——如果原型感覺笨拙,可能是選錯了基礎元件
  • 在 Google 面對的是持續的規模問題:今天能處理的量,兩年後可能就不夠了
  • 有些問題可以用信封背面計算回答,有些需要實際建造才知道

Google 的工程文化#

Code Review#

  • 在 Google,每一次 check-in 都必須經過其他人的審查
  • Guido van Rossum 加入 Google 時,Python 程式碼要經過審查,Ken Thompson 的 C 程式碼也一樣
  • 有些人是更好的審查者——不只是找到格式問題,還會提出設計改進建議
  • 新人常犯的錯誤:把所有程式碼放在實驗分支中太久,累積成巨大的 check-in

Hacker vs Engineer 文化#

  • Google 和 NASA 代表了兩種截然不同的軟體文化
  • NASA:火箭科學家,把軟體視為「必要之惡」,對創新非常保守——「很好,等它在另外兩個任務上驗證過再說」
  • Google:從創辦人到管理層都有電腦科學背景,軟體是公司的核心
  • NASA 局長 Don Goldin 曾說應該做「更好、更快、更便宜」的任務——跑更多任務,有些會失敗但整體收穫更多。政治上卻行不通,因為公眾無法接受失去太空船

Launch Early, Launch Often#

  • Google 的哲學是「早發布、常發布」
  • 大多數產品免費,軟體在伺服器端可以隨時修復
  • 不需要擔心 CD 壓片和安裝更新的噩夢

NASA 的軟體 Bug#

Mars Climate Orbiter 事件#

  • Norvig 參與處理 1998 年 Mars 程式失敗的善後
  • Mars Climate Orbiter:著名的英制-公制單位混淆問題
    • 一方生成以英尺-磅為單位的資料檔案
    • 另一方的導航軟體期望的是牛頓
    • 根本原因是 JPL(Pasadena)和 Lockheed-Martin(Colorado)的兩個團隊之間溝通不足
    • 飛行途中有人注意到數據不對,發了 email 但沒有放入 bug tracking 系統
    • 兩邊各自假設對方已經處理了這個問題

軟體重用的風險#

  • 前一個任務中,以英尺-磅記錄的資料被歸類為「非任務關鍵」的 log
  • 新任務重用了大部分軟體,但改變了導航方式,使得這個 log 變成了導航的輸入
  • 原本「非關鍵」的資料突然變成了「關鍵」的

Norvig 認為 NASA 也許用更便宜但有更多 bug 的軟體加上更好的營運流程會更好。太空梭飛行軟體每行要 $1,500,但太空人仍然需要接受大量模擬器訓練來處理軟體無法涵蓋的情況。


除錯方法#

混合式除錯#

  • 根據情況使用不同方法:有時用 IDE 的追蹤功能,有時用 Emacs 加 print 除錯
  • tracing、printing、thinking 是核心方法
  • 寫更小的測試案例,把功能拆解到找出失敗的測試

重寫而非修補#

  • 經常會不找 bug 就直接重寫有問題的程式碼區段
  • 「我感覺這部分不對,與其一點一點修補,不如丟掉這兩百行,從頭重寫」
  • 有時因此沒有真正理解 bug 是什麼,但修復了問題且速度更快

對形式驗證的看法#

  • 傾向非正式方面——沒有大量使用 assertions 或 loop invariants
  • 認為對高可靠度軟體(如太空任務)形式驗證很重要,但一般情況下先讓程式跑起來更實際
  • 對自己的偏見也保持警覺——曾經在拼寫校正程式中引入 bug 改善了評分,居然相信了那個更好的分數

Norvig 的確認偏見故事很有教育意義:他在測量程式品質的程式碼中引入了 bug,得到更好的分數,他選擇相信而非質疑。「如果分數變差了,我絕對不會只說『哦,這個小改動一定讓它變差了』——我會去找其他原因。」


程式設計師的技能與成長#

什麼是好的程式設計師#

  • 能夠做出進展然後改進 (make progress and then improve on it)
  • 有方向感和自省能力:「我在哪裡?我怎麼到這裡的?有沒有更好的路?」
  • 最好的程式設計師會提取可重用的工具,讓他人也能受益
  • 不同人有不同強項——Google 最好的搜尋人才不一定寫最好看的程式碼,但對軟體行為有驚人的心理模型

團隊合作#

  • 進入產業最重要的技能是與人相處:理解客戶、與隊友互動、與上級溝通
  • 傾向分工獨立作業 + Code Review,而非 pair programming
  • 約 10% 的時間值得坐在一起做腦力激盪和建立共識,其餘時間獨立工作更高效

閱讀程式碼#

  • 在 Berkeley 時大量閱讀 Symbolics 的程式碼
  • 閱讀方式是興趣驅動:「這個檔案系統讓你用同樣的協定讀取網路上和本機的檔案,它怎麼做到的?」
  • 從 open 函數開始追蹤,看它呼叫了什麼,逐步深入

理解大型程式碼庫#

  • 混合使用靜態和動態方法:先閱讀程式碼,然後取得一些 trace 來了解呼叫關係和時間花費
  • 從嘗試做一個小改動開始,逐步學習
  • 或者去 issues database 找一個小問題來修——「只需要學一小部分就夠了」

對軟體工程的看法#

過度泛化的陷阱#

  • 坦承自己偏好優雅解而非實用解,必須克制自己
  • 「完美是好的敵人」——每個實用工程師都必須學會的課
  • S 型曲線:達到 80-90% 完成度後就進入收益遞減,應該轉去做其他事情

程式語言的影響被高估#

  • 語言設計者常常高估語言本身的影響
  • 「在我閃亮的新語言裡,這六行程式碼變成兩行」——但你寫的程式碼只是整個生產系統的一小部分
  • 真正的瓶頸是每天更新資料、爬取網頁、整理格式等「髒活」
  • 切換語言必須帶來足夠大的好處才值得

學術界與產業界#

  • 電腦科學課程大體上是好的,但某些重要的東西教得不夠:團隊合作、大規模系統、雲端計算
  • 產業界有時確實忽視了學術界的好想法——Intel 的浮點乘法 bug 就是因為沒有重視 model checking
  • 但不認為這是因為抗拒改變——如果某些東西真的有明顯優勢,人們會採用

推薦書籍#

  • 必讀一本演算法書:可以是 Knuth、也可以是 Cormen/Leiserson/Rivest 等
  • 必讀一本關於抽象思維的書:推薦 Abelson & Sussman (SICP)
  • 必讀語言的參考手冊
  • 推薦類似 Code Complete 的書,涵蓋除錯、測試等程式設計實務

程式設計的樂趣#

小程式的方法#

  • 寫網站上的小程式時,最重要的是把所有東西都裝在腦子裡
  • 小程式這很容易做到;大程式需要額外工具來管理
  • 也很重視知道自己在做什麼——知識和技巧同樣重要

年齡與程式設計#

  • 不認為程式設計純粹是年輕人的活動
  • 年輕的優勢:能長時間集中注意力,把整個問題裝進腦中
  • 年長的優勢:經驗豐富,知道怎麼做,效率更高
  • 自己現在寫得少了一些,感到有點沮喪——「不知道所有新東西讓人挫折」

關於生產力差異#

  • 不確定程式設計師之間的「數量級差異」研究是否仍然成立
  • 存在相關性 vs 因果性的問題:角落辦公室的程式設計師更有生產力,是因為辦公室讓他們更好,還是因為好的程式設計師得到了好辦公室?
  • 對可控因素更感興趣:更大的螢幕、更好的工具是否真的提高生產力?