精品国产一级在线观看,国产成人综合久久精品亚洲,免费一级欧美大片在线观看

Stripe面向未來的APIs 版本控制方案

責任編輯:editor004

作者: Brandur Leach

2017-08-28 11:20:09

摘自:INFOQ

然后,我們會按照時間向前追溯,請求這個過程中找到的每一個版本變更模塊,直到找到目標版本:Event的request 字段之前是一個字符串,現(xiàn)在是一個還包含冪等鍵的子對象(在上述版本變更中產(chǎn)生的):

談到API,它的變更并不受人歡迎。對于軟件開發(fā)人員來說,他們早已習慣了快速頻繁的功能迭代;而API開發(fā)者卻不一樣,哪怕只有一位用戶調用了API,那么這個API想要改動就很麻煩了,它牽一發(fā)而動全身。我們中許多人都了解Unix操作系統(tǒng)的演化。1994年,Unix-Haters手冊發(fā)布,其中包含很多有關該軟件的郵件——內容無所不包,從專門針對Teletype機器而優(yōu)化的、過度隱晦的命令名稱,到不可逆的文件刪除,再到選項過多的、不直觀的程序本身。20多年后,甚至是在眾多的現(xiàn)代衍生系統(tǒng)中,這些吐槽中的絕大多數(shù)仍然適用。這是因為,Unix的應用已經(jīng)如此廣泛,改變其行為影響巨大。但是,無論如何,它與客戶訂立的契約,定義了Unix接口的行為方式。

類似地,一個API代表了一份通信契約,沒有通力的配合和大量的工作是無法修改的。由于許許多多的企業(yè)都將Stripe作為基礎設施,所以我們從Stripe成立開始就一直在考慮這些契約。截至目前,我們要維護自2011年公司成立以來每個API的每個版本的兼容性。在這篇文章中,我們將分享在Stripe我們是如何管理API版本的。

編寫代碼集成API的過程中會加入某些固定的預期。如果一個端點返回一個名為verified的布爾型字段用于說明一個銀行賬戶的狀態(tài),那么用戶可能會編寫如下代碼:

if bank_account[:verified] ...else ...end

如果我們后來使用一個status字段代替了銀行賬戶的布爾型字段verified,由它包含verified的值(我們在2014年這樣做過),那么上述代碼就會被破壞,因為它依賴于一個此時已經(jīng)不存在的字段。這種類型的變更不具備向后兼容性,是我們應該避免的。以前有的字段應該一直保留,而且類型和名稱應該保持不變。不過,不是所有的變更都是向后不兼容的;例如,新增一個API端點,或者向一個已經(jīng)存在但曾未用過的API端點添加一個新字段,這些都是安全的。

以通力合作為基礎,我們也許能讓我們的用戶了解我們將要做出的變更,并讓他們更新自己的集成代碼,但即使可以這樣做,也不是很友好的方式。就像電網(wǎng)連接或供水,在連接好之后,API應該盡可能地保持運行不中斷。

Stripe的使命是提供互聯(lián)網(wǎng)經(jīng)濟基礎設施。就像電力公司不應該每隔兩年就改變電壓一樣,我們認為,我們應該讓用戶相信,我們提供的Web API會盡可能地保持穩(wěn)定。

API版本控制方案

Web API演進的一種常見方法是使用版本控制。用戶在發(fā)出請求時指定版本,API提供商可以根據(jù)需要修改下一個版本而又保持和當前版本兼容。當新版本發(fā)布后,用戶可以在方便的時候升級。

這經(jīng)常被視為一種主要的版本控制方案,將類似v1、v2、v3這樣的名稱作為URL前綴(如/v1/widgets)或者通過HTTP頭(如Accept)傳遞。這是一種有效的方法,但是,其主要缺點是,版本之間的變化太大,對用戶的影響也太大,其痛苦程度都快趕上重新集成了。這種方法也沒有明顯的優(yōu)勢,因為不愿意或無法升級的用戶就被困在了舊版本上。這時,提供商就必須做出艱難的選擇,是退役API版本,還是舍棄那些用戶,或者付出相當大的代價沒完沒了地維護舊版本。雖然讓提供商維護舊版本可能乍看之下對用戶是有好處的,但是,他們也間接地付出了獲得更新的速度下降的代價,因為工程時間花在了維護舊代碼而不是開發(fā)新特性上。

