過去我們談過《程式碼的演化之路》主題,重點放在怎麼讓程式碼不斷的隨著需求的變化而演化。的確,在開發軟體時,一方面不可能在初始階段就預測出接下來所有可能的需求,另一方面,也不可能在一開始就實作出所有需要的功能。

先不說沒有人是先知,可以預測所有的需求,即使是預測需求錯誤或範圍假想過大,就會變成過度工程化,不只徒然浪費力氣,還讓系統變複雜了。更別說,其實,軟體需求經常是隨著市場態勢,或是對市場的了解,而持續動態改變的。

此外,想要畢其功於一役,在第一版就實作出所有的功能,也只會延遲軟體發行的時機。

這些現況都在在說明了,軟體程式碼持續隨著需求變化而演化的必要性。

不過,在過去所談到的程式碼的演化,大多集中在比較微觀、比較局部的層次,也就是說,探討的重心放在類別,以及類別之間的關係上。不過,在那之上,其實還有比較巨觀的層次,也就是軟體架構層次的演化。

當軟體必須隨著需求、功能的變化而跟著演化時,我們演化的層級,有時不僅僅是局部程式碼的演化,當變化足夠大時,還會引發軟體架構跟著演化。而在本文中,試著探討軟體架構演化的一些現象及特質。

軟體架構有其特定想要達成的功能,當然也會碰到極限

雖然,軟體架構或程式碼的演化,通常是伴隨著軟體功能的演化而發生的,但是很有趣的是,軟體功能在演化時,往往是獨立於當時的軟體架構或程式碼的。

也就是說,決定軟體功能的角色,在考量下一版應修改或納入的軟體功能時,常常是不會考慮到現有的軟體架構適不適合如此的演化。

這其實很合理,因為,軟體功能的決定,往往是因應市場需求或所接收到的使用者意見回饋,而不是從技術的角度去判斷那些功能容易做、適合做,畢竟,開發軟體的主要目的是為了滿足市場需要,而不是為了開發的容易。也因此,當功能持續擴展時,就是考驗軟體架構的時機了。

如果從程式碼演化的觀點來看,容易加入新功能的架構,就是好的架構。但是,每個架構都有其極限,沒有辦法無止盡的容納新功能,於是,當所設計出來的架構,無法滿足輕易新增功能的要求時,就衍生出演化架構的需要。

我們可以說,每個架構都有其「能力」,而這個能力有其極限,當我們在這個架構底下,可以輕易增加功能或改變功能時,代表這個架構的能力還足以應付我們的需要。

此時,我們可以說這個架構到達了「成熟」的階段。或許新增的功能會造成局部的程式碼演化,但架構並不需要更動。

軟體架構成熟與演化之間的交替

當然,有些時候,我們在軟體中所實作出來的功能,未必完全發揮出該架構能力的極限。通常軟體發行第一個版本時的架構,足以應付接下來的幾次改版,但是漸漸地,當軟體功能的演化就會超出了架構的能力,此時,架構就得因應做演化,以擴展其「能力」,才能進一步滿足新的功能需求。架構演化的同時,會為新的架構帶來更多的能力,使其足以容納當前或未來想要新增的功能。

所以說,軟體架構的「成熟」和「演化」是交互出現的。軟體架構在達到成熟的階段之後,開始讓軟體長出諸般功能,接著,想要擴展的功能超出了架構的能力,此時軟體就會進入演化,直到它的能力成熟足以繼續添加新功能。

無法應變時,多半以演化或疊床架屋的方式來解決

我們試著回想起來,我們在開發軟體的第一版本時,通常會針對已知的需求(或許還依據經驗或計畫,預測了一下未來的需求),設計出一套架構,因此,這套架構的「能力」起碼滿足了已知和可預測的需求。

通常,第一個版本花去最多的時間。有一部份的原因是,基礎架構得先建立出來──我們花了許多的時間在「打地基」。即使第一版的房子蓋的不那麼漂亮,但是倘若地基打得好,接下來房子變漂亮的速度就快多了。

隨著房子持續變漂亮,對房子的需求也就愈來愈多樣化,甚至超出了原先所建地基所能負荷的範圍。此時,有人選擇演化架構,重新調整整座建築物的基礎設施,使得它日後還可以更穩固的繼續發展下去。

但是,也有人選擇疊床架屋,他們不演化架構,而是在現有的架構上,使用一些 「work around」 。這些「work around」 是一些在不演化架構的前提下,繼續擴展軟體功能的方法和技巧。它讓軟體功能的發展超出了軟體架構的能力,就像 CPU 的「超頻」那樣。但也正如 CPU 的「超頻」一樣,你沒有辦法無止盡地超頻下去,超頻有其極限,而且超得愈高,系統愈不穩定。

軟體架構演化的必要性

而且,開發的現實就是,當交付的期限逼近時,大家只會把心力放在功能的實作上。

與其伴隨著更動的軟體功能而演化架構,應該許多人會寧可把精神花在新功能的實作,而這就是架構演化的困難之一。

軟體架構要容易演化,基本精神還是和微觀的程式碼演化相去不遠。像是抽象化、資訊隱藏、模組化、降低模組間的相依性 ……等等。但是因為涉及的程式碼範圍更大,因此,必須更為慎重。一開始選對了一個好的基礎架構,例如你選了一個分層的架構(multi-layered architecture),即使一開始你只實作了兩層的架構,但是每一層有其各自定義的抽象化程度,隨著功能的演化,導致架構必須做演化時,有可能再增加一層,就得以達到成熟了。因此,一開始的選擇,相形之下就頗為重要。

就和程式碼的演化一樣,每一次架構的演化都是在為下一次的演化做準備,我們無法預測很遠的未來,卻有機會看清楚接下來的可能變化。

每次演化時,除了增加足夠的「能力」,也要判斷出接下來的架構方向,使得下一次的演化更容易發生。

因為,我們很少憑藉著預測未來的先知,來做出好的架構,實際上,我們比較容易倚靠著一次又一次成功的演化,讓我們的架構能夠持續適應環境。

當演化的方向錯誤或是持續的疊床架屋時,很有可能導致新功能的需求產生時,現有架構的能力不足,但是要增加架構能力的成本卻太高。一旦增加架構能力的成本太高、又無法繼續疊床架屋時,許多人就會走上「砍掉重練」的道路。

然而,每次的砍掉重練,都隱含著大量的成本廢棄,因為過去的程式碼其實都是長期累積的資產。這更說明了成功的軟體架構演化有多麼的重要。

在本文中,描述了軟體架構的「能力」、「成熟」、以及「演化」的動態關係。在我們日常的開發生活中,這些現象時常在發生,但我們或許並不自覺。

若能深入了解這個動態關係,有助於我們設計出更具演化能力的軟體架構。

專欄作者

熱門新聞

Advertisement