總是找不出錯誤,也許正是轉換視角的好時機

如果某個系統內發生錯誤而導致系統的產出變差,我們將這個錯誤移除並讓系統的產出恢復正常。這種方法論,在醫學領域叫診斷;在應用軟體開發領域,叫做除錯;在水電工領域,叫做抓漏。

《從錯誤到創新:跨領域的錯誤處理、創新之道》一書的作者陳家宏 (Laurence Chen),目前為睿博資訊負責人,同時也是台灣 Clojure 語言社群、dbt 台北社群的召集人之一。從一個概念出發:「管理學談的problem solving,與軟體開發的debug ,似乎有著千絲萬縷的關係?」接著透過各式不同的案例,論及許多管理學書籍都會討論的「解決問題」、「創新」、「系統設計」,讀來不覺枯燥,當中的方法甚至可應用人、組織、軟體、機器或流程上,用途極廣。例如,在學程式時,如果遇到教材與開發實作不一致的情況,會發生什麼事呢?

當理論預測與實驗結果不一致,會發生什麼事?

在我大學一年級剛開始學習程式設計時,學校教的課程是 C++ 語言。由於課本背面有附上的一片光碟,內容是微軟公司的編譯器 (Visual Studio 6.0) ,Visual Studio 6.0 安裝起來也滿省事的,那就用它來開發軟體吧。

很快的,我就撞到了一個覺得簡直是百思不得其解的問題:「課本上描述的程式語言的規格,與編譯器的實作不一致!」最初,我一直覺得一定是我寫程式的能力有問題、我對程式語言的理解有問題、我的英文閱讀能力有問題。但是,我把程式碼減到最少,測了一次又一次,確實就是不一致。這不是很奇怪的事情嗎?微軟公司這麼大,是世界級的大公司,連編譯器都無法做對嗎?會有如此明顯的臭蟲被我抓到嗎?怎麼可能呢?總之,沒有辦法,作業還是要交、考試還是要考,我只好把這個小小的認知失調放在心裡,就當作我的編譯器實作的是 C++ 方言。如果紙筆考試要考,我就寫課本上描述的程式語言。如果是交作業,我就用讓編譯器可以順利編譯的寫法。

過了幾年後,我才了解,曾有一段時間,微軟公司會故意在一些小地方讓自家的軟體與主流的規格有微小的差異,以增加用戶轉移的成本,這是一種商業策略。換言之,微軟公司的 Visual Studio 6.0 確實就是實作一種特別的 C++ 方言,真的就跟標準不同。

像上述這種理論的預測與實驗的結果不一致的情況如果一再地發生時,會發生什麼事?曾經有位哲學家孔恩 (Thomas Kuhn) 充分地研究過了這個議題。

孔恩在念物理博士時,有一回得到了一個去大學部授課的機會,要授課科學哲學。然而,他在準備教材時,卻發現古希臘時代亞里斯多德所寫的物理學,他身為當代頂尖的物理學博士生卻看半天看不懂。這嚴重地違反了他原先的假設,他本來的假設是:「幾千年前的原始科學,應該是要很單純而簡單的」結果完全不是如此,亞里斯多德自成系統的世界觀一點也不單純。這個重大發現,引起了孔恩心裡的大哉問,於是,孔恩對這個大哉問,寫出他了的生涯代表著作做為答案 --- 科學革命的結構

照孔恩的所述的科學史,科學史其實是不連續的。大多數的時候,是「常態科學」,某些觀點或理論會被科學社群所接受,成為典範,而科學的社群以此典範為中心,去探索與發展新知識。少數的時候,則是「典範轉移」。當基於典範的科學理論所預測之結果與實驗的結果不一致的情況發生時,人們未必會立刻去挑戰理論的正確性。更有可能的發生的事情是,人們會先去質疑做實驗的方法或是工具有問題。要等到很多的不一致出現時,對於既有的典範帶來嚴重的危機,這時才為科學革命帶來契機,人們會開始發展新的觀點去取代舊的典範,成為新的典範。

典範轉移與除錯方法

除錯方法的第一步驟是「界定系統」,在許多除錯的情境,特別是我們覺得情境已經相當熟悉的時候,很容易忽略了潛意識已經完成了「界定系統」這件事,因為系統是如此地顯而易見。於是,系統就成為了我們操作除錯方法時,幾乎不會去懷疑的典範。然而,當錯誤總是無法找出來的時候,重新思考我們對於系統的認知顯然是個值得考慮的解題方向,這就是套用典範轉移的好時機了。

愛因斯坦曾說過:「原則上由觀察到的數值來建立一個理論是不對的。實際上往往相反,反而是你用的理論決定你能觀察到甚麼。」這句話略做修改,也可以套用在除錯方法上:「你界定的系統,決定了你能觀察到什麼錯誤。」

我們在第一章時,曾經提到過對於錯誤的定義,有狹義的錯誤與廣義的錯誤。在實務上,單純只是找出錯誤並加以修正的難度,往往與突破既有的效能/產能天花板的難度,不是同一個量級,後者常常會困難的多。突破既有的效能/產能天花板這樣子的改進,可以稱之為「創新」,它往往比單純的修正錯誤更有價值。如果使用除錯方法要解決的難題是那種需要突破既有的效能/產能天花板的難題,那麼從「界定系統」這個面向來做尋找解題方向常可以帶來不錯的結果。