在Stripe,我們通過滾動版本實現(xiàn)版本控制,版本命名使用了API發(fā)布的日期(如2017-05-24)。雖然向后不兼容,但每個版本包含一小部分變化,這讓增量升級變得相對容易,這樣一來,集成就可以跟上版本更新的步伐。

用戶第一次發(fā)起API請求時,他們的賬戶會自動釘選到最新的可用版本,之后,他們發(fā)起的每次API調用都會被隱式地分配到那個版本。這種方法可以確保用戶不會突然接收到破壞性修改,并通過減少必要的配置讓最初的集成少了些痛苦。用戶可以手動設置Stripe-Version頭,或者從Stripe控制板更新其賬戶釘選的版本,覆寫任意單個請求的調用版本。

可能有讀者已經(jīng)注意到,Stripe API也有使用前綴路徑定義主版本(如/v1/charges)的情況。雖然我們確實會在某些時候使用這種方式,但是目前使用的方式在一段時間內將不會改變。如上所述,主版本變化往往會讓升級很痛苦,而且,我們很難想象,一個API的重新設計重要到要讓用戶受到這種程度的影響。我們目前采用的方法已經(jīng)支撐我們在過去六年中完成了將近100次向后不兼容的升級。

底層版本控制

版本控制總是要兼顧改善開發(fā)體驗和維護舊版本的成本。我們努力實現(xiàn)前者,同時又最小化后者,并實現(xiàn)了一個版本控制系統(tǒng)幫助我們實現(xiàn)這一目標。讓我們快速瀏覽一下它的工作原理。Stripe API每一種可能的響應都被編寫成類,我們稱之為API資源。API資源使用DSL定義可用的字段:

class ChargeAPIResource required :id, String required :amount, Integerend

API資源被記錄下來,其所描述的結構就是我們希望API的當前版本返回的內容。當我們需要做出向后不兼容的變更時,我們將其封裝在一個版本變更模塊中,其中定義了變更相關的注釋、一個轉換以及符合條件需要修改的API資源類型集:

class CollapseEventRequest < AbstractVersionChange description "Event objects (and webhooks) will now render " "`request` subobject that contains a request ID " "and idempotency key instead of just a string " "request ID." response EventAPIResource do change :request, type_old: String, type_new: Hash run do |data| data.merge(:request => data[:request][:id]) end endend

在主列表中為版本變更分配一個相應的API版本:

class VersionChanges VERSIONS = { '2017-05-25' => [ Change::AccountTypes, Change::CollapseEventRequest, Change::EventAccountToUserID ], '2017-04-06' => [Change::LegacyTransfers], '2017-02-14' => [ Change::AutoexpandChargeDispute, Change::AutoexpandChargeRule ], '2017-01-27' => [Change::SourcedTransfersOnBts], ... }end

版本變更被記錄下來,因此可以期望它們從當前的API版本按照順序自動向后適用。但每次版本變更都會假設,“即使后續(xù)可能有新的變更,但它們收到的數(shù)據(jù)應該和該API最初編寫出來時一樣”。

在生成響應時,API首先會通過描述當前版本的API資源來格式化數(shù)據(jù),然后根據(jù)下面三項內容中的一項確定目標API的版本:

如果提供了的話,則根據(jù)Stripe-Version頭;如果請求是以用戶的名義發(fā)送,則根據(jù)經(jīng)過OAuth授權的應用程序的版本;根據(jù)用戶釘選的版本,這是在用戶首次向Stripe發(fā)送請求時設定的。

然后,我們會按照時間向前追溯,請求這個過程中找到的每一個版本變更模塊,直到找到目標版本:

  在返回響應之前,請求由版本變更模塊處理

版本變更模塊會將更舊的版本從核心代碼路徑中剔除出去。在構建新產(chǎn)品時,開發(fā)人員多半可以不考慮它們。

具有副作用的變更

大多數(shù)向后不兼容的API變更都會修改響應,但情況并非總是如此。有時候,可能需要進行比較復雜的變更,其范圍超出了定義它的模塊。我們?yōu)檫@些模塊添加has_side_effects注解,它們定義的轉換變成了空操作:

class LegacyTransfers < AbstractVersionChange description "..." has_side_effectsend

