
API貨幣化的最佳實踐:定價、打包和計費
這就是我最初接觸 OpenAPI 或 API Blueprint 等 API 描述文檔的方式,也是我們的第一本書建議 API 開發人員做事的方式。這在當時可能很有道理,但我很快發現這是一種不成熟的工作流程。
這里的一個問題是,“先寫代碼,后寫文檔”將 API 描述視為一種制作 API 參考文檔的奇特方式,而這只是 API 描述可以做的 100 件事之一。API 描述是機器可讀的文件,包含大量數據和元數據,可用于在早期階段收集反饋,以便在通過模擬編寫 API 之前提高 API 的質量,提供客戶端驗證和服務器端驗證。
首先編寫一堆代碼,部署東西,讓客戶上手并得到特別的親自動手處理等,這是一項非常繁重的工作。當整個階段完成后,花一個月的時間編寫“只會過時”的文檔,感覺就像是一項艱巨的任務,大多數企業都很難優先考慮,所以這項任務永遠都做不完。
這是我經常聽到的借口,說明為什么 WeWork 在 2016 年擁有約 50 名工程師,卻在任何時候都無法提供任何文檔,卻成功構建了約 30 個 API。缺乏文檔導致了我所見過的最瘋狂的時間和金錢浪費,人們構建了新版本的端點和 API,因為沒有人記得代碼是如何工作的。由于 API A 動態返回來自 API B 和 API C 的 JSON 塊,而沒有任何序列化器,因此甚至幾乎不可能閱讀代碼。
“我們稍后再寫文檔”意味著“我們不會寫文檔”,等你發現需要它時,已經太晚了。如果你是少數幾個能快速完成的人之一,那么讓這些文檔與代碼“同步”是大多數開發人員面臨的最大問題。在我關于這個主題的API the Docs演講中,當我問“這里有誰在努力保持代碼和文檔同步”時,整個房間大約 80-100 人都舉起了手。
有幾種方法,但即使你完全使用Dredd或類似的工具來保持同步,還有另一個我們尚未涉及的相當大的問題:你在給客戶機會使用它之前就構建了整個 API。
模擬經常被忽視,人們浪費時間和金錢構建無意義的 API,而這些 API 對客戶沒有幫助。這通常意味著 v2 在 v1 之后很快推出,也許需要 v3,因為會有更多客戶參與并提供更多反饋。這通常意味著 API 過于規范化,導致客戶需要發出 150 個 HTTP 請求來解決他們的用例,或者資源非常龐大,這意味著在 100 個用戶不需要的字段中隱藏著有用的數據。
無論您為 API 構建選擇了哪種 API 范式,用例驅動的 API通常都比數據驅動的 API 更有用。讓您的用戶盡早分享他們的反饋,因為此時更改仍然成本低且容易,而不是在已經投入生產并且更改變得更加復雜時。
這種對 API 描述采用代碼優先方法的流行變體是為了加快“稍后編寫文檔”部分的流程,許多 API 開發人員決定使用注釋或代碼注釋以特殊格式在其源代碼中散布 API 描述的片段。
有多種工具可用于此目的。在一些嚴格類型語言中,注釋工具包含的信息非常少,大多只有人類可讀的描述。可以從代碼中推斷出基本類型(“字符串”和“整數”)等信息,是否允許為空等。可悲的是,有些人認為這就是他們需要放入描述文檔的所有信息。他們忽略了示例值、“電子郵件”或“日期時間”等格式(可以增加驗證優勢并使文檔更有用),以及 OpenAPI 或 JSON Schema 中的其他更高級的功能,如 allOf、oneOf 等。
以注釋為首要功能的語言通常對此支持得更好一些,例如 Java。它們擁有大量的注釋系統,如果您在其中編寫了垃圾內容,則可能會出現語法錯誤。
class UserController {
@OpenApi(
path = "/users",
method = HttpMethod.POST,
// ...
)
public static void createUser(Context ctx) {
// ...
}
}
其他語言(例如 PHP)依賴于文檔塊注釋,而這只是在文本編輯器中寫入無意義的內容。
/**
* @OAGet(path="/2.0/users/{username}",
* operationId="getUserByName",
* @OAParameter(name="username",
* in="path",
* required=true,
* description=Explaining all about the username parameter
* @OASchema(type="string")
* ),
* @OAResponse(response="200",
* description="The User",
* @OAJsonContent(ref="#/components/schemas/user"),
* @OALink(link="userRepositories", ref="#/components/links/UserRepositories")
* )
* )
*/
public function getUserByName($username, $newparam)
{
}
在我看來,這似乎很粗糙,但人們為它辯護的理由如下:“在代碼附近添加注釋意味著開發人員更有可能保持代碼更新”。更有可能并不一定。
使用注釋時,您仍然需要使用其中一種方法來確保代碼和描述同步,但您必須添加構建步驟以從源代碼導出,然后通過 Dredd 或類似程序運行生成的 OpenAPI 文件。或者,您只能希望所有開發人員都記住并且“一切都會好起來”。
這里的反饋循環仍然有點長。它是在你編寫了一大堆代碼之后出現的,或者你可能將所有路由都寫到了一堆空控制器中,并且可以導出 OpenAPI 來創建模擬服務器,但這一切聽起來仍然很繁重。還有更多改進要做。
總體而言,“API 設計優先”是指實質上關閉反饋循環。在編寫任何代碼之前,您都會獲得模擬和文檔,因此在相當多的客戶確認界面符合他們的需求之前,您無需再對代碼進行任何修改,而且由于您已經擁有生成文檔所需的一切,因此您不必擔心以后再做這件事。
這種設計優先的特定風格仍然存在很多問題,但最近 API 領域的一些大人物一直在提倡這種做法。我認為他們提倡這種做法主要是因為他們厭倦了手寫 API 描述:在這里插入關于“數千行 YAML”的常見抱怨。也許他們一開始使用 DSL 來設計東西,然后在完成后切換到注釋,再次希望這樣“它更有可能保持最新”。
這里的眾多錯誤觀點之一是,認為有一個設計階段,然后你就停止設計,然后是代碼編寫的時間,之后我們不需要設計新的功能。
無論開發人員是手工編寫 API 代碼還是根據 API 描述生成代碼,設計階段都是永無止境的。設計是一個循環的生命周期,具有反饋循環,可產生新的資源和端點、新的全局版本或只是新的屬性。API 會隨著時間的推移而發展,在不收集客戶反饋的情況下推出新功能始終是一個壞主意,不僅僅是在初始設計階段。
我在Meetup上看到一些人使用“不可變服務”取得了成功,他們從 OpenAPI 生成路由、控制器、數據模型、docker 配置,甚至所有 Kubernetes 設置,然后他們只需在空白處插入一些業務邏輯并點擊部署即可。當他們需要更改合同時會發生什么?那將是一項全新的服務。不允許更改。計劃得足夠好,你不需要花很長時間調整它們,然后在需要更改時棄用并替換它們。不可變服務不是一種常見的做事方式,需要大量的紀律才能做好。
對于其他所有人來說,演變更為常見,因為即使人們為其 API 使用主要的全球版本,也會在過程中做出向后兼容的更改(新端點等)。要求您“導入”OpenAPI,然后從那里繼續的工具會讓您在未來無法設計新功能,即使他們提供了導出 OpenAPI 功能(許多人沒有)。
更糟糕的是,許多此類工具都會在云中保留其自己的 API 描述版本,而這些版本可以獨立于 Git 存儲庫中的 API 描述進行更改,這意味著您沒有真相來源:您有兩個謊言來源。
讓我們看一個工作流程,它允許您使用 API 描述作為單一事實來源,它會隨著您的代碼一起發展。
這種方法不再將 API 描述文檔視為事后的想法或苦差事,因為它們不是。過去可能需要 DSL 才能使編寫 OpenAPI 變得容易,但有了Stoplight Studio等令人驚嘆的可視化編輯器,使用 DSL 來避免手動編寫 YAML 的日子已經一去不復返了。Studio 可讓您在本地機器上免費使用 OpenAPI 文件,因此任何人都可以輕松構建強大的描述文檔,甚至可以輕松地在多個 API 之間重用模型,這樣整個“千行 YAML”的事情就完全消失了。
無論您使用 Studio、DSL 還是手寫,都請從空的存儲庫開始,只使用描述文檔。盡早并經常運行模擬服務器,獲取客戶的反饋,然后在達成一致后提交文檔。
然后您就可以開始編寫代碼了。使用描述文檔來支持服務器端驗證甚至 API 網關驗證的工具可以大大簡化您需要編寫的代碼量。
這不是代碼生成,而是使用 API 描述來支持生產驗證。您用于呈現文檔的相同描述文檔現在支持 API 的最復雜方面,并且事情永遠不會“不同步”,因為只有一個事實來源。
當客戶請求新功能時,您可以輕松添加新端點、引入新屬性等,并在開始編寫代碼之前獲得有關新內容的反饋。您永遠不會失去這種能力,因此您可以從先設計、再設計、再設計中受益。
這無助于保持響應“同步”,但是由于您的描述文檔就在您的存儲庫中,因此您可以使用它們來大大簡化您的單元/集成測試,從而覆蓋整個界面。
不要草率地編寫描述文檔。使用它們來規劃一些了不起的事情,并減少后續需要重新編碼的工作量。創建使用壽命更長、文檔更完善、測試更充分的 API,同時減少開發 API 所花費的總時間。
文章轉載自:API設計優化與代碼優化