許多人所熟知的、在軟體領域影響包括我在內的許多人想法的 Martin Fowler,在最近寫了一篇部落格文章,標題是《犧牲的架構(SacrificialArchitecture)》。在這一回裡,我想談談對這篇部落格的想法。

演化 vs.砍掉重練

在正式開始之前,可以先回顧一下 Martin Fowler。

對我來說,他和 Ken Beck等其他作者共同著作的《極限編程規劃(Programming Extreme Programming)》,以及《重構:改善現有程式碼的設計(Refactoring: Improving the Design of Existing Code)》,這兩本書,影響了我對軟體開發的想法不少,尤其是「軟體及程式碼的演化」方面,它們帶給我不少啟示,也就是,軟體和程式碼與其想一步到位,不如持續透過修正,找到真正的需求,或是因為環境的變化進而滿足需求。

所以,我習慣在設計程式碼時,從試著讓它有能力演化的方向來著手。而若是接手他人開發的程式碼時,也總抱持著還有演化機會的想法來嘗試。就像「重構」的想法一樣,並不是直接作廢掉現有的程式碼,而是試著嗅出現有程式碼的壞味道,並且加以去除。因為現有的程式碼,都是寶貴的資產,它們都是花了不少時間所寫作,而且更重要的是——花了更多的時間修正其錯誤、使其穩定並正確的運作。

因此,廢棄掉程式碼的代價很沉重。雖然,你不難觀察到,不少工程師喜歡「砍掉重練」,因為重新寫過,可以用最新的想法、最新的技術來寫,把過去的陳舊包袱通通丟掉。但是,許多情況下,砍掉重練的結果就是「認賠殺出」,因為它意謂著過去的投資有許多都白費了。開發團隊必須重新開始設計、撰寫程式碼,還有測試並使其穩定。讓程式碼持續演化的作用之一,便是確保過去的投資。

不過,這次 Martin Fowler 所談的「犧牲的架構」有點不太一樣,因為這篇文章是在談廢棄掉現有的程式碼。所謂的「犧牲的架構」,意指所開發出來的程式碼都能正常的運作了,甚至運作了好些年,但是開發團隊決定將其捨棄、重寫,重新建立全新的架構。等等! Martin Fowler 不是演化派的大師嗎?怎麼會講到「砍掉重練」這個主題上來了?

不同時期所選擇的架構與技術方案,皆有其意義

他舉了一個 eBay 的例子。當然,眾所皆知的事實,eBay 是世界上最成功的電子商務網站之一,不過,它最初的系統是在 1995 年,從一組 Perl 的 script(腳本)程式碼開始的。而在 1997 年,則改用 C++重新寫過;到了 2002 年,又整個改以 Java 重寫。

為什麼 eBay 要這麼做?為什麼不一開始就用 C++或 Java(當然,1995 那一年 Java 才剛問世)來開發他們的系統呢?這是否代表著最初使用Perl來開發系統的決定是錯誤的?因為之後又改用 C++寫過了。或甚至第一次改寫時使用 C++,也同樣是個錯誤?因為,之後又用Java的新版本來取代了。

這樣的決策需要考慮到很多因素,首先便是商務本身的成長條件。對於一個尚處於初期階段的商務想法來說,盡快落實、推到市場上,並且接受來自使用者的回饋,進而得以改良商務上的想法以及背後支持這個想法的系統,是很重要的一件事。因此,在初期階段,需要的是時間-服務到市場上的時間,以及迅速變化,以因應使用者真實需求的時間。用Perl可以讓他們達成這個目的。

當新服務的想法在經歷一些演化、獲得使用者的認同後,使用者的規模會增加。所以,你可以想像,當 eBay 打算使用C++來做為系統的主力時,他們應該是預期或正遭遇到系統的效能問題。所以,使用一個當時相對高效的程式語言開發的新系統,來替換掉舊系統,是合理的決定。

為什麼不在當時使用 Java ?我想,在 1997 年時,Java 在大家的心目中,還是寫著 Applet 此等小玩意的語言,尚未展露未來成為伺服器端關鍵語言地位的潛力。

那為什麼 eBay 在 2002 年,又改用 Java 來開發?我猜想是,因為需要更多、更複雜的功能,而 Java 在當時開始提供了足夠的效能,而在開發生產力上,又有卓越的表現。

這個捨棄兩次舊有程式碼的例子,告訴我們,在商務的發展上,每個階段都有不同的目標及任務,而技術方案是為了符合該階段的目標及任務而生的。單看技術方案,能論得出高下嗎?

我們不能說 eBay 最早的 Perl 版本就比 C++的版本糟,只是因為它被 C++的版本給取代了。事實上eBay今日的成功,足證每個階段的版本都夠好。

每個階段的技術方案,都稱職地扮演了它們應該扮演的角色。若是 eBay 在一開始就打算使用 C++來開發,那麼它可能會錯過商務發展的黄金時期,因為 C++的版本需要更多時間來開發。而且,當需要改變時,它因應改變的速度也會比較慢。

所以,每個架構都有其存在的意義,它們之所以被犧牲掉,不是因為它們不夠好,只是因為局勢改變了,需要新的架構來支持。我們不需要每次都嘗試要打造出最快、最有規模可擴充性的系統,因為這樣的系統,不見得滿足當前商務上的需求。通常,我們在初期需要的,是快速完成成品及彈性,接著,才會轉變成為需要效能及規模可擴充性。

就像在eBay 的例子裡看到的,快速完成成品及彈性,有助於及早將服務推到市場上接受回饋,而彈性則可以幫助在接收到回饋後,迅速調整以反應這些回饋。

演化終有極限,仍須仰賴大幅改變原有架構,才能更進一步發展

有時候,我們在犧牲架構時所做的改變,並不像改用另一語言重寫那般的激烈,但是還是大幅度的重寫。

需要這樣大幅度改寫的原因,在於原有的架構、原有的程式碼難以做小幅度的演化就達到目的,尤其發生在架構性的特質上,例如規模可擴充的改進,可能需要更動到大幅度的架構,才有機會達成。這個時候,就大概是需要犧牲原有架構的時間點了。

在 Martin Fowler 的這篇文章裡,提到了 Google有個規則是在設計系統時,所滿足的需求,必須是當前需求的十倍。這也就是說,例如當前系統需求的規模是 X 的話,那麼,所設計出來的系統所能承載的規模就必須是 10X。當需求超出了一個數量級時,就意謂著系統最好丟掉,重新打造一個新的了。這是個有趣的規則,因為在設計一個系統時,已經預見了未來可能犧牲它的時機。

為什麼不是兩個數量級,即百倍呢?我想,這是因為可能會花去太多時間在設計及實作,但系統不見得有機會證明自身需要成長到百倍的規模。

日後即使面臨改弦易張的狀況,仍可以在事前的設計裡有所準備

即使架構可以被犧牲,但是,我想其實在設計時,還是可以針對犧牲做準備。就像 Google 的十倍規則,你可以想像若是持續成長,它總有一天要被犧牲掉,因而在設計時埋下日後犧牲掉它的伏筆,使得日後犧牲它時,更容易替換上新的程式碼。就像 Martin Fowler 在此文中也提到的,模組化有助於系統的取代。當一個系統的子系統都有足夠的模組化程度時,犧牲掉特定的子系統,將會簡單許多。

犧牲的架構,本身就是因應商務的演化可能發生的情境。而為了更容易犧牲換新,還是可以置入演化的概念,只不過演化的粒度更大罷了。

作者簡介


Advertisement

更多 iThome相關內容