程式設計者時常會需要遇到設計、實作API的情況。

而所謂的API,即為應用程式介面(Application Programming Interface),通常是作業系統或是程式庫,為了便利應用程式的撰寫,而提供的一組程式碼。

其目的在於隱藏底層實作的細節,透過簡化、抽象又一致的介面,讓應用程式的撰寫者,並不需要明白底層確切的實作方式,也不需要花費時間重新開發這些共通的輪子,而能更專注在自己所欲開發的應用程式之上。

開發API的目的
而程式設計者開發API,其目的可能是為了僅供團隊內部的應用程式開發之用,也有可能是提供給外部的開發團隊所用,甚至做為一個產品來發行。既然如此,API本身的介面或是實作內容,也都會有持續演化的情況,因此會有版本變化的可能性。

API的介面,規範了API實作程式碼與客戶端程式碼(client code),也就是使用API的程式碼彼此相介接的方式。除了這個介面本身的長相,例如,每個函式的名稱、傳入那些引數、每個引數的型別、……等等之外,介面下的行為也算是介面的一部份,例如每個函式在呼叫後,應該產生什麼作用,回傳值的意義等等,都是屬於介面的一部份。

為什麼API介面需要改變?以及會造成什麼樣的影響?
介面的長相可以說是「語法(syntax)」,而其行為可以說是「語義(semantics)」了。無論是語法或是語義的變動,都可以算是API介面的變動了。

API的實作程式碼之所以需要改變,有很多可能的原因,包括有臭蟲、效能不足、資源耗用過多、……等等,一般我們在撰寫應用程式時,可能會遇到的修改原因,都有可能同樣會發生在API的實作程式碼上。

API實作程式碼的改變,通常不涉及API「語法」及「語義」的改變,否則,這改變就成了介面程式的更動了。實作程式碼的改變相較於API介面的改變,對客戶端程式碼所造成的衝擊,當然是小很多。

因為在這種情況下,API的實作程式碼通常是往好的方向去發展,例如解決了臭蟲、變得更快、變得消耗更少資源,而且因為介面的語法和語義都沒有改變,意謂著和客戶端程式碼介接的部份及API本身實作的行為,都沒有改變,所以也不會導致客戶端程式碼必須修改。

而這些來自於實作的改良,也都被封裝隱藏在API的介面底下。日後,即使API改版了,理論上也不會影響到客戶端程式碼,因為客戶端程式碼可以直接基於新版的API來運行。API介面的更動對API來說,就是一件重大的事情了,因為這意謂著,基於過去所發行舊版本API介面的客戶端程式碼,有可能因此而受到波及,而必須連帶修改。

所以,對API的設計者而言,對於介面,多半採取能不動就不動的態度。但是,基於一些原因,有時候我們免不了要更動API的介面。我們可以透過實作的改良來讓API效能更好、品質更好,但是我們沒辦法單靠實作的改良,來讓API滿足更多的使用需求。

有時候,API一開始的設計並不夠理想,隨著客戶端程式碼在應用中實際的操演,或隨著有很多能力更好的設計人員加入,API設計團隊會發現需要對API介面做修改,才能換來更長遠的長治久安。而「提供更廣泛的功能」和「改善不佳的原有設計」,就成了API介面修改的兩項主要原因。

從版本命名的系統,來看API改變後對客戶端程式碼的影響程度

關於API變化的程度,讓我想到了,曾讀過GitHub共同創辦人Tom Preston-Werner所寫的 《Semantic Versioning》,它是一套對於版本號的命名系統。它將版本號分為X.Y.Z,其中X是主要(major)版本號,Y是次要(minor)版本號,而Z是修正(patch)版本號。在這個系統中提到了,這三個版本號組成遞增的原則。

若只有往下相容的臭蟲被修正時,則遞增修正版本號。那什麼是往下相容的臭蟲修正呢?即僅修正錯誤行為的內部變動。

同時,當API介面引進新的、往下相容的功能時,則次要版本號必須遞增。而若內部的實作程式碼有大量的新功能或改進時,則次要版本號「可以」遞增。在遞增次要版本號時,可以一併包括在修正版本號層級的修正。最後,若API介面上引進了無法向下相容的改變時,就必須遞增主要版本號,當然,在遞增主要版本號時,可以一併包括在次要及修正版本號層級的修正。

從這種版本號的命名系統,我們不難看出,不同類型的改變所造成的衝擊程度,究竟有多大。僅在修正版本號層級的修改,當然是影響幅度最小的;次要版本號層級的,次之;影響最大的,當然是主要版本號層級上的修改。

這樣的命名系統,除了讓版本號的命名有一定的規則可依循之外,也可以讓每個接觸到的人員,都可以很輕易地從版本號,明白變遷的幅度。從介面引入了無法向下相容的變動,而必須改變主要版本號的規則來看,就不難了解,這樣的變動對API來說,是一件多麼嚴重,而且還要慎重的事情。因此,非到萬不得已,一般來說,是不會在介面上引入無法向下相容的修改。

分析現行API維持向下相容性的幾種做法
向下相容性的重要性,不言可喻。

而當新版的API發生了不可向下相容的情況時,倚賴舊版API的客戶端程式碼只有兩種選擇,一種是繼續維持使用舊版本的API。但這麼一來,新版API中所導入的新功能或修正,就無法在客戶端程式碼中享受到。

此外,有些API會同時維持多種主要版本號的版本,以便讓舊版的API客戶程式碼,也能繼續得到對臭蟲的修正,但新功能是肯定無法在舊版的分支中運用到了。

這也是一種API設計團隊在納入新設計,以及對舊版本客戶支援的兩全之道。

不過,同樣的錯誤修正,要同時納入多個主要版本號的API程式碼中,當然也會提高版本管理上的複雜度。

對客戶端程式碼而言,除了選擇繼續維持使用舊版本的API之外,也可以選擇跟上新版的API。不過,視API介面發生向下相容的幅度不同,客戶端程式碼受到衝擊的程度也不同,而需要連帶跟著修改的程度也不同。

無論如何,發生不能向下相容的情況,都是一件麻煩的事情。

所以,除非可以帶來明顯又夠多的好處,一般的API設計者是不會這麼做的,尤其當API的客戶端程式設計者眾多時,任何一個不能往下相容的情況,都會造成所有的設計者必須思考究竟要放棄新功能,或是也跟著修改自己程式碼的決策。

因此,如何設計盡量能保持向下相容的介面,就成了API設計者的一個重要的議題。而當真的有強烈的需要,必須引入不能往下相容的介面時,如何盡可能地降低影響的層面,也是另一個需要考驗設計者的問題。

在下一回中,我們將繼續探討這項議題。

 

專欄作者

熱門新聞

Advertisement