在代碼的其他地方需要對它們進行檢查,看看是否還有效:

VersionChanges.active?(LegacyTransfers)

這種弱化的封裝讓具有副作用的變更更加難以維護,因此,我們會極力避免。

聲明式變更

自包含版本變更模塊的其中一個好處是,它可以定義注釋,說明它們影響的字段和資源。我們可以再次利用該注釋快速向用戶提供更多有用的信息。例如,我們的API變更日志是程序生成的,新版本的服務一部署,變更日志就會收到更新。

我們還針對特定的用戶裁剪API參考文檔。它知道誰登錄了,并根據(jù)賬戶的API版本注釋字段。這里,我們會警告開發(fā)人員,他們使用的API自釘選版本之后有向后不兼容的變更。Event的request 字段之前是一個字符串,現(xiàn)在是一個還包含冪等鍵的子對象(在上述版本變更中產(chǎn)生的):

  我們的文檔會檢測用戶的API版本并發(fā)出相關警告

最小化變更

提供廣泛的向后兼容性并不是免費的;每個新版本都意味著更多需要理解和維護的代碼。我們盡力讓我們編寫的代碼清晰,但是,如果整個項目里到處都是無法清晰封裝、需要足夠時間和大量檢查的版本變更,則會延緩項目、降低可讀性,讓API變得更加脆弱。我們采用了一些度量指標,盡力避免招致這種昂貴的技術債務。

即使我們有可用的版本控制系統(tǒng),我們還是盡可能地避免使用它,并設法在最初設計時保證API的正確性。輸出變更是通過一個輕量級的API審核流程收集的,這些變更會被寫入一個簡單的支持文檔中,并提交到郵件列表。這讓每一個變更提案都可以被公司里更多的人看到,讓我們可以在它們發(fā)布之前發(fā)現(xiàn)錯誤和不一致的地方。

我們一直都注意停用和使用的取舍。保持兼容性很重要,但即便如此,我們最終還是會希望開始退役舊的API版本。幫助用戶遷移到API的新版本讓他們可以利用新特性,同時也簡化了我們構建新特性的基礎。

變更的原則

滾動版本和支持這一機制的內部框架,這兩者的結合讓我們吸引了大量的用戶,我們對API做了大量的變更,同時又將對現(xiàn)有集成的影響降至了最低。這種方式依賴于我們過去幾年來總結出的一些規(guī)則。我們認為重要的——API升級應該是:

“輕量級的(Lightweight)”。盡量降低升級成本(不管是對用戶而言,還是對我們自己而言)。“一等的(First-class)”。讓版本控制成為API的一等概念,這樣,可以用它保持文檔和工具的準確和及時更新,并自動生成變更日志。“成本固定的(Fixed-cost)”。通過將舊版本密封進版本變更模塊來最小化維護成本。換句話說,在編寫新代碼時需要考慮的舊版本行為越少越好。

圍繞REST、GraphQL、gRPC的爭論及這些技術的發(fā)展讓我們興奮,更廣泛地說,是Web API未來會發(fā)展成什么樣子讓我們興奮,我們希望在接下來的很長一段時間內可以繼續(xù)支持版本控制方案。

查看英文原文:APIs as infrastructure: future-proofing Stripe with versioning

鏈接已復制,快去分享吧

企業(yè)網(wǎng)版權所有?2010-2024 京ICP備09108050號-6京公網(wǎng)安備 11010502049343號

  • <menuitem id="jw4sk"></menuitem>

    1. <form id="jw4sk"><tbody id="jw4sk"><dfn id="jw4sk"></dfn></tbody></form>
      主站蜘蛛池模板: 荣昌县| 福贡县| 修武县| 资阳市| 高雄市| 泗洪县| 金塔县| 大姚县| 化隆| 江城| 长治市| 卫辉市| 九台市| 阿拉善右旗| 息烽县| 九龙坡区| 汝城县| 黄山市| 德令哈市| 苏尼特右旗| 三门峡市| 饶河县| 沁阳市| 汉沽区| 金秀| 乌恰县| 吉隆县| 平远县| 彭水| 新龙县| 淄博市| 乐东| 资中县| 泰和县| 汾西县| 邓州市| 吉水县| 肇源县| 海林市| 柏乡县| 乌兰浩特市|