為什麼要多樣化環境#
有時候,透過改變「戰場」就能找出微妙的 bug。你可以使用不同的 compiler、runtime interpreter、virtual machine、middleware、作業系統或 CPU 架構來建置或執行軟體。這是因為不同的環境可能會對輸入做更嚴格的檢查,或因為其結構放大了你的錯誤。
如果你遇到無法重現的應用程式不穩定、當機或可攜性問題,試著在另一個環境中測試你的軟體。
跨平台編譯的好處#
在不同作業系統上編譯或執行軟體,可以揭露不正確的 API 使用假設。例如:
- C/C++ header 檔案可能包含非必要的宣告,讓你忘記 include 真正需要的 header
- 不同 OS 的 API 實作可能差異很大(Solaris、FreeBSD、GNU/Linux 的 C library 各有不同)
- 這些差異也會影響使用底層 C library 的直譯語言(JavaScript、Lua、Perl、Python、Ruby)
處理器架構差異#
對於 C、C++ 等貼近硬體的語言,底層處理器架構會影響程式行為:
- x86 和 ARM 在 misaligned memory access 的處理上有差異
- 結構體的大小和成員偏移量在不同架構間可能不同
- 基本型別的大小會隨架構和 OS 變化
#include <stdio.h>
int
main()
{
printf("S=%zu I=%zu L=%zu LL=%zu P=%zu\n", sizeof(short),
sizeof(int), sizeof(long),
sizeof(long long), sizeof(char *));
}不同平台的輸出差異明顯,例如 long 在 Windows x64 是 4 bytes,但在 GNU/Linux x86_64 是 8 bytes。
三種多樣化環境的方式#
- 虛擬機軟體 (Virtual Machine):在工作站上安裝不同 OS,且可以保存乾淨的映像檔隨時還原
- 低價小型電腦:如 Raspberry Pi,可輕鬆取得 ARM 架構的環境,也能透過網路存取
- 雲端主機 (Cloud-based Hosts):租用執行不同 OS 的雲端機器
在同一工作站上多樣化#
不一定要使用另一台機器,也可以在自己的開發環境中引入多樣性。不同的 compiler 通常能偵測到更多問題,包括可攜性問題和邏輯缺陷。建議的替代環境組合:
- .NET:同時使用 Mono 和 Microsoft 的工具
- C/C++/Objective C:同時使用 LLVM 和 GCC
- Java:同時使用 OpenJDK 和 Oracle JDK,嘗試多個 Java runtime
- Ruby:除了 CRuby,也試 JRuby、Rubinius、mruby
用高階語言重新實作#
更激進的做法是用高階語言重新實作有問題的演算法。例如將 C 語言的實作用 Python、R、Ruby、Haskell 或 Unix shell 重寫,利用高階語言的 set operations、pipes、filters 和 higher-order functions,快速得到一個正確運作的版本。
這種方法能幫助你快速找到演算法設計中的問題,修正實作錯誤。之後如果效能至關重要,再回到原本的語言實作,並使用差異比對法 (Item 5) 來除錯。
重點回顧#
- 多樣化的編譯和執行平台可以提供寶貴的除錯洞察
- 用高階語言重新實作有問題的演算法,可以幫助找出並修復問題