把除錯方法與典範轉移科學進程做個比較(請向左滑動表格):

除錯方法 典範轉移科學進程
幾乎不改的觀點 對系統的界定、系統運作的原理 某些被科學社群所接受的觀點或理論,成為典範
大多數時候的作法 預設系統的界定無誤、描述系統運作的原理無誤,以此去尋找錯誤的根源 以此典範為中心,有效率地去探索與發展新知識
危機 找不出錯誤、無法解題、發現重大矛盾 基於典範的科學理論所預測之結果與實驗的結果有大量的不一致
解決方案 調整對於系統的界定、質疑系統運作的原理 質疑原有的典範
性質 解決突破既有的效能/產能天花板的難題 帶來科學革命

轉換看待事物的視角

如果我們用比較通俗的說法來談從界定系統這個面向來除錯,我們也可以說成是,先轉換看待事物的視角再來設法除錯。轉換看待問題的視角,英文是 to think outside the box (破框思考),很多人聽過這種說法,然而,難就難在該如何突破思想上的框架。

轉換看待事物的視角是個開放性的問題,新的視角可能有無數種,這邊列舉三種適合用於除錯的轉換視角方法:

  • 思考系統的邊界
  • 思考子系統之間的邊界
  • 檢查假設、限制、槓桿點

思考系統的邊界

在電梯剛被發明出來的年代,很快地,只要在樓層略高的建築,人們就開始強烈地抱怨,電梯不夠快。由於受限於那個時代的電梯科技與建築技術,負責研發電梯的工程師也遭遇到了巨大的困難,他們發現:「在既有的科技與成本限制,根本不可能在安全的前提之下,把電梯做得更快了。」然而,這時有某位跳出思想框架的工程師,在電梯裡安裝了一面鏡子,順利地緩解了人們抱怨電梯太慢的問題。

上述的故事是跳出思想框架的經典教材。那我們有辦法照著故事裡的方式,去跳出思想框架嗎?下表是電梯例子的轉換視角前後的比較。

原始的系統 轉換視角之後的系統
系統 電梯 電梯與使用電梯的人
對問題的主要陳述 電梯的速度太慢 使用者感到不耐煩
尋找解法的方向 電梯的速度 電梯的速度、使用者對時間的感受

在電梯的這個例子裡,一旦負責思考這個難題的人,從系統的邊界切入,把使用電梯的人一併納入這個系統來思考,就容易發現本來難以發現的解決方案。

案例:應用軟體開發的除錯

應用軟體從開發到在線運作,會經過三個階段,分別是:

  1. 軟體開發的階段 (develop):撰寫程式碼
  2. 軟體集成的階段 (build):程式碼經過編繹,並且與函式庫結合在一起,成為可執行檔
  3. 軟體在線上運行的階段 (run):可執行檔在硬體上運作

曾經有一回,我在開發一套有前後端的會計軟體時,遇到了錯誤。在我的筆電上開發與測試時,一切正常。然而,當軟體功能開發完畢,我把原始碼上傳到版本控制伺服器,經過了集成階段,在測試機上運作時,卻有一個圖檔沒有顯示。

我在除錯時,第一時間還是反射地先猜測,八成是我的程式碼寫錯了,於是就先檢查了數個我認為可能寫錯的點,一一驗証之後,都正確無誤。接下來,我開始主動有意識地套用除錯方法的思考,第一個問題自然是詢問自己,是否思考了正確的系統邊界?

無論是「軟體開發的階段」造成的錯、「軟體集成的階段」造成的錯,又或是「軟體在線上運行的階段」造成的錯,都有可能造成最後在測試機上運作時,沒有顯示圖檔的狀況。然而,如果我一味地只去猜想,必定是我的程式碼寫錯了,這個猜想,已經預設了錯誤只發生於「軟體開發的階段」。如果是這樣子的話,在筆電上的測試卻正確無誤,這點就頗為矛盾。

由於發現了重大矛盾,我重新設定了系統的邊界,假設錯誤應該是出在「軟體集成的階段」的階段。換了這個觀點之後,我很快地找到了錯誤的根源:我在上傳檔案到版本控管伺服器時,下錯了指令,導致一張圖檔沒有上傳成功。換言之,「沒有顯示圖檔」的結果,並不是導因於程式碼有錯,而是要顯示的圖檔在軟體集成時,沒有順利集成進軟體裡。

思考子系統之間的邊界

系統一旦有了某個程度以上的複雜度之後,系統的內部就需要有子系統,如此系統才能被管理。然而,將系統劃分成子系統的方法,因為太過理所當然、想當然爾、我們很容易一味地照著前人劃分成子系統的方式來做,而不去思考這個拆分方式背後運作的邏輯是什麼?

本文節錄自《從錯誤到創新:跨領域的錯誤處理、創新之道》,由陳家宏 (Laurence Chen)授權轉載。