<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>模組十一案例庫：API 設計與對外契約 on Tarragon</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/</link><description>Recent content in 模組十一案例庫：API 設計與對外契約 on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 03 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/backend/11-api-design/cases/index.xml" rel="self" type="application/rss+xml"/><item><title>11.C1 Fielding 論文第 5 章：REST 是約束推導的架構風格</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-fielding-dissertation-ch5/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-fielding-dissertation-ch5/</guid><description>&lt;p>這個案例的核心責任是提供 REST 定義的一手基準、讓各流派的論證有共同錨點。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>REST 在論文中由六個約束從 null style 逐步推導：client-server、stateless、cache、uniform interface、layered system、code-on-demand（optional）。Uniform interface 是 REST 的區別性特徵、由四個子約束構成：resource identification、manipulation through representations、self-descriptive messages、hypermedia as the engine of application state。文中明示 uniform interface 以效率為代價換取一般性與互動可見性。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>這是所有 REST 論爭的 SSoT 錨點 — 純粹派、pragmatic 派、hypermedia 復興派都引用它。教學價值在揭露「REST 是約束集合、不是 URL + JSON + 動詞的 checklist」、並建立「約束是有 trade-off 的推導結果、不是教條」的框架。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/rest-semantics-dispute/" data-link-title="REST 語意學之爭：一個詞的定義權爭奪" data-link-desc="Fielding 原義、業界 JSON-over-HTTP 慣行、第三方史觀三方的完整論證 — 以及這場命名之爭對工程溝通的實際影響">REST 語意學之爭&lt;/a>（定義基準、已引用）、&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/resource-modeling-operation-semantics/" data-link-title="11.3 資源建模與操作語意" data-link-desc="endpoint 該建模成資源還是動作、HTTP method 與 status 承諾了什麼、available actions 由誰計算 — 建模決策的判準">11.3 資源建模&lt;/a>（representation 概念來源、已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://roy.gbiv.com/pubs/dissertation/rest_arch_style.htm">Architectural Styles and the Design of Network-based Software Architectures, Chapter 5（Roy Fielding、2000）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供 REST 定義的一手基準、讓各流派的論證有共同錨點。</p>
<h2 id="觀察">觀察</h2>
<p>REST 在論文中由六個約束從 null style 逐步推導：client-server、stateless、cache、uniform interface、layered system、code-on-demand（optional）。Uniform interface 是 REST 的區別性特徵、由四個子約束構成：resource identification、manipulation through representations、self-descriptive messages、hypermedia as the engine of application state。文中明示 uniform interface 以效率為代價換取一般性與互動可見性。</p>
<h2 id="判讀">判讀</h2>
<p>這是所有 REST 論爭的 SSoT 錨點 — 純粹派、pragmatic 派、hypermedia 復興派都引用它。教學價值在揭露「REST 是約束集合、不是 URL + JSON + 動詞的 checklist」、並建立「約束是有 trade-off 的推導結果、不是教條」的框架。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/rest/rest-semantics-dispute/" data-link-title="REST 語意學之爭：一個詞的定義權爭奪" data-link-desc="Fielding 原義、業界 JSON-over-HTTP 慣行、第三方史觀三方的完整論證 — 以及這場命名之爭對工程溝通的實際影響">REST 語意學之爭</a>（定義基準、已引用）、<a href="/blog/backend/11-api-design/resource-modeling-operation-semantics/" data-link-title="11.3 資源建模與操作語意" data-link-desc="endpoint 該建模成資源還是動作、HTTP method 與 status 承諾了什麼、available actions 由誰計算 — 建模決策的判準">11.3 資源建模</a>（representation 概念來源、已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://roy.gbiv.com/pubs/dissertation/rest_arch_style.htm">Architectural Styles and the Design of Network-based Software Architectures, Chapter 5（Roy Fielding、2000）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C2 Fielding：REST API 必須是 hypertext-driven</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-fielding-hypertext-driven/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-fielding-hypertext-driven/</guid><description>&lt;p>這個案例的核心責任是記錄「語意學之爭」的引爆點：定義擁有者親自劃線。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Fielding 在 2008 年的 blog 文列出六條 REST API 規則：協定獨立、不改標準協定、描述精力放在 media type 與 relation name（而非逐 URI 逐方法的文件）、server 必須擁有自己的 namespace（client 不得假設固定 URI 結構）、resource type 對 client 不可見、所有 application state transition 必須由 client 從 server 提供的選項中選擇驅動。文中直接批評自稱 REST 的 RPC-style API 造成過度耦合。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>教學判準：「client 是否需要 out-of-band 文件才能操作 — 需要、就不是 Fielding 意義的 REST」。同時揭露命名權之爭：術語定義者與業界慣行的張力、是流派層敘事的起點。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/rest-semantics-dispute/" data-link-title="REST 語意學之爭：一個詞的定義權爭奪" data-link-desc="Fielding 原義、業界 JSON-over-HTTP 慣行、第三方史觀三方的完整論證 — 以及這場命名之爭對工程溝通的實際影響">REST 語意學之爭&lt;/a>（核心案例、已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven">REST APIs must be hypertext-driven（Roy Fielding blog、2008）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄「語意學之爭」的引爆點：定義擁有者親自劃線。</p>
<h2 id="觀察">觀察</h2>
<p>Fielding 在 2008 年的 blog 文列出六條 REST API 規則：協定獨立、不改標準協定、描述精力放在 media type 與 relation name（而非逐 URI 逐方法的文件）、server 必須擁有自己的 namespace（client 不得假設固定 URI 結構）、resource type 對 client 不可見、所有 application state transition 必須由 client 從 server 提供的選項中選擇驅動。文中直接批評自稱 REST 的 RPC-style API 造成過度耦合。</p>
<h2 id="判讀">判讀</h2>
<p>教學判準：「client 是否需要 out-of-band 文件才能操作 — 需要、就不是 Fielding 意義的 REST」。同時揭露命名權之爭：術語定義者與業界慣行的張力、是流派層敘事的起點。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/rest/rest-semantics-dispute/" data-link-title="REST 語意學之爭：一個詞的定義權爭奪" data-link-desc="Fielding 原義、業界 JSON-over-HTTP 慣行、第三方史觀三方的完整論證 — 以及這場命名之爭對工程溝通的實際影響">REST 語意學之爭</a>（核心案例、已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven">REST APIs must be hypertext-driven（Roy Fielding blog、2008）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C3 Richardson 成熟度模型：分級階梯與它的自我聲明</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-fowler-richardson-maturity-model/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-fowler-richardson-maturity-model/</guid><description>&lt;p>這個案例的核心責任是提供業界最通行的 REST 教學階梯、以及它「不是 REST 認證」的原始 caveat。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>模型分四級：Level 0（HTTP 當 RPC 隧道、單一 endpoint）、Level 1（資源化、逐資源 URI）、Level 2（HTTP 動詞與狀態碼正確使用）、Level 3（hypermedia controls / HATEOAS）。Fowler 明文標注：RMM 是理解 REST 元素的思考工具、不是 REST 的分級定義；並記錄 Fielding 的立場 — 只有 Level 3 才算 REST。模型本身出自 Richardson 的 QCon 演講、此文屬權威轉述。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>教學價值在雙重性：用 RMM 定位自己的 API 在哪一級是合法用法、拿 RMM 當 REST 認證是誤用。它也是「業界普遍停在 Level 2」這個實證現象的參照系。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/richardson-maturity-practical-reading/" data-link-title="Richardson 成熟度的實用讀法" data-link-desc="RMM 四級當定位與溝通工具的用法、每一級的工程意義、以及把它當合規認證或升級路線圖的誤用邊界">Richardson 成熟度的實用讀法&lt;/a>（骨幹、已引用）、11.3 資源建模（定位工具段、已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://martinfowler.com/articles/richardsonMaturityModel.html">Richardson Maturity Model（Martin Fowler、2010）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供業界最通行的 REST 教學階梯、以及它「不是 REST 認證」的原始 caveat。</p>
<h2 id="觀察">觀察</h2>
<p>模型分四級：Level 0（HTTP 當 RPC 隧道、單一 endpoint）、Level 1（資源化、逐資源 URI）、Level 2（HTTP 動詞與狀態碼正確使用）、Level 3（hypermedia controls / HATEOAS）。Fowler 明文標注：RMM 是理解 REST 元素的思考工具、不是 REST 的分級定義；並記錄 Fielding 的立場 — 只有 Level 3 才算 REST。模型本身出自 Richardson 的 QCon 演講、此文屬權威轉述。</p>
<h2 id="判讀">判讀</h2>
<p>教學價值在雙重性：用 RMM 定位自己的 API 在哪一級是合法用法、拿 RMM 當 REST 認證是誤用。它也是「業界普遍停在 Level 2」這個實證現象的參照系。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/rest/richardson-maturity-practical-reading/" data-link-title="Richardson 成熟度的實用讀法" data-link-desc="RMM 四級當定位與溝通工具的用法、每一級的工程意義、以及把它當合規認證或升級路線圖的誤用邊界">Richardson 成熟度的實用讀法</a>（骨幹、已引用）、11.3 資源建模（定位工具段、已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://martinfowler.com/articles/richardsonMaturityModel.html">Richardson Maturity Model（Martin Fowler、2010）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C4 Carson Gross：REST 如何變成 REST 的反義詞</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-gross-opposite-of-rest/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-gross-opposite-of-rest/</guid><description>&lt;p>這個案例的核心責任是把 Fielding 的抽象約束翻成工程史敘事、代表 hypermedia 復興派的論證。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Gross 重建語意漂移路徑：XML-RPC / SOAP 時代、JSON 取代 XML（但 JSON 是純資料、不是 hypertext）、RMM 普及但業界停在 Level 2、SPA 讓前端與 REST 原則脫鉤、GraphQL 乾脆放棄 REST 名義。結論：今日的 JSON API 是掛 REST 名的 RPC；真正 RESTful 的是 hypermedia-driven 的 HTML 回應。文中同時承認 GraphQL 對 thick-client 場景是合理選擇。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>關鍵教學判準：「REST 的 self-describing 特性是為 uniform client（瀏覽器）設計、machine-to-machine 的 JSON client 用不上」— 這條是復興派與 pragmatic 派唯一共識的觀察、兩派從它推出相反結論、適合當章節的對照樞紐。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 與 HATEOAS 復興&lt;/a>（主案例、已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://htmx.org/essays/how-did-rest-come-to-mean-the-opposite-of-rest/">How Did REST Come To Mean The Opposite of REST?（htmx essays、Carson Gross）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是把 Fielding 的抽象約束翻成工程史敘事、代表 hypermedia 復興派的論證。</p>
<h2 id="觀察">觀察</h2>
<p>Gross 重建語意漂移路徑：XML-RPC / SOAP 時代、JSON 取代 XML（但 JSON 是純資料、不是 hypertext）、RMM 普及但業界停在 Level 2、SPA 讓前端與 REST 原則脫鉤、GraphQL 乾脆放棄 REST 名義。結論：今日的 JSON API 是掛 REST 名的 RPC；真正 RESTful 的是 hypermedia-driven 的 HTML 回應。文中同時承認 GraphQL 對 thick-client 場景是合理選擇。</p>
<h2 id="判讀">判讀</h2>
<p>關鍵教學判準：「REST 的 self-describing 特性是為 uniform client（瀏覽器）設計、machine-to-machine 的 JSON client 用不上」— 這條是復興派與 pragmatic 派唯一共識的觀察、兩派從它推出相反結論、適合當章節的對照樞紐。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 與 HATEOAS 復興</a>（主案例、已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://htmx.org/essays/how-did-rest-come-to-mean-the-opposite-of-rest/">How Did REST Come To Mean The Opposite of REST?（htmx essays、Carson Gross）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C5 htmx HATEOAS essay：透支帳戶的兩種表徵對照</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-htmx-hateoas-html-necessity/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-htmx-hateoas-html-necessity/</guid><description>&lt;p>這個案例的核心責任是提供 HATEOAS 爭論裡最小可教的具體範例。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>essay 用銀行帳戶透支做對照：HTML 回應在透支時只回 deposit 連結 — 業務狀態直接編碼在可用操作裡、client 零業務知識；JSON 回應回 &lt;code>status: &amp;quot;overdrawn&amp;quot;&lt;/code> 欄位、client 必須靠 out-of-band 文件理解語意與下一步 URL。結論主張 HTML 這類 natural hypermedia 是實作 RESTful 系統的 practical necessity、在 JSON 上疊 hypermedia controls 的做法已被業界廣泛拒絕。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>教學判準：「available actions 由誰計算 — server 算完放進 response、還是 client 讀狀態欄位自己算」是 HATEOAS 有無的操作型判別法、比背定義有效。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/resource-modeling-operation-semantics/" data-link-title="11.3 資源建模與操作語意" data-link-desc="endpoint 該建模成資源還是動作、HTTP method 與 status 承諾了什麼、available actions 由誰計算 — 建模決策的判準">11.3 資源建模與操作語意&lt;/a>（範例主寫、已引用）、&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 與 HATEOAS 復興&lt;/a>（範例層引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://htmx.org/essays/hateoas/">HATEOAS（htmx essays）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供 HATEOAS 爭論裡最小可教的具體範例。</p>
<h2 id="觀察">觀察</h2>
<p>essay 用銀行帳戶透支做對照：HTML 回應在透支時只回 deposit 連結 — 業務狀態直接編碼在可用操作裡、client 零業務知識；JSON 回應回 <code>status: &quot;overdrawn&quot;</code> 欄位、client 必須靠 out-of-band 文件理解語意與下一步 URL。結論主張 HTML 這類 natural hypermedia 是實作 RESTful 系統的 practical necessity、在 JSON 上疊 hypermedia controls 的做法已被業界廣泛拒絕。</p>
<h2 id="判讀">判讀</h2>
<p>教學判準：「available actions 由誰計算 — server 算完放進 response、還是 client 讀狀態欄位自己算」是 HATEOAS 有無的操作型判別法、比背定義有效。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/resource-modeling-operation-semantics/" data-link-title="11.3 資源建模與操作語意" data-link-desc="endpoint 該建模成資源還是動作、HTTP method 與 status 承諾了什麼、available actions 由誰計算 — 建模決策的判準">11.3 資源建模與操作語意</a>（範例主寫、已引用）、<a href="/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 與 HATEOAS 復興</a>（範例層引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://htmx.org/essays/hateoas/">HATEOAS（htmx essays）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C6 HAL spec：JSON hypermedia 標準化的過期 draft</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-kelly-hal-spec/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-kelly-hal-spec/</guid><description>&lt;p>這個案例的核心責任是記錄「在 JSON 上補 hypermedia」路線的標準化現狀。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>HAL 用 &lt;code>_links&lt;/code> 與 &lt;code>_embedded&lt;/code> 兩個保留屬性在 JSON 上表達 hypermedia controls、目標是讓通用函式庫可以在任何 HAL API 上重用（uniform interface）。狀態：IETF Internet-Draft（最新 v11）、已過期歸檔、無標準地位；生態面曾是 Spring HATEOAS 的預設格式。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>教學判準：「格式碎片化（HAL / Siren / JSON-LD / Collection+JSON 並立、無一勝出）是 hypermedia JSON 未形成 uniform client 的結構性原因」— 這正是 Fielding 說要把力氣花在 media type 上、而業界沒收斂的實證。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 與 HATEOAS 復興&lt;/a>（格式現實段、與 Siren 並列、已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://datatracker.ietf.org/doc/html/draft-kelly-json-hal-08">JSON Hypertext Application Language（IETF draft-kelly-json-hal、已過期）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄「在 JSON 上補 hypermedia」路線的標準化現狀。</p>
<h2 id="觀察">觀察</h2>
<p>HAL 用 <code>_links</code> 與 <code>_embedded</code> 兩個保留屬性在 JSON 上表達 hypermedia controls、目標是讓通用函式庫可以在任何 HAL API 上重用（uniform interface）。狀態：IETF Internet-Draft（最新 v11）、已過期歸檔、無標準地位；生態面曾是 Spring HATEOAS 的預設格式。</p>
<h2 id="判讀">判讀</h2>
<p>教學判準：「格式碎片化（HAL / Siren / JSON-LD / Collection+JSON 並立、無一勝出）是 hypermedia JSON 未形成 uniform client 的結構性原因」— 這正是 Fielding 說要把力氣花在 media type 上、而業界沒收斂的實證。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 與 HATEOAS 復興</a>（格式現實段、與 Siren 並列、已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://datatracker.ietf.org/doc/html/draft-kelly-json-hal-08">JSON Hypertext Application Language（IETF draft-kelly-json-hal、已過期）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C7 Siren spec：表達力更完整、採用曲線停滯</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-swiber-siren-adoption/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-swiber-siren-adoption/</guid><description>&lt;p>這個案例的核心責任是跟 HAL 對照、揭露 hypermedia 格式勝出的變數。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Siren 用 entities / actions / links 三元件表達資源、特點是 first-class 的 &lt;code>actions&lt;/code>（含 method 與欄位、比 HAL 的純連結更接近 HTML form）。採用現狀：約 1.3k stars、最後 release v0.6.2 停在 2017-04、media type &lt;code>application/vnd.siren+json&lt;/code>。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>教學判準：「表達力不是 hypermedia 格式勝出的變數、client 生態才是」— Siren 表達力上更完整（actions 近似 HTML form 的 JSON 化）、採用反而更少。同時支撐 htmx 派「JSON 不是 natural hypermedia」與 pragmatic 派「別等標準收斂」兩邊的決策。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 與 HATEOAS 復興&lt;/a>（格式現實段、與 HAL 並列、已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/kevinswiber/siren">Siren: a hypermedia specification（Kevin Swiber、GitHub repo）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是跟 HAL 對照、揭露 hypermedia 格式勝出的變數。</p>
<h2 id="觀察">觀察</h2>
<p>Siren 用 entities / actions / links 三元件表達資源、特點是 first-class 的 <code>actions</code>（含 method 與欄位、比 HAL 的純連結更接近 HTML form）。採用現狀：約 1.3k stars、最後 release v0.6.2 停在 2017-04、media type <code>application/vnd.siren+json</code>。</p>
<h2 id="判讀">判讀</h2>
<p>教學判準：「表達力不是 hypermedia 格式勝出的變數、client 生態才是」— Siren 表達力上更完整（actions 近似 HTML form 的 JSON 化）、採用反而更少。同時支撐 htmx 派「JSON 不是 natural hypermedia」與 pragmatic 派「別等標準收斂」兩邊的決策。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 與 HATEOAS 復興</a>（格式現實段、與 HAL 並列、已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://github.com/kevinswiber/siren">Siren: a hypermedia specification（Kevin Swiber、GitHub repo）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C8 Ben Morris：不做 hypermedia 的 pragmatic REST（反例對照）</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-morris-pragmatic-no-hateoas/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-morris-pragmatic-no-hateoas/</guid><description>&lt;p>這個案例的核心責任是提供 hypermedia 復興派的對照組：pragmatic 派的完整反對論證。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>文章列四條反對論據：client 開發者實務上讀文件直打 endpoint、不會跟連結走（解耦收益落空）；格式無共識、「不會出現資料版的瀏覽器這種 generic REST client」；hypermedia 傳不了資料語意、文件仍不可免；複雜度與 response 膨脹沒有換到等比收益。主張保留 stateless 資源設計的收益、捨 HATEOAS、引 Twitter / Facebook API 為 pragmatic 成功例。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>論證方式是逐條拆 HATEOAS 的收益假設（decoupling、discoverability、evolvability）在 machine-to-machine 場景不成立、而非攻擊名詞。教學判準：「HATEOAS 的收益前提是存在會動態跟連結走的 client — 先問你的 client 是誰、再決定投不投 hypermedia」。與 C4 / C5 形成同觀察、反結論的完整對照。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/rest-semantics-dispute/" data-link-title="REST 語意學之爭：一個詞的定義權爭奪" data-link-desc="Fielding 原義、業界 JSON-over-HTTP 慣行、第三方史觀三方的完整論證 — 以及這場命名之爭對工程溝通的實際影響">REST 語意學之爭&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 復興&lt;/a>（pragmatic 派立場、皆已引用）。反例。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.ben-morris.com/pragmatic-rest-apis-without-hypermedia-and-hateoas/">Pragmatic REST APIs without hypermedia and HATEOAS（Ben Morris）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供 hypermedia 復興派的對照組：pragmatic 派的完整反對論證。</p>
<h2 id="觀察">觀察</h2>
<p>文章列四條反對論據：client 開發者實務上讀文件直打 endpoint、不會跟連結走（解耦收益落空）；格式無共識、「不會出現資料版的瀏覽器這種 generic REST client」；hypermedia 傳不了資料語意、文件仍不可免；複雜度與 response 膨脹沒有換到等比收益。主張保留 stateless 資源設計的收益、捨 HATEOAS、引 Twitter / Facebook API 為 pragmatic 成功例。</p>
<h2 id="判讀">判讀</h2>
<p>論證方式是逐條拆 HATEOAS 的收益假設（decoupling、discoverability、evolvability）在 machine-to-machine 場景不成立、而非攻擊名詞。教學判準：「HATEOAS 的收益前提是存在會動態跟連結走的 client — 先問你的 client 是誰、再決定投不投 hypermedia」。與 C4 / C5 形成同觀察、反結論的完整對照。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/rest/rest-semantics-dispute/" data-link-title="REST 語意學之爭：一個詞的定義權爭奪" data-link-desc="Fielding 原義、業界 JSON-over-HTTP 慣行、第三方史觀三方的完整論證 — 以及這場命名之爭對工程溝通的實際影響">REST 語意學之爭</a> 與 <a href="/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 復興</a>（pragmatic 派立場、皆已引用）。反例。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.ben-morris.com/pragmatic-rest-apis-without-hypermedia-and-hateoas/">Pragmatic REST APIs without hypermedia and HATEOAS（Ben Morris）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C9 twobithistory：被挪用的 REST 論文（史觀對照）</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-twobithistory-misappropriation/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/rest-twobithistory-misappropriation/</guid><description>&lt;p>這個案例的核心責任是給「語意學之爭」提供兩派之外的第三方史觀。二手來源、引用時標明。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>考據指出：Fielding 論文寫於 2000、談的是 HTTP/1.1 的設計理據而非 API 建構（當時 web API 尚不存在）；業界在棄 SOAP 時需要學術正當性、把 pragmatic HTTP 用法掛上 REST 名字；Rails 2007 移除 SOAP 支援是轉折符號。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>史觀的教學價值是中立定調：兩派都沒有冤枉對方 — 名字確實被挪用、但挪用後的東西自成合理架構。適合當「REST 語意學之爭」章節收尾的第三方視角、避免章節被迫選邊。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/rest-semantics-dispute/" data-link-title="REST 語意學之爭：一個詞的定義權爭奪" data-link-desc="Fielding 原義、業界 JSON-over-HTTP 慣行、第三方史觀三方的完整論證 — 以及這場命名之爭對工程溝通的實際影響">REST 語意學之爭&lt;/a>（收尾史觀段、已引用）。二手來源、標明性質後使用。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://twobithistory.org/2020/06/28/rest.html">Roy Fielding&amp;rsquo;s Misappropriated REST Dissertation（Two-Bit History、2020）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是給「語意學之爭」提供兩派之外的第三方史觀。二手來源、引用時標明。</p>
<h2 id="觀察">觀察</h2>
<p>考據指出：Fielding 論文寫於 2000、談的是 HTTP/1.1 的設計理據而非 API 建構（當時 web API 尚不存在）；業界在棄 SOAP 時需要學術正當性、把 pragmatic HTTP 用法掛上 REST 名字；Rails 2007 移除 SOAP 支援是轉折符號。</p>
<h2 id="判讀">判讀</h2>
<p>史觀的教學價值是中立定調：兩派都沒有冤枉對方 — 名字確實被挪用、但挪用後的東西自成合理架構。適合當「REST 語意學之爭」章節收尾的第三方視角、避免章節被迫選邊。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/rest/rest-semantics-dispute/" data-link-title="REST 語意學之爭：一個詞的定義權爭奪" data-link-desc="Fielding 原義、業界 JSON-over-HTTP 慣行、第三方史觀三方的完整論證 — 以及這場命名之爭對工程溝通的實際影響">REST 語意學之爭</a>（收尾史觀段、已引用）。二手來源、標明性質後使用。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://twobithistory.org/2020/06/28/rest.html">Roy Fielding&rsquo;s Misappropriated REST Dissertation（Two-Bit History、2020）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C10 Stripe：日期滾動版本與 version change module</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-stripe-rolling-date-versions/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-stripe-rolling-date-versions/</guid><description>&lt;p>這個案例的核心責任是說明 date-based rolling versioning 的機制與成本結構。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Stripe 用日期型滾動版本（如 &lt;code>2017-05-24&lt;/code>）、帳號首次呼叫 API 時自動 pin 住當時版本、可用 &lt;code>Stripe-Version&lt;/code> header 覆寫。內部用 version change module 封裝每個 breaking change、response 依時間反向流過模組鏈、轉換成使用者 pin 的版本形狀。截至 2017 年累積約 100 個 backwards-incompatible 升級、維持與 2011 年以來每一版相容。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>把「相容性」從 API 路由層（/v1、/v2）搬進轉換層、breaking change 的成本由服務端一次吸收、而非攤到所有 client。宣示性的變更定義讓 changelog 與版本感知文件可自動生成 — 版本策略是基礎設施投資、不是命名慣例。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.5 版本策略與 deprecation（anchor、機制主展開）、11.1 成本分配段交叉、版本策略爭論文章。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://stripe.com/blog/api-versioning">APIs as infrastructure: future-proofing Stripe with versioning（Stripe blog、Brandur Leach、2017）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是說明 date-based rolling versioning 的機制與成本結構。</p>
<h2 id="觀察">觀察</h2>
<p>Stripe 用日期型滾動版本（如 <code>2017-05-24</code>）、帳號首次呼叫 API 時自動 pin 住當時版本、可用 <code>Stripe-Version</code> header 覆寫。內部用 version change module 封裝每個 breaking change、response 依時間反向流過模組鏈、轉換成使用者 pin 的版本形狀。截至 2017 年累積約 100 個 backwards-incompatible 升級、維持與 2011 年以來每一版相容。</p>
<h2 id="判讀">判讀</h2>
<p>把「相容性」從 API 路由層（/v1、/v2）搬進轉換層、breaking change 的成本由服務端一次吸收、而非攤到所有 client。宣示性的變更定義讓 changelog 與版本感知文件可自動生成 — 版本策略是基礎設施投資、不是命名慣例。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.5 版本策略與 deprecation（anchor、機制主展開）、11.1 成本分配段交叉、版本策略爭論文章。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://stripe.com/blog/api-versioning">APIs as infrastructure: future-proofing Stripe with versioning（Stripe blog、Brandur Leach、2017）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C11 Stripe 現行方案：具名 major release 與相容變更清單</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-stripe-named-major-releases/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-stripe-named-major-releases/</guid><description>&lt;p>這個案例的核心責任是記錄 Stripe 版本策略的現行切片、跟 2017 blog 構成演進對照。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>現行 Stripe docs 描述的方案已從純日期版演進：major release 具名（如 Acacia）、含 backward-incompatible 變更；月度 release 只含相容變更、沿用上一個 major 名。docs 明列「什麼算 backwards-compatible」：新增資源、新增 optional 參數、新增 response property、property 順序改變、opaque string（object ID、可到 255 字元）長度格式改變、新增 event type。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>同一家公司的版本策略會隨規模演進 — 2017 blog（C10）與現行 docs 是兩個時間切片、引用時要標時點。「相容變更清單」是變更紀律章的直接教材：把 client 不可依賴的介面性質（ID 長度、欄位順序）明文化、等於劃出契約邊界。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.6 向後相容的變更紀律（清單主展開）、11.5 版本策略、11.1 契約劃界段交叉。與 C10 同 cluster。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://docs.stripe.com/upgrades">Stripe API upgrades（Stripe docs）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄 Stripe 版本策略的現行切片、跟 2017 blog 構成演進對照。</p>
<h2 id="觀察">觀察</h2>
<p>現行 Stripe docs 描述的方案已從純日期版演進：major release 具名（如 Acacia）、含 backward-incompatible 變更；月度 release 只含相容變更、沿用上一個 major 名。docs 明列「什麼算 backwards-compatible」：新增資源、新增 optional 參數、新增 response property、property 順序改變、opaque string（object ID、可到 255 字元）長度格式改變、新增 event type。</p>
<h2 id="判讀">判讀</h2>
<p>同一家公司的版本策略會隨規模演進 — 2017 blog（C10）與現行 docs 是兩個時間切片、引用時要標時點。「相容變更清單」是變更紀律章的直接教材：把 client 不可依賴的介面性質（ID 長度、欄位順序）明文化、等於劃出契約邊界。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.6 向後相容的變更紀律（清單主展開）、11.5 版本策略、11.1 契約劃界段交叉。與 C10 同 cluster。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://docs.stripe.com/upgrades">Stripe API upgrades（Stripe docs）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C12 GitHub：REST API calendar versioning 與 24 個月支援承諾</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-github-calendar-versioning/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-github-calendar-versioning/</guid><description>&lt;p>這個案例的核心責任是記錄 date-based versioning 在 Stripe 之後被第二個大平台採納的決策與承諾結構。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>GitHub 2022 年為 REST API 引入日期命名版本（如 &lt;code>2022-11-28&lt;/code>）、client 逐請求用 &lt;code>X-GitHub-Api-Version&lt;/code> header 選版、承諾新版釋出後舊版至少支援 24 個月。範圍只涵蓋 REST API、GraphQL 與 webhooks 除外。公告明講理由：「不能也不期待 integrator 隨我們調整 API 而不斷更新整合」。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>「最低支援窗口」把 deprecation 成本從隱性承諾變成 SLA 式的明文契約。header 選版（而非 URL 路徑）代表版本被視為內容協商、不是資源身分 — 直接對接版本策略爭論文章的 URI vs header 流派分界。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.5 版本策略與 deprecation（anchor）、版本策略爭論文章。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.blog/2022-11-28-to-infinity-and-beyond-enabling-the-future-of-githubs-rest-api-with-api-versioning/">To infinity and beyond: enabling the future of GitHub&amp;rsquo;s REST API with API versioning（GitHub blog、2022）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄 date-based versioning 在 Stripe 之後被第二個大平台採納的決策與承諾結構。</p>
<h2 id="觀察">觀察</h2>
<p>GitHub 2022 年為 REST API 引入日期命名版本（如 <code>2022-11-28</code>）、client 逐請求用 <code>X-GitHub-Api-Version</code> header 選版、承諾新版釋出後舊版至少支援 24 個月。範圍只涵蓋 REST API、GraphQL 與 webhooks 除外。公告明講理由：「不能也不期待 integrator 隨我們調整 API 而不斷更新整合」。</p>
<h2 id="判讀">判讀</h2>
<p>「最低支援窗口」把 deprecation 成本從隱性承諾變成 SLA 式的明文契約。header 選版（而非 URL 路徑）代表版本被視為內容協商、不是資源身分 — 直接對接版本策略爭論文章的 URI vs header 流派分界。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.5 版本策略與 deprecation（anchor）、版本策略爭論文章。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://github.blog/2022-11-28-to-infinity-and-beyond-enabling-the-future-of-githubs-rest-api-with-api-versioning/">To infinity and beyond: enabling the future of GitHub&rsquo;s REST API with API versioning（GitHub blog、2022）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C13 GitHub：密碼認證廢止的 brownout 執行</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-github-password-auth-brownout/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-github-password-auth-brownout/</guid><description>&lt;p>這個案例的核心責任是說明 deprecation 的執行機制設計：brownout 作為遷移警報。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>GitHub 2020-12 宣布 2021-08-13 起 Git 操作停用密碼認證、強制 token。執行前在 2021-06-30 與 07-28 兩個 UTC 時窗做 brownout（暫時停用再恢復）、讓沒讀公告的使用者在低風險時窗先撞牆。已開 2FA 者、GHES、GitHub App 不受影響。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>brownout 承擔的角色是「email 與 blog 公告觸及不到的長尾 client、只有短暫真實故障叫得醒」。宣告日到強制日約 8 個月、且提前明列豁免族群 — 遷移窗口設計包含「誰不用動」的邊界宣告、跟「何時動」同等重要。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.5 版本策略與 deprecation、11.6 向後相容的變更紀律。與 C12 同公司 cluster。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/">Token authentication requirements for Git operations（GitHub blog、2020）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是說明 deprecation 的執行機制設計：brownout 作為遷移警報。</p>
<h2 id="觀察">觀察</h2>
<p>GitHub 2020-12 宣布 2021-08-13 起 Git 操作停用密碼認證、強制 token。執行前在 2021-06-30 與 07-28 兩個 UTC 時窗做 brownout（暫時停用再恢復）、讓沒讀公告的使用者在低風險時窗先撞牆。已開 2FA 者、GHES、GitHub App 不受影響。</p>
<h2 id="判讀">判讀</h2>
<p>brownout 承擔的角色是「email 與 blog 公告觸及不到的長尾 client、只有短暫真實故障叫得醒」。宣告日到強制日約 8 個月、且提前明列豁免族群 — 遷移窗口設計包含「誰不用動」的邊界宣告、跟「何時動」同等重要。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.5 版本策略與 deprecation、11.6 向後相容的變更紀律。與 C12 同公司 cluster。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/">Token authentication requirements for Git operations（GitHub blog、2020）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C14 Fielding：對 API 版本化的建議是「別做」</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-fielding-no-versioning/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-fielding-no-versioning/</guid><description>&lt;p>這個案例的核心責任是提供 no-versioning 流派的理論錨點。刊載平台是 InfoQ（第三方媒體）、內容為 Fielding 本人一手陳述。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Fielding 在 2014 年訪談明言對 &lt;code>/v1/&lt;/code> 式介面版本化的建議是「DON&amp;rsquo;T」：版本化逼 client 要嘛跟著重佈署、要嘛讓舊版成為「permanent lead weight」。他主張 hypermedia as the engine of application state 是 REST 的約束而非選配、控制項應在執行期動態習得、並稱「Versioning interface names only manages change for the API owner&amp;rsquo;s sake」。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>no-versioning 立場的前提是 client 動態習得控制項 — 這個前提在大多數 JSON-over-HTTP API 不成立、正是 Stripe / GitHub 實務路線與學院立場分歧的根源。爭論文章用此案例呈現張力、而非判定對錯。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>版本策略爭論文章（no-versioning 派錨點）、&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/versioning-and-deprecation/" data-link-title="11.5 版本策略與 deprecation" data-link-desc="版本方案怎麼選（URI/header/date-based）、支援窗口怎麼承諾、舊版怎麼安全退場 — 承諾分期與回收的操作設計">11.5 版本策略&lt;/a>（已引用）、&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/rest-semantics-dispute/" data-link-title="REST 語意學之爭：一個詞的定義權爭奪" data-link-desc="Fielding 原義、業界 JSON-over-HTTP 慣行、第三方史觀三方的完整論證 — 以及這場命名之爭對工程溝通的實際影響">REST 語意學之爭&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 復興&lt;/a>（皆已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.infoq.com/articles/roy-fielding-on-versioning/">Roy Fielding on Versioning, Hypermedia, and REST（InfoQ 訪談、Mike Amundsen 採訪、2014）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供 no-versioning 流派的理論錨點。刊載平台是 InfoQ（第三方媒體）、內容為 Fielding 本人一手陳述。</p>
<h2 id="觀察">觀察</h2>
<p>Fielding 在 2014 年訪談明言對 <code>/v1/</code> 式介面版本化的建議是「DON&rsquo;T」：版本化逼 client 要嘛跟著重佈署、要嘛讓舊版成為「permanent lead weight」。他主張 hypermedia as the engine of application state 是 REST 的約束而非選配、控制項應在執行期動態習得、並稱「Versioning interface names only manages change for the API owner&rsquo;s sake」。</p>
<h2 id="判讀">判讀</h2>
<p>no-versioning 立場的前提是 client 動態習得控制項 — 這個前提在大多數 JSON-over-HTTP API 不成立、正是 Stripe / GitHub 實務路線與學院立場分歧的根源。爭論文章用此案例呈現張力、而非判定對錯。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>版本策略爭論文章（no-versioning 派錨點）、<a href="/blog/backend/11-api-design/versioning-and-deprecation/" data-link-title="11.5 版本策略與 deprecation" data-link-desc="版本方案怎麼選（URI/header/date-based）、支援窗口怎麼承諾、舊版怎麼安全退場 — 承諾分期與回收的操作設計">11.5 版本策略</a>（已引用）、<a href="/blog/backend/11-api-design/styles/rest/rest-semantics-dispute/" data-link-title="REST 語意學之爭：一個詞的定義權爭奪" data-link-desc="Fielding 原義、業界 JSON-over-HTTP 慣行、第三方史觀三方的完整論證 — 以及這場命名之爭對工程溝通的實際影響">REST 語意學之爭</a> 與 <a href="/blog/backend/11-api-design/styles/rest/hypermedia-hateoas-revival/" data-link-title="Hypermedia 與 HATEOAS 復興" data-link-desc="復興派的論證本體：uniform client 前提、語意漂移史、格式標準化的失敗現實、反方的收益假設拆解 — 與 hypermedia 的適用邊界">Hypermedia 復興</a>（皆已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.infoq.com/articles/roy-fielding-on-versioning/">Roy Fielding on Versioning, Hypermedia, and REST（InfoQ 訪談、Mike Amundsen 採訪、2014）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C15 RFC 8594 Sunset header：退場宣告的機器可讀層</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-sunset-header-rfc8594/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-sunset-header-rfc8594/</guid><description>&lt;p>這個案例的核心責任是提供 deprecation 通訊的機器可讀層標準與它的誠實邊界。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Sunset header 帶 HTTP-date、宣告資源預期不可用的時點；spec 明確區分 deprecation 兩階段 — header 只對應第二階段（真的下線）、不用於「不再推薦」。另定義 &lt;code>sunset&lt;/code> link relation 指向退場政策與遷移文件。定位是 client hint、不是保證。RFC 地位為 Informational、非 Standards Track（2019、Erik Wilde）。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>公告與 email 觸及人、header 觸及程式 — 這是 Sunset header 承擔的縫隙。教材引用時要標明它是「約定嘗試」而非普及標準：Slack 與 GitHub 的實務分別用 in-band response warning 與 brownout、沒等這個 spec。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.5 版本策略與 deprecation、11.6 向後相容的變更紀律。邊緣（spec 補充）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.rfc-editor.org/rfc/rfc8594.html">RFC 8594: The Sunset HTTP Header Field（IETF、Informational、2019）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供 deprecation 通訊的機器可讀層標準與它的誠實邊界。</p>
<h2 id="觀察">觀察</h2>
<p>Sunset header 帶 HTTP-date、宣告資源預期不可用的時點；spec 明確區分 deprecation 兩階段 — header 只對應第二階段（真的下線）、不用於「不再推薦」。另定義 <code>sunset</code> link relation 指向退場政策與遷移文件。定位是 client hint、不是保證。RFC 地位為 Informational、非 Standards Track（2019、Erik Wilde）。</p>
<h2 id="判讀">判讀</h2>
<p>公告與 email 觸及人、header 觸及程式 — 這是 Sunset header 承擔的縫隙。教材引用時要標明它是「約定嘗試」而非普及標準：Slack 與 GitHub 的實務分別用 in-band response warning 與 brownout、沒等這個 spec。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.5 版本策略與 deprecation、11.6 向後相容的變更紀律。邊緣（spec 補充）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.rfc-editor.org/rfc/rfc8594.html">RFC 8594: The Sunset HTTP Header Field（IETF、Informational、2019）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C16 Slack：四族 API 分階段收斂到 Conversations API</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-slack-conversations-api-sunset/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-slack-conversations-api-sunset/</guid><description>&lt;p>這個案例的核心責任是提供 deprecation 分階段執行的完整時序範例。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Slack 2020-01-07 宣告 &lt;code>channels.*&lt;/code> / &lt;code>groups.*&lt;/code> / &lt;code>im.*&lt;/code> / &lt;code>mpim.*&lt;/code> 四族方法全數 deprecated、由 &lt;code>conversations.*&lt;/code> 取代；2020-06-10 起新建 app 直接拿不到舊方法、2021-02-24 全面停用。過渡期呼叫舊方法會在 response 收到 &lt;code>method_deprecated&lt;/code> warning、附解法提示與退場日期。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>三個日期各承擔一種風險：先掐斷新增量（新 app 禁用）、再處理存量、最後硬停 — 13 個月分階段收斂。in-band warning（response 內帶 deprecation 訊號）是比 Sunset header 更常見的實務做法、因為它出現在開發者一定會看的地方。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.5 版本策略與 deprecation（anchor）、11.6 向後相容的變更紀律。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://docs.slack.dev/changelog/2020-01-deprecating-antecedents-to-the-conversations-api">Deprecating antecedents to the Conversations API（Slack changelog、2020）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供 deprecation 分階段執行的完整時序範例。</p>
<h2 id="觀察">觀察</h2>
<p>Slack 2020-01-07 宣告 <code>channels.*</code> / <code>groups.*</code> / <code>im.*</code> / <code>mpim.*</code> 四族方法全數 deprecated、由 <code>conversations.*</code> 取代；2020-06-10 起新建 app 直接拿不到舊方法、2021-02-24 全面停用。過渡期呼叫舊方法會在 response 收到 <code>method_deprecated</code> warning、附解法提示與退場日期。</p>
<h2 id="判讀">判讀</h2>
<p>三個日期各承擔一種風險：先掐斷新增量（新 app 禁用）、再處理存量、最後硬停 — 13 個月分階段收斂。in-band warning（response 內帶 deprecation 訊號）是比 Sunset header 更常見的實務做法、因為它出現在開發者一定會看的地方。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.5 版本策略與 deprecation（anchor）、11.6 向後相容的變更紀律。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://docs.slack.dev/changelog/2020-01-deprecating-antecedents-to-the-conversations-api">Deprecating antecedents to the Conversations API（Slack changelog、2020）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C17 Facebook Graph API v1.0 退場：靜默語意切換（反例）</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-facebook-graph-v1-forced-upgrade/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/versioning-facebook-graph-v1-forced-upgrade/</guid><description>&lt;p>這個案例的核心責任是提供 deprecation 執行的反例：靜默語意切換比明確錯誤更危險。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Facebook 2014 年為 Graph API 引入版本化、給 v1.0 一年遷移期（官方版本表：v1.0 2010-04-21 到 2015-04-30）；到期後未遷移的請求被靜默改以 v2.0 語意處理、而非關閉。v2.0 移除了 friends 資料等大範圍權限。未遷移 app 的行為直接改變、拿不到明確錯誤。損壞影響的描述依賴二手來源（Amazon Appstore 對開發者的警告轉述）、事實句以官方版本表可支撐的範圍為準。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>「到期後語意靜默切換」的危險在 client 不會炸在認證層、而是拿到形狀不同的資料默默壞掉 — 明確錯誤反而是對消費者更友善的失敗模式。它同時是「平台從無版本到版本化」的轉折案例：v1.0 活了近 5 年沒有契約、之後每版才有明文窗口。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.1 違約模式段（主展開、反例）、11.5 到期行為與 11.6 邊界條款交叉、版本策略爭論文章。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://developers.facebook.com/docs/graph-api/changelog/versions/">Graph API version schedule（Meta for Developers、官方版本表）&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.amazon.com/blogs/post/TxTORRXOM8UG9G/Announcement-Facebook-Deprecates-API-v1-0-on-April-30th">Facebook Deprecates API v1.0 on April 30th（Amazon developer blog、二手轉述）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供 deprecation 執行的反例：靜默語意切換比明確錯誤更危險。</p>
<h2 id="觀察">觀察</h2>
<p>Facebook 2014 年為 Graph API 引入版本化、給 v1.0 一年遷移期（官方版本表：v1.0 2010-04-21 到 2015-04-30）；到期後未遷移的請求被靜默改以 v2.0 語意處理、而非關閉。v2.0 移除了 friends 資料等大範圍權限。未遷移 app 的行為直接改變、拿不到明確錯誤。損壞影響的描述依賴二手來源（Amazon Appstore 對開發者的警告轉述）、事實句以官方版本表可支撐的範圍為準。</p>
<h2 id="判讀">判讀</h2>
<p>「到期後語意靜默切換」的危險在 client 不會炸在認證層、而是拿到形狀不同的資料默默壞掉 — 明確錯誤反而是對消費者更友善的失敗模式。它同時是「平台從無版本到版本化」的轉折案例：v1.0 活了近 5 年沒有契約、之後每版才有明文窗口。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.1 違約模式段（主展開、反例）、11.5 到期行為與 11.6 邊界條款交叉、版本策略爭論文章。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://developers.facebook.com/docs/graph-api/changelog/versions/">Graph API version schedule（Meta for Developers、官方版本表）</a></li>
<li><a href="https://developer.amazon.com/blogs/post/TxTORRXOM8UG9G/Announcement-Facebook-Deprecates-API-v1-0-on-April-30th">Facebook Deprecates API v1.0 on April 30th（Amazon developer blog、二手轉述）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C18 GitHub：採用 GraphQL 的可量化動機</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-github-adoption/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-github-adoption/</guid><description>&lt;p>這個案例的核心責任是記錄大平台採用 GraphQL 的可量化動機。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>GitHub 2016 年公開 GraphQL API 時明言：既有 REST API 負責超過 60% 的資料庫層請求、且同時「送太多資料、又缺消費者需要的資料」（over-fetching 與 under-fetching 並存）。技術棧為 graphql-ruby 加 Shopify 的 graphql-batch、公告前已在 production 運行、前後端用 Relay 協作。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>採用動機是可量化的基礎設施成本（DB 層負載）、不只是 DX 敘事 — 這讓它成為「什麼規模的 over-fetching 痛才值得換風格」的錨點案例。graphql-batch 出現在 day-one 技術棧、顯示 N+1 是第一天就要面對的問題、不是後期優化。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退&lt;/a>（anchor、已引用）、11.2 風格選型交叉。GitHub cluster 之一（C18-C20）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.blog/engineering/the-github-graphql-api/">The GitHub GraphQL API（GitHub engineering blog、2016）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄大平台採用 GraphQL 的可量化動機。</p>
<h2 id="觀察">觀察</h2>
<p>GitHub 2016 年公開 GraphQL API 時明言：既有 REST API 負責超過 60% 的資料庫層請求、且同時「送太多資料、又缺消費者需要的資料」（over-fetching 與 under-fetching 並存）。技術棧為 graphql-ruby 加 Shopify 的 graphql-batch、公告前已在 production 運行、前後端用 Relay 協作。</p>
<h2 id="判讀">判讀</h2>
<p>採用動機是可量化的基礎設施成本（DB 層負載）、不只是 DX 敘事 — 這讓它成為「什麼規模的 over-fetching 痛才值得換風格」的錨點案例。graphql-batch 出現在 day-one 技術棧、顯示 N+1 是第一天就要面對的問題、不是後期優化。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退</a>（anchor、已引用）、11.2 風格選型交叉。GitHub cluster 之一（C18-C20）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://github.blog/engineering/the-github-graphql-api/">The GitHub GraphQL API（GitHub engineering blog、2016）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C19 GitHub：GraphQL point system 成本計點限流</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-github-cost-rate-limiting/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-github-cost-rate-limiting/</guid><description>&lt;p>這個案例的核心責任是說明 GraphQL 對傳統限流模型的衝擊與平台的應對設計。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>GitHub GraphQL API 依 connection 展開的請求數對每個 query 計 point（總請求數除以 100、最低 1 點）、user 每小時 5,000 點、Enterprise 10,000 點；另有 500,000 node 上限與 &lt;code>first&lt;/code> / &lt;code>last&lt;/code> 參數 1-100 的限制；client 可事後查 &lt;code>rateLimit.cost&lt;/code>、也可事前預估。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>一個 request 的成本在 GraphQL 下不再是常數 — per-request rate limiting 的假設被打破、平台被迫發明成本模型。node 上限與分頁參數上限是防執行爆炸的靜態防線、動靜兩層並存本身就是教學重點。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-execution-cost-security/" data-link-title="GraphQL 執行成本與攻擊面" data-link-desc="resolver 執行模型讓請求成本不再是常數 — N&amp;#43;1 的基礎設施化、成本計點限流、introspection 偵察、persisted queries 的收斂路線">執行成本與攻擊面&lt;/a>（機制主寫、已引用）、&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/external-traffic-semantics/" data-link-title="11.9 對外流量語意" data-link-desc="rate limit 對消費者承諾什麼、429 與 Retry-After 怎麼設計、配額 header 該不該信 — 限流作為契約的語意設計">11.9 對外流量語意&lt;/a>（現象層、已引用）。GitHub cluster 之一。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://docs.github.com/en/graphql/overview/rate-limits-and-node-limits-for-the-graphql-api">Rate limits and node limits for the GraphQL API（GitHub docs）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是說明 GraphQL 對傳統限流模型的衝擊與平台的應對設計。</p>
<h2 id="觀察">觀察</h2>
<p>GitHub GraphQL API 依 connection 展開的請求數對每個 query 計 point（總請求數除以 100、最低 1 點）、user 每小時 5,000 點、Enterprise 10,000 點；另有 500,000 node 上限與 <code>first</code> / <code>last</code> 參數 1-100 的限制；client 可事後查 <code>rateLimit.cost</code>、也可事前預估。</p>
<h2 id="判讀">判讀</h2>
<p>一個 request 的成本在 GraphQL 下不再是常數 — per-request rate limiting 的假設被打破、平台被迫發明成本模型。node 上限與分頁參數上限是防執行爆炸的靜態防線、動靜兩層並存本身就是教學重點。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/graphql/graphql-execution-cost-security/" data-link-title="GraphQL 執行成本與攻擊面" data-link-desc="resolver 執行模型讓請求成本不再是常數 — N&#43;1 的基礎設施化、成本計點限流、introspection 偵察、persisted queries 的收斂路線">執行成本與攻擊面</a>（機制主寫、已引用）、<a href="/blog/backend/11-api-design/external-traffic-semantics/" data-link-title="11.9 對外流量語意" data-link-desc="rate limit 對消費者承諾什麼、429 與 Retry-After 怎麼設計、配額 header 該不該信 — 限流作為契約的語意設計">11.9 對外流量語意</a>（現象層、已引用）。GitHub cluster 之一。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://docs.github.com/en/graphql/overview/rate-limits-and-node-limits-for-the-graphql-api">Rate limits and node limits for the GraphQL API（GitHub docs）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C20 GitHub：REST 與 GraphQL 雙軌並行的十年穩態</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-github-rest-parallel/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-github-rest-parallel/</guid><description>&lt;p>這個案例的核心責任是記錄大平台採用 GraphQL 後的長期穩態。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>GitHub 官方文件明言「不需要獨佔使用其中一個 API」；GraphQL 建議用於減少請求數與精準取數（mobile、巢狀關聯）、REST 建議給熟悉傳統 HTTP 慣例者；並承認「某功能可能只在其中一個 API 支援」。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>2016 年的採用者（C18）在多年後的穩態是雙軌並行、功能覆蓋不對等 — 這是「大平台採用 GraphQL 的長期終點是共存」的最直接證據、支撐「進退」章的結論框架。跟 Shopify 的 all-in 策略（C21）形成兩個極端。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退&lt;/a>（anchor、已引用）、11.2 風格選型（共存段、已引用）。GitHub cluster 之一。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://docs.github.com/en/rest/about-the-rest-api/comparing-githubs-rest-api-and-graphql-api">Comparing GitHub&amp;rsquo;s REST API and GraphQL API（GitHub docs）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄大平台採用 GraphQL 後的長期穩態。</p>
<h2 id="觀察">觀察</h2>
<p>GitHub 官方文件明言「不需要獨佔使用其中一個 API」；GraphQL 建議用於減少請求數與精準取數（mobile、巢狀關聯）、REST 建議給熟悉傳統 HTTP 慣例者；並承認「某功能可能只在其中一個 API 支援」。</p>
<h2 id="判讀">判讀</h2>
<p>2016 年的採用者（C18）在多年後的穩態是雙軌並行、功能覆蓋不對等 — 這是「大平台採用 GraphQL 的長期終點是共存」的最直接證據、支撐「進退」章的結論框架。跟 Shopify 的 all-in 策略（C21）形成兩個極端。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退</a>（anchor、已引用）、11.2 風格選型（共存段、已引用）。GitHub cluster 之一。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://docs.github.com/en/rest/about-the-rest-api/comparing-githubs-rest-api-and-graphql-api">Comparing GitHub&rsquo;s REST API and GraphQL API（GitHub docs）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C21 Shopify：宣告 GraphQL 為唯一 API、REST 轉 legacy</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-shopify-all-in/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-shopify-all-in/</guid><description>&lt;p>這個案例的核心責任是記錄 GraphQL 採用光譜的另一個極端：平台強制遷移。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Shopify 2024-10 宣告 REST Admin API 標為 legacy、不再開發新功能；2025-04-01 起新上架 app 必須只用 GraphQL；配套措施包含 rate limit 加倍、connection query 成本降 75%；部分新能力（2,000 product variants、Metaobjects）只在 GraphQL 提供。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>跟 GitHub 的共存路線（C20）相反 — 用「新功能只上 GraphQL」製造遷移壓力。判準案例：「平台對生態系有強制力時才可能 all-in」。成本降 75% 的配套也反向印證 cost-based limiting 是 GraphQL 採用的隱含稅。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退&lt;/a>（anchor、已引用）、11.2 風格選型（共存段、已引用）。Shopify cluster（與 C24 graphql-batch 相關）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.shopify.com/partners/blog/all-in-on-graphql">All in on GraphQL（Shopify partners blog、2024）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄 GraphQL 採用光譜的另一個極端：平台強制遷移。</p>
<h2 id="觀察">觀察</h2>
<p>Shopify 2024-10 宣告 REST Admin API 標為 legacy、不再開發新功能；2025-04-01 起新上架 app 必須只用 GraphQL；配套措施包含 rate limit 加倍、connection query 成本降 75%；部分新能力（2,000 product variants、Metaobjects）只在 GraphQL 提供。</p>
<h2 id="判讀">判讀</h2>
<p>跟 GitHub 的共存路線（C20）相反 — 用「新功能只上 GraphQL」製造遷移壓力。判準案例：「平台對生態系有強制力時才可能 all-in」。成本降 75% 的配套也反向印證 cost-based limiting 是 GraphQL 採用的隱含稅。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退</a>（anchor、已引用）、11.2 風格選型（共存段、已引用）。Shopify cluster（與 C24 graphql-batch 相關）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.shopify.com/partners/blog/all-in-on-graphql">All in on GraphQL（Shopify partners blog、2024）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C22 Matt Bessey：六年 GraphQL 老手的撤退清單（反例）</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-bessey-retreat/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-bessey-retreat/</guid><description>&lt;p>這個案例的核心責任是提供 GraphQL 撤退論證中最完整的執行期代價清單。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>作者（六年 GraphQL 使用經驗）列舉：每個 field 都要各自做授權檢查；128 bytes 的惡意查詢可耗 10 秒 CPU；畸形 directives 造成 2,000 倍記憶體放大；N+1 迫使處處防禦性套 Dataloader、且授權檢查本身也會 N+1。建議控制得了 client 的團隊改用 OpenAPI 3 REST（FastAPI / tsoa / TypeSpec）。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>代價清單全部落在執行期與安全面 — 跟採用文宣的 DX 敘事正交、逐條對應「執行成本與安全」章的大綱（授權下推到 field、成本不可預測、解析層攻擊面）。撤退判準的核心句：「控制得了 client、就不需要 GraphQL 的彈性」。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-execution-cost-security/" data-link-title="GraphQL 執行成本與攻擊面" data-link-desc="resolver 執行模型讓請求成本不再是常數 — N&amp;#43;1 的基礎設施化、成本計點限流、introspection 偵察、persisted queries 的收斂路線">執行成本與攻擊面&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退&lt;/a>（皆已引用）、11.2 風格選型（消費者形狀軸、已引用）。反例。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://bessey.dev/blog/2024/05/24/why-im-over-graphql/">Why, after 6 years, I&amp;rsquo;m over GraphQL（Matt Bessey、2024）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供 GraphQL 撤退論證中最完整的執行期代價清單。</p>
<h2 id="觀察">觀察</h2>
<p>作者（六年 GraphQL 使用經驗）列舉：每個 field 都要各自做授權檢查；128 bytes 的惡意查詢可耗 10 秒 CPU；畸形 directives 造成 2,000 倍記憶體放大；N+1 迫使處處防禦性套 Dataloader、且授權檢查本身也會 N+1。建議控制得了 client 的團隊改用 OpenAPI 3 REST（FastAPI / tsoa / TypeSpec）。</p>
<h2 id="判讀">判讀</h2>
<p>代價清單全部落在執行期與安全面 — 跟採用文宣的 DX 敘事正交、逐條對應「執行成本與安全」章的大綱（授權下推到 field、成本不可預測、解析層攻擊面）。撤退判準的核心句：「控制得了 client、就不需要 GraphQL 的彈性」。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/graphql/graphql-execution-cost-security/" data-link-title="GraphQL 執行成本與攻擊面" data-link-desc="resolver 執行模型讓請求成本不再是常數 — N&#43;1 的基礎設施化、成本計點限流、introspection 偵察、persisted queries 的收斂路線">執行成本與攻擊面</a> 與 <a href="/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退</a>（皆已引用）、11.2 風格選型（消費者形狀軸、已引用）。反例。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://bessey.dev/blog/2024/05/24/why-im-over-graphql/">Why, after 6 years, I&rsquo;m over GraphQL（Matt Bessey、2024）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C23 Echobind：從 GraphQL 撤到 tRPC 的量化帳（反例）</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-echobind-trpc-retreat/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-echobind-trpc-retreat/</guid><description>&lt;p>這個案例的核心責任是劃出 GraphQL 與 tRPC 各自的適用邊界、單一 TypeScript 團隊場景的量化對照。跨主題案例：GraphQL 撤退面與 tRPC 採用面共用本檔。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>痛點是 double declaration：同一資料形狀要在 Prisma / Nexus / GraphQL operations / codegen types / client queries 五層宣告；三層 codegen 產出 8,200 行型別檔、常需重啟 VSCode language server；GraphQL 依賴 81.2kb、tRPC 23.7kb；Apollo normalized cache 在 mutation 後常態性要手動 &lt;code>refetchQueries&lt;/code>。遷移後淨減 1,608 行。文章同時明列 tRPC 前提：「server 用 TypeScript 且與 client 共置」、無法有效服務公開第三方 API。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>撤退動因是「單一團隊同時擁有前後端」時、GraphQL 的 schema 中介層變成純開銷 — schema 作為跨團隊 / 跨 client 契約才有價值、同構 TypeScript 單團隊是反指標。作者自列的 tRPC 邊界（公開 API 不適用）可直接引用、避免被讀成萬用推薦。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退&lt;/a>（適用邊界段、反例、已引用）、styles/rpc-revival/「tRPC 與型別共享」（anchor、backlog）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://echobind.com/post/why-we-ditched-graphql-for-trpc">Why we ditched GraphQL for tRPC（Echobind blog、2022）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是劃出 GraphQL 與 tRPC 各自的適用邊界、單一 TypeScript 團隊場景的量化對照。跨主題案例：GraphQL 撤退面與 tRPC 採用面共用本檔。</p>
<h2 id="觀察">觀察</h2>
<p>痛點是 double declaration：同一資料形狀要在 Prisma / Nexus / GraphQL operations / codegen types / client queries 五層宣告；三層 codegen 產出 8,200 行型別檔、常需重啟 VSCode language server；GraphQL 依賴 81.2kb、tRPC 23.7kb；Apollo normalized cache 在 mutation 後常態性要手動 <code>refetchQueries</code>。遷移後淨減 1,608 行。文章同時明列 tRPC 前提：「server 用 TypeScript 且與 client 共置」、無法有效服務公開第三方 API。</p>
<h2 id="判讀">判讀</h2>
<p>撤退動因是「單一團隊同時擁有前後端」時、GraphQL 的 schema 中介層變成純開銷 — schema 作為跨團隊 / 跨 client 契約才有價值、同構 TypeScript 單團隊是反指標。作者自列的 tRPC 邊界（公開 API 不適用）可直接引用、避免被讀成萬用推薦。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退</a>（適用邊界段、反例、已引用）、styles/rpc-revival/「tRPC 與型別共享」（anchor、backlog）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://echobind.com/post/why-we-ditched-graphql-for-trpc">Why we ditched GraphQL for tRPC（Echobind blog、2022）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C24 DataLoader 譜系：N+1 的官方解法變成基礎設施</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-dataloader-n-plus-one/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-dataloader-n-plus-one/</guid><description>&lt;p>這個案例的核心責任是說明 N+1 在 GraphQL 執行模型下的地位轉變與解法譜系。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>DataLoader 把單一執行 frame 內的個別 load 合併成 batch、加 per-request 快取；概念源自 Facebook 2010 年的內部 Loader API、早於 GraphQL 開源、跨語言存在（如 Haskell 的 Haxl）。Shopify 維護的 Ruby 版 graphql-batch（loader pattern + promise）被 GitHub 2016 年採用時直接引入、至 2025-09 仍在發版。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>N+1 不是 GraphQL 發明的問題、但 resolver-per-field 執行模型讓它從偶發變成預設 — 所以官方生態把 batching 做成基礎設施、不是優化技巧。「連 GitHub day-one 都要帶 graphql-batch」是最直接的教學證據。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-execution-cost-security/" data-link-title="GraphQL 執行成本與攻擊面" data-link-desc="resolver 執行模型讓請求成本不再是常數 — N&amp;#43;1 的基礎設施化、成本計點限流、introspection 偵察、persisted queries 的收斂路線">執行成本與攻擊面&lt;/a>（N+1 段、anchor、已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/graphql/dataloader">graphql/dataloader（GraphQL 基金會、GitHub repo）&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/Shopify/graphql-batch">Shopify/graphql-batch（GitHub repo）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是說明 N+1 在 GraphQL 執行模型下的地位轉變與解法譜系。</p>
<h2 id="觀察">觀察</h2>
<p>DataLoader 把單一執行 frame 內的個別 load 合併成 batch、加 per-request 快取；概念源自 Facebook 2010 年的內部 Loader API、早於 GraphQL 開源、跨語言存在（如 Haskell 的 Haxl）。Shopify 維護的 Ruby 版 graphql-batch（loader pattern + promise）被 GitHub 2016 年採用時直接引入、至 2025-09 仍在發版。</p>
<h2 id="判讀">判讀</h2>
<p>N+1 不是 GraphQL 發明的問題、但 resolver-per-field 執行模型讓它從偶發變成預設 — 所以官方生態把 batching 做成基礎設施、不是優化技巧。「連 GitHub day-one 都要帶 graphql-batch」是最直接的教學證據。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/graphql/graphql-execution-cost-security/" data-link-title="GraphQL 執行成本與攻擊面" data-link-desc="resolver 執行模型讓請求成本不再是常數 — N&#43;1 的基礎設施化、成本計點限流、introspection 偵察、persisted queries 的收斂路線">執行成本與攻擊面</a>（N+1 段、anchor、已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://github.com/graphql/dataloader">graphql/dataloader（GraphQL 基金會、GitHub repo）</a></li>
<li><a href="https://github.com/Shopify/graphql-batch">Shopify/graphql-batch（GitHub repo）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C25 HackerOne：introspection 列舉出未授權的 CreateAdminUser</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-introspection-auth-bypass/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-introspection-auth-bypass/</guid><description>&lt;p>這個案例的核心責任是提供「introspection 等於攻擊面偵察工具」的具體實證。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>某電商平台的第三方 banner 服務暴露 GraphQL 端點、introspection 開啟；研究者（J. Francisco Bolivar、2023 HackerOne Ambassador World Cup 期間回報）用 introspection 列舉 schema、發現未加驗證的 &lt;code>CreateAdminUser&lt;/code> mutation、直接取得管理權限、數日內修復。報告結論建議：production 關 introspection、field-level authorization、移除不必要的 mutation。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>REST 世界要靠 fuzzing 才找得到的隱藏端點、GraphQL 用型別系統自己告訴你。教學上與 C22 的 CPU / 記憶體放大並列成兩類攻擊面：資訊暴露與資源耗盡。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-execution-cost-security/" data-link-title="GraphQL 執行成本與攻擊面" data-link-desc="resolver 執行模型讓請求成本不再是常數 — N&amp;#43;1 的基礎設施化、成本計點限流、introspection 偵察、persisted queries 的收斂路線">執行成本與攻擊面&lt;/a>（introspection 段、已引用）。邊緣（安全單點案例）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.hackerone.com/blog/how-graphql-bug-resulted-authentication-bypass">How a GraphQL Bug Resulted in Authentication Bypass（HackerOne blog）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供「introspection 等於攻擊面偵察工具」的具體實證。</p>
<h2 id="觀察">觀察</h2>
<p>某電商平台的第三方 banner 服務暴露 GraphQL 端點、introspection 開啟；研究者（J. Francisco Bolivar、2023 HackerOne Ambassador World Cup 期間回報）用 introspection 列舉 schema、發現未加驗證的 <code>CreateAdminUser</code> mutation、直接取得管理權限、數日內修復。報告結論建議：production 關 introspection、field-level authorization、移除不必要的 mutation。</p>
<h2 id="判讀">判讀</h2>
<p>REST 世界要靠 fuzzing 才找得到的隱藏端點、GraphQL 用型別系統自己告訴你。教學上與 C22 的 CPU / 記憶體放大並列成兩類攻擊面：資訊暴露與資源耗盡。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/graphql/graphql-execution-cost-security/" data-link-title="GraphQL 執行成本與攻擊面" data-link-desc="resolver 執行模型讓請求成本不再是常數 — N&#43;1 的基礎設施化、成本計點限流、introspection 偵察、persisted queries 的收斂路線">執行成本與攻擊面</a>（introspection 段、已引用）。邊緣（安全單點案例）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.hackerone.com/blog/how-graphql-bug-resulted-authentication-bypass">How a GraphQL Bug Resulted in Authentication Bypass（HackerOne blog）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C26 GraphQL 官方：versionless API 與 nullable-by-default</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-versionless-evolution/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-versionless-evolution/</guid><description>&lt;p>這個案例的核心責任是記錄 GraphQL 官方的 versionless 設計哲學與其依賴的紀律。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>官方教學明文：GraphQL 只回傳明確請求的資料、所以新能力可以透過新 type 或既有 type 的新 field 加入而不造成 breaking change；「永遠避免 breaking change、提供 versionless API」被稱為 common practice。同時「type system 中每個 field 預設 nullable」、理由包含後端局部故障與細粒度授權。驗證備註：官方站主頁對 fetcher 回 403、內容以官方 GitHub repo 的 source 檔驗證、對外引用用官方教學頁 URL。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>no-versioning 的實質是把演進成本轉嫁到三個紀律：只加不改、deprecation 標注、nullable 預設 — 版本管理的工作換了位置、沒有消失。nullable-by-default 正是為了讓局部失敗與授權拒絕不炸掉整個 response — 這條因果鏈是 schema 演進篇的骨幹。可與 WunderGraph 批評（C27）對照：versionless 解 schema 相容、解不了組織層的舊 client 支援。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-schema-evolution/" data-link-title="GraphQL Schema 演進：versionless 的紀律代價" data-link-desc="只加不改、deprecation 標注、nullable 預設怎麼共同取代版本號 — 以及每個紀律各自的隱藏帳單">Schema 演進&lt;/a>（anchor、已引用）、11.6 格式層紀律（主引用）、11.2 / 11.5 交叉、版本策略爭論文章。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://graphql.org/learn/schema-design/">Schema design（GraphQL 官方教學）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄 GraphQL 官方的 versionless 設計哲學與其依賴的紀律。</p>
<h2 id="觀察">觀察</h2>
<p>官方教學明文：GraphQL 只回傳明確請求的資料、所以新能力可以透過新 type 或既有 type 的新 field 加入而不造成 breaking change；「永遠避免 breaking change、提供 versionless API」被稱為 common practice。同時「type system 中每個 field 預設 nullable」、理由包含後端局部故障與細粒度授權。驗證備註：官方站主頁對 fetcher 回 403、內容以官方 GitHub repo 的 source 檔驗證、對外引用用官方教學頁 URL。</p>
<h2 id="判讀">判讀</h2>
<p>no-versioning 的實質是把演進成本轉嫁到三個紀律：只加不改、deprecation 標注、nullable 預設 — 版本管理的工作換了位置、沒有消失。nullable-by-default 正是為了讓局部失敗與授權拒絕不炸掉整個 response — 這條因果鏈是 schema 演進篇的骨幹。可與 WunderGraph 批評（C27）對照：versionless 解 schema 相容、解不了組織層的舊 client 支援。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/graphql/graphql-schema-evolution/" data-link-title="GraphQL Schema 演進：versionless 的紀律代價" data-link-desc="只加不改、deprecation 標注、nullable 預設怎麼共同取代版本號 — 以及每個紀律各自的隱藏帳單">Schema 演進</a>（anchor、已引用）、11.6 格式層紀律（主引用）、11.2 / 11.5 交叉、版本策略爭論文章。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://graphql.org/learn/schema-design/">Schema design（GraphQL 官方教學）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C27 WunderGraph：GraphQL 不該直接暴露在公網</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-wundergraph-not-for-internet/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/graphql-wundergraph-not-for-internet/</guid><description>&lt;p>這個案例的核心責任是提供公開 API 進退光譜的中間選項：persisted queries 家族做法。vendor 創辦人立場、引用時標明利益相關。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>兩篇文章的論點：單一 query 可觸發數千次 resolver、middleware 式安全模型失效；introspection 洩漏內部架構；schema traversal 可越權。解法是把 named operations 存在後端、對外轉成 JSON-RPC 式端點、完全不暴露 GraphQL endpoint。後篇補充：versioning 的組織問題 GraphQL 沒解、self-documenting 是迷思、type safety OpenAPI 也有。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>「GraphQL 當 server-side 查詢語言、對外只開 persisted operations」是介於全開與撤退之間的第三條路、公開 API 進退章需要這個中間選項。來源是賣此方案的 vendor、立場要標明；但攻擊面描述與 Bessey（C22）、HackerOne（C25）獨立互證。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-execution-cost-security/" data-link-title="GraphQL 執行成本與攻擊面" data-link-desc="resolver 執行模型讓請求成本不再是常數 — N&amp;#43;1 的基礎設施化、成本計點限流、introspection 偵察、persisted queries 的收斂路線">執行成本與攻擊面&lt;/a>（persisted queries 段、已引用）與 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退&lt;/a>（中間路線段、已引用）。邊緣偏反例。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://wundergraph.com/blog/graphql_is_not_meant_to_be_exposed_over_the_internet">GraphQL is not meant to be exposed over the internet（WunderGraph blog、Jens Neuse）&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://wundergraph.com/blog/why_not_use_graphql">Why not use GraphQL?（WunderGraph blog）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供公開 API 進退光譜的中間選項：persisted queries 家族做法。vendor 創辦人立場、引用時標明利益相關。</p>
<h2 id="觀察">觀察</h2>
<p>兩篇文章的論點：單一 query 可觸發數千次 resolver、middleware 式安全模型失效；introspection 洩漏內部架構；schema traversal 可越權。解法是把 named operations 存在後端、對外轉成 JSON-RPC 式端點、完全不暴露 GraphQL endpoint。後篇補充：versioning 的組織問題 GraphQL 沒解、self-documenting 是迷思、type safety OpenAPI 也有。</p>
<h2 id="判讀">判讀</h2>
<p>「GraphQL 當 server-side 查詢語言、對外只開 persisted operations」是介於全開與撤退之間的第三條路、公開 API 進退章需要這個中間選項。來源是賣此方案的 vendor、立場要標明；但攻擊面描述與 Bessey（C22）、HackerOne（C25）獨立互證。</p>
<h2 id="對應大綱">對應大綱</h2>
<p><a href="/blog/backend/11-api-design/styles/graphql/graphql-execution-cost-security/" data-link-title="GraphQL 執行成本與攻擊面" data-link-desc="resolver 執行模型讓請求成本不再是常數 — N&#43;1 的基礎設施化、成本計點限流、introspection 偵察、persisted queries 的收斂路線">執行成本與攻擊面</a>（persisted queries 段、已引用）與 <a href="/blog/backend/11-api-design/styles/graphql/graphql-public-api-tradeoffs/" data-link-title="公開 API 的 GraphQL 進退" data-link-desc="GitHub 雙軌、Shopify all-in、與撤退案例 — 同一技術不同結局的情境變數、GraphQL 的適用邊界">公開 API 的 GraphQL 進退</a>（中間路線段、已引用）。邊緣偏反例。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://wundergraph.com/blog/graphql_is_not_meant_to_be_exposed_over_the_internet">GraphQL is not meant to be exposed over the internet（WunderGraph blog、Jens Neuse）</a></li>
<li><a href="https://wundergraph.com/blog/why_not_use_graphql">Why not use GraphQL?（WunderGraph blog）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C28 protobuf 官方規範：field number 紀律</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/grpc-protobuf-field-number-discipline/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/grpc-protobuf-field-number-discipline/</guid><description>&lt;p>這個案例的核心責任是提供 proto 演進紀律的規範錨點。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>官方語言規範明文：field number 一旦訊息投入使用即不可變更、因為它就是 wire format 的欄位識別；刪欄位後必須 reserve 該編號、重用編號會使 wire 解碼歧義、後果列舉包括 parse / merge error、PII 洩漏、資料損毀。文件把 schema 變更分三類：wire-unsafe、wire-safe（加欄位、加 enum 值、刪欄位皆安全）、conditionally wire-compatible。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>契約相容性在 protobuf 是編碼格式的數學性質、不是 code review 慣例 —「加法演進 + 編號永不回收」是 protobuf 相對 JSON schema 的核心工程差異、也是所有 breaking-change 工具（C29）存在的前提。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>styles/grpc/「proto 演進紀律」（anchor）、11.6 向後相容的變更紀律交叉。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://protobuf.dev/programming-guides/proto3/">Language Guide (proto 3)（Protocol Buffers 官方文件）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供 proto 演進紀律的規範錨點。</p>
<h2 id="觀察">觀察</h2>
<p>官方語言規範明文：field number 一旦訊息投入使用即不可變更、因為它就是 wire format 的欄位識別；刪欄位後必須 reserve 該編號、重用編號會使 wire 解碼歧義、後果列舉包括 parse / merge error、PII 洩漏、資料損毀。文件把 schema 變更分三類：wire-unsafe、wire-safe（加欄位、加 enum 值、刪欄位皆安全）、conditionally wire-compatible。</p>
<h2 id="判讀">判讀</h2>
<p>契約相容性在 protobuf 是編碼格式的數學性質、不是 code review 慣例 —「加法演進 + 編號永不回收」是 protobuf 相對 JSON schema 的核心工程差異、也是所有 breaking-change 工具（C29）存在的前提。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>styles/grpc/「proto 演進紀律」（anchor）、11.6 向後相容的變更紀律交叉。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://protobuf.dev/programming-guides/proto3/">Language Guide (proto 3)（Protocol Buffers 官方文件）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C29 Buf breaking detection：四級規則對應消費者依賴</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/grpc-buf-breaking-detection/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/grpc-buf-breaking-detection/</guid><description>&lt;p>這個案例的核心責任是說明相容性檢查如何工具化進 CI、以及檢查粒度的選擇邏輯。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>&lt;code>buf breaking&lt;/code> 對比歷史版本 schema、在 merge 前擋下如「Field 1 type int32 改 string」這類變更；規則分四級（FILE、PACKAGE、WIRE_JSON、WIRE、嚴格包含寬鬆）。文件明言「Catching this before merge is the point」、並指出破壞發生在多層 — 改名破壞 generated code、改 type 破壞 wire format — 人工 review 抓不全。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>這是 C28 紀律從「人的自律」升級成 CI gate 的論證。四級規則的核心主張：「選符合消費者實際依賴的等級」— 只走 wire 的消費者用 WIRE、有外部 Go import 的要 PACKAGE。教學重點是「相容性檢查粒度是產品決策、不是工具預設」。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.6 向後相容的變更紀律（工具層 anchor、已引用）、styles/grpc/「proto 演進紀律」、11.10 API 規範治理（linting 進 CI）交叉。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://buf.build/docs/breaking/">Breaking change detection（Buf 官方文件）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是說明相容性檢查如何工具化進 CI、以及檢查粒度的選擇邏輯。</p>
<h2 id="觀察">觀察</h2>
<p><code>buf breaking</code> 對比歷史版本 schema、在 merge 前擋下如「Field 1 type int32 改 string」這類變更；規則分四級（FILE、PACKAGE、WIRE_JSON、WIRE、嚴格包含寬鬆）。文件明言「Catching this before merge is the point」、並指出破壞發生在多層 — 改名破壞 generated code、改 type 破壞 wire format — 人工 review 抓不全。</p>
<h2 id="判讀">判讀</h2>
<p>這是 C28 紀律從「人的自律」升級成 CI gate 的論證。四級規則的核心主張：「選符合消費者實際依賴的等級」— 只走 wire 的消費者用 WIRE、有外部 Go import 的要 PACKAGE。教學重點是「相容性檢查粒度是產品決策、不是工具預設」。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.6 向後相容的變更紀律（工具層 anchor、已引用）、styles/grpc/「proto 演進紀律」、11.10 API 規範治理（linting 進 CI）交叉。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://buf.build/docs/breaking/">Breaking change detection（Buf 官方文件）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C30 Buf Connect 發布文：對 grpc-go 的系統性批評</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/grpc-buf-connect-critique/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/grpc-buf-connect-critique/</guid><description>&lt;p>這個案例的核心責任是提供 gRPC 部署邊界的完整批評清單。Buf 是利益相關方、引用時標明；但 trailers 與瀏覽器不相容是可獨立驗證的協議事實。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>發布文指 grpc-go 有 13 萬行手寫程式碼、近百個設定選項、不用 Go 標準庫而自帶 HTTP/2 實作、導致無法與其他 HTTP 流量共存；gRPC 協議要求端到端 HTTP/2 加 trailers、瀏覽器支援需要翻譯 proxy；不遵守 semver、debug 時連 &lt;code>curl | jq&lt;/code> 都不可行。Connect 的對案：建在 net/http 上、同時支援 gRPC / gRPC-Web / Connect 三協議。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>gRPC 的部署邊界（瀏覽器、proxy、trailers）是風格選型時常被忽略的維度 — 協議能力表不會列「你的 LB 過不過 trailers」。與 C32 的獨立批評互證後、批評點的可信度不依賴 Buf 的立場。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>styles/grpc/「streaming 語意與部署邊界」（anchor）、11.2 風格選型（操作可及性軸、已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://buf.build/blog/connect-a-better-grpc">Connect: a better gRPC（Buf blog）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供 gRPC 部署邊界的完整批評清單。Buf 是利益相關方、引用時標明；但 trailers 與瀏覽器不相容是可獨立驗證的協議事實。</p>
<h2 id="觀察">觀察</h2>
<p>發布文指 grpc-go 有 13 萬行手寫程式碼、近百個設定選項、不用 Go 標準庫而自帶 HTTP/2 實作、導致無法與其他 HTTP 流量共存；gRPC 協議要求端到端 HTTP/2 加 trailers、瀏覽器支援需要翻譯 proxy；不遵守 semver、debug 時連 <code>curl | jq</code> 都不可行。Connect 的對案：建在 net/http 上、同時支援 gRPC / gRPC-Web / Connect 三協議。</p>
<h2 id="判讀">判讀</h2>
<p>gRPC 的部署邊界（瀏覽器、proxy、trailers）是風格選型時常被忽略的維度 — 協議能力表不會列「你的 LB 過不過 trailers」。與 C32 的獨立批評互證後、批評點的可信度不依賴 Buf 的立場。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>styles/grpc/「streaming 語意與部署邊界」（anchor）、11.2 風格選型（操作可及性軸、已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://buf.build/blog/connect-a-better-grpc">Connect: a better gRPC（Buf blog）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C31 Dropbox Courier：百萬 RPS 規模的 gRPC 遷移</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/grpc-dropbox-courier/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/grpc-dropbox-courier/</guid><description>&lt;p>這個案例的核心責任是提供內部 RPC 選型的規模上限案例。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Dropbox 從 HTTP/1.1 加 protobuf 的自製 RPC 遷移到 gRPC、動機是保留既有 protobuf、取得 multiplexing 與雙向 streaming；在 gRPC 上疊了 mTLS 服務身分、per-method 統計、強制 deadline 傳播、LIFO queue 熔斷。踩雷紀錄：大規模重啟時 TLS 握手成本迫使 RSA 2048 換 ECDSA P-256、且 HTTP/1.1 與 gRPC 要拆成不同 server 處理。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>gRPC 在 Dropbox 的價值是「框架層集中加 infra-wide 可靠性」的載體、不是序列化效能本身 — 選型判準應該看組織要不要這一層集中點。「migration 比初始開發久得多」與 TLS 握手踩雷適合當規模判讀訊號。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>styles/grpc/「內部 RPC 的選型位置」（anchor）、11.2 風格選型（操作可及性軸、已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://dropbox.tech/infrastructure/courier-dropbox-migration-to-grpc">Courier: Dropbox migration to gRPC（Dropbox tech blog）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供內部 RPC 選型的規模上限案例。</p>
<h2 id="觀察">觀察</h2>
<p>Dropbox 從 HTTP/1.1 加 protobuf 的自製 RPC 遷移到 gRPC、動機是保留既有 protobuf、取得 multiplexing 與雙向 streaming；在 gRPC 上疊了 mTLS 服務身分、per-method 統計、強制 deadline 傳播、LIFO queue 熔斷。踩雷紀錄：大規模重啟時 TLS 握手成本迫使 RSA 2048 換 ECDSA P-256、且 HTTP/1.1 與 gRPC 要拆成不同 server 處理。</p>
<h2 id="判讀">判讀</h2>
<p>gRPC 在 Dropbox 的價值是「框架層集中加 infra-wide 可靠性」的載體、不是序列化效能本身 — 選型判準應該看組織要不要這一層集中點。「migration 比初始開發久得多」與 TLS 握手踩雷適合當規模判讀訊號。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>styles/grpc/「內部 RPC 的選型位置」（anchor）、11.2 風格選型（操作可及性軸、已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://dropbox.tech/infrastructure/courier-dropbox-migration-to-grpc">Courier: Dropbox migration to gRPC（Dropbox tech blog）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C32 gRPC: The Bad Parts：cURL 測試不過的 API（反例）</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/grpc-kmcd-bad-parts/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/grpc-kmcd-bad-parts/</guid><description>&lt;p>這個案例的核心責任是提供非 vendor 立場的 gRPC 獨立批評、跟 C30 互證。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>批評點：非標準術語（unary RPC）墊高學習曲線、通不過「傳一個 cURL 範例給朋友」測試、瀏覽器無法處理 HTTP trailers 需 gRPC-Web 加 proxy、HTTP/3 採用遲緩、protobuf 要求完整解析整個訊息使大檔處理容易出錯、依賴管理長期無標準。文章同時承認 Buf CLI / ConnectRPC / Postman 支援已改善部分問題。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>「批評 + 承認生態已修補」的平衡結構適合直接當教材敘事骨架。cURL 測試是「debug 可及性」這個選型維度的好判準 — 協議效率表不會告訴你 on-call 時能不能徒手戳一個 endpoint。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>styles/grpc/「內部 RPC 的選型位置」（gRPC 邊界與代價段）、11.2 風格選型（操作可及性軸、已引用）。反例 / 邊緣。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://kmcd.dev/posts/grpc-the-bad-parts/">gRPC: The Bad Parts（Kevin McDonald）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供非 vendor 立場的 gRPC 獨立批評、跟 C30 互證。</p>
<h2 id="觀察">觀察</h2>
<p>批評點：非標準術語（unary RPC）墊高學習曲線、通不過「傳一個 cURL 範例給朋友」測試、瀏覽器無法處理 HTTP trailers 需 gRPC-Web 加 proxy、HTTP/3 採用遲緩、protobuf 要求完整解析整個訊息使大檔處理容易出錯、依賴管理長期無標準。文章同時承認 Buf CLI / ConnectRPC / Postman 支援已改善部分問題。</p>
<h2 id="判讀">判讀</h2>
<p>「批評 + 承認生態已修補」的平衡結構適合直接當教材敘事骨架。cURL 測試是「debug 可及性」這個選型維度的好判準 — 協議效率表不會告訴你 on-call 時能不能徒手戳一個 endpoint。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>styles/grpc/「內部 RPC 的選型位置」（gRPC 邊界與代價段）、11.2 風格選型（操作可及性軸、已引用）。反例 / 邊緣。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://kmcd.dev/posts/grpc-the-bad-parts/">gRPC: The Bad Parts（Kevin McDonald）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C33 tRPC 設計哲學：無 schema 無 codegen 的型別共享</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/rpc-trpc-design-philosophy/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/rpc-trpc-design-philosophy/</guid><description>&lt;p>這個案例的核心責任是記錄 tRPC 官方自述的設計哲學、含它自己承認的前提與代價。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>核心主張：「build &amp;amp; consume fully typesafe APIs without schemas or code generation」、靠 TypeScript 型別推導、無 codegen、無 runtime bloat、無 build pipeline。官方 FAQ 明言前提與代價：脫離 monorepo 就失去 client 與 server 一起運作的保證、替代方案是把 backend 型別發成 private npm package；動態型別輸出做不到（需 TypeScript 尚未支援的 higher-kinded types）；Netflix、Pleo 等在 production 使用。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>tRPC 是「把 API 契約從 IDL 檔搬進型別系統」的極端點 — 換到零 codegen 的 DX、付出語言鎖定（TS-only）與部署形態鎖定（同倉或私有 npm 包）。教學上與 protobuf 對照：兩者都在解契約同步、一個走 schema-first 跨語言、一個走 inference-first 單語言。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>styles/rpc-revival/「tRPC 與型別共享」（anchor、與 C23 Echobind 並用）、11.2 風格選型交叉。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://trpc.io/docs">tRPC docs&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://trpc.io/docs/faq">tRPC FAQ&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄 tRPC 官方自述的設計哲學、含它自己承認的前提與代價。</p>
<h2 id="觀察">觀察</h2>
<p>核心主張：「build &amp; consume fully typesafe APIs without schemas or code generation」、靠 TypeScript 型別推導、無 codegen、無 runtime bloat、無 build pipeline。官方 FAQ 明言前提與代價：脫離 monorepo 就失去 client 與 server 一起運作的保證、替代方案是把 backend 型別發成 private npm package；動態型別輸出做不到（需 TypeScript 尚未支援的 higher-kinded types）；Netflix、Pleo 等在 production 使用。</p>
<h2 id="判讀">判讀</h2>
<p>tRPC 是「把 API 契約從 IDL 檔搬進型別系統」的極端點 — 換到零 codegen 的 DX、付出語言鎖定（TS-only）與部署形態鎖定（同倉或私有 npm 包）。教學上與 protobuf 對照：兩者都在解契約同步、一個走 schema-first 跨語言、一個走 inference-first 單語言。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>styles/rpc-revival/「tRPC 與型別共享」（anchor、與 C23 Echobind 並用）、11.2 風格選型交叉。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://trpc.io/docs">tRPC docs</a></li>
<li><a href="https://trpc.io/docs/faq">tRPC FAQ</a></li>
</ul>
]]></content:encoded></item><item><title>11.C34 JSON-RPC 重生：LSP 與 MCP 都選它當訊息層</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/rpc-jsonrpc-lsp-mcp-revival/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/rpc-jsonrpc-lsp-mcp-revival/</guid><description>&lt;p>這個案例的核心責任是記錄 JSON-RPC 重生場景的共同形狀：兩份現代 spec 的採用事實。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>LSP spec 明文 content part 使用 JSON-RPC 描述 requests / responses / notifications、固定 &lt;code>jsonrpc: &amp;quot;2.0&amp;quot;&lt;/code>、外層自訂 Content-Length header 傳輸。MCP spec 規定所有訊息 MUST follow JSON-RPC 2.0、並在其上收緊（request ID 不可為 null、同 session 不可重用）、transport 支援 stdio 與 HTTP、schema 以 TypeScript 為 source of truth。注意：LSP spec 只陳述採用、未寫選型理由段 — 教材推導理由時要標明是判讀、不是引文。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>重生場景的共同形狀：本地 process 間、雙向、低頻、需要 notification 語意、且生態工具（編輯器 / agent）要求零 codegen 可自省 — 這組條件下 gRPC 的 HTTP/2 加 codegen 成本全是負資產。兩份 spec 都「在 JSON-RPC 上加約束」而非發明新協議、是「選最小夠用訊息層」的教材主軸。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>styles/rpc-revival/「JSON-RPC 的重生場景」（anchor）、11.2 風格選型交叉。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/">Language Server Protocol Specification 3.17（Microsoft）&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://modelcontextprotocol.io/specification/2025-06-18/basic">Model Context Protocol: Base Protocol（spec、2025-06-18）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄 JSON-RPC 重生場景的共同形狀：兩份現代 spec 的採用事實。</p>
<h2 id="觀察">觀察</h2>
<p>LSP spec 明文 content part 使用 JSON-RPC 描述 requests / responses / notifications、固定 <code>jsonrpc: &quot;2.0&quot;</code>、外層自訂 Content-Length header 傳輸。MCP spec 規定所有訊息 MUST follow JSON-RPC 2.0、並在其上收緊（request ID 不可為 null、同 session 不可重用）、transport 支援 stdio 與 HTTP、schema 以 TypeScript 為 source of truth。注意：LSP spec 只陳述採用、未寫選型理由段 — 教材推導理由時要標明是判讀、不是引文。</p>
<h2 id="判讀">判讀</h2>
<p>重生場景的共同形狀：本地 process 間、雙向、低頻、需要 notification 語意、且生態工具（編輯器 / agent）要求零 codegen 可自省 — 這組條件下 gRPC 的 HTTP/2 加 codegen 成本全是負資產。兩份 spec 都「在 JSON-RPC 上加約束」而非發明新協議、是「選最小夠用訊息層」的教材主軸。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>styles/rpc-revival/「JSON-RPC 的重生場景」（anchor）、11.2 風格選型交叉。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/">Language Server Protocol Specification 3.17（Microsoft）</a></li>
<li><a href="https://modelcontextprotocol.io/specification/2025-06-18/basic">Model Context Protocol: Base Protocol（spec、2025-06-18）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C35 RFC 9457：problem+json 標準化錯誤格式</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/error-rfc9457-problem-details/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/error-rfc9457-problem-details/</guid><description>&lt;p>這個案例的核心責任是提供錯誤格式標準化的現行基準（obsoletes RFC 7807）。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>RFC 9457（IETF Proposed Standard、Standards Track、2023-07）定義 &lt;code>application/problem+json&lt;/code>：五個核心成員 &lt;code>type&lt;/code>（URI、預設 &lt;code>about:blank&lt;/code>）、&lt;code>title&lt;/code>、&lt;code>status&lt;/code>、&lt;code>detail&lt;/code>、&lt;code>instance&lt;/code>；允許 extension members 且要求 client 忽略不認識的欄位；建立 IANA common problem types registry（7807 沒有）。RFC 明言 problem details 是補充 HTTP status code、不是取代。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>&lt;code>type&lt;/code> 用 URI 而非字串 enum、把錯誤種類的命名空間外部化、避免各團隊 code 撞名；「client MUST ignore unknown extensions」是向前相容的演化條款、等同錯誤模型的 OCP。registry 的新增是對 7807 生態碎片化的直接回應。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.4 錯誤模型設計（anchor）、錯誤格式爭論文章。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.rfc-editor.org/rfc/rfc9457.html">RFC 9457: Problem Details for HTTP APIs（IETF、2023）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供錯誤格式標準化的現行基準（obsoletes RFC 7807）。</p>
<h2 id="觀察">觀察</h2>
<p>RFC 9457（IETF Proposed Standard、Standards Track、2023-07）定義 <code>application/problem+json</code>：五個核心成員 <code>type</code>（URI、預設 <code>about:blank</code>）、<code>title</code>、<code>status</code>、<code>detail</code>、<code>instance</code>；允許 extension members 且要求 client 忽略不認識的欄位；建立 IANA common problem types registry（7807 沒有）。RFC 明言 problem details 是補充 HTTP status code、不是取代。</p>
<h2 id="判讀">判讀</h2>
<p><code>type</code> 用 URI 而非字串 enum、把錯誤種類的命名空間外部化、避免各團隊 code 撞名；「client MUST ignore unknown extensions」是向前相容的演化條款、等同錯誤模型的 OCP。registry 的新增是對 7807 生態碎片化的直接回應。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.4 錯誤模型設計（anchor）、錯誤格式爭論文章。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.rfc-editor.org/rfc/rfc9457.html">RFC 9457: Problem Details for HTTP APIs（IETF、2023）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C36 Stripe 錯誤物件：type / code / param 三層分離</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/error-stripe-error-object/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/error-stripe-error-object/</guid><description>&lt;p>這個案例的核心責任是提供大型 API 錯誤模型的實作設計、跟 RFC 9457 形成「標準與大廠自訂並存」的對照。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Stripe 錯誤物件分四個 &lt;code>type&lt;/code>（&lt;code>api_error&lt;/code> / &lt;code>card_error&lt;/code> / &lt;code>idempotency_error&lt;/code> / &lt;code>invalid_request_error&lt;/code>）；&lt;code>code&lt;/code> 是細粒度機器碼、&lt;code>decline_code&lt;/code> 透傳發卡行原因、&lt;code>param&lt;/code> 指出出錯欄位、&lt;code>message&lt;/code> 明示可直接顯示給終端使用者、附 &lt;code>doc_url&lt;/code> 與 &lt;code>request_log_url&lt;/code>。HTTP 402 專用於「參數合法但交易被拒」。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>「路由層（type）、分支層（code）、UI 層（param + message）」做成三個正交欄位、client 端 error handling 各層各取所需。&lt;code>idempotency_error&lt;/code> 列為 first-class type、顯示冪等衝突在支付 API 是預期常態、不是邊角。Stripe 早於 RFC 7807 生態自成一格 — 標準與自訂並存是錯誤格式爭論的現實背景。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.4 錯誤模型設計（anchor）、11.8 API 層冪等交叉、錯誤格式爭論文章。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://docs.stripe.com/api/errors">Errors（Stripe API reference）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供大型 API 錯誤模型的實作設計、跟 RFC 9457 形成「標準與大廠自訂並存」的對照。</p>
<h2 id="觀察">觀察</h2>
<p>Stripe 錯誤物件分四個 <code>type</code>（<code>api_error</code> / <code>card_error</code> / <code>idempotency_error</code> / <code>invalid_request_error</code>）；<code>code</code> 是細粒度機器碼、<code>decline_code</code> 透傳發卡行原因、<code>param</code> 指出出錯欄位、<code>message</code> 明示可直接顯示給終端使用者、附 <code>doc_url</code> 與 <code>request_log_url</code>。HTTP 402 專用於「參數合法但交易被拒」。</p>
<h2 id="判讀">判讀</h2>
<p>「路由層（type）、分支層（code）、UI 層（param + message）」做成三個正交欄位、client 端 error handling 各層各取所需。<code>idempotency_error</code> 列為 first-class type、顯示冪等衝突在支付 API 是預期常態、不是邊角。Stripe 早於 RFC 7807 生態自成一格 — 標準與自訂並存是錯誤格式爭論的現實背景。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.4 錯誤模型設計（anchor）、11.8 API 層冪等交叉、錯誤格式爭論文章。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://docs.stripe.com/api/errors">Errors（Stripe API reference）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C37 Slack：offset 到 opaque cursor 的分頁遷移</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/pagination-slack-cursor-migration/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/pagination-slack-cursor-migration/</guid><description>&lt;p>這個案例的核心責任是提供 pagination 決策最乾淨的工程紀錄：明示 tradeoff 的產品決策。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Slack 記錄 offset 的兩個失效模式：&lt;code>LIMIT / OFFSET&lt;/code> 深頁掃描的丟棄成本、高寫入頻率下 page window 漂移造成跳項或重複。遷移到 Base64 opaque cursor（受 Relay GraphQL spec 啟發）、介面收斂為 &lt;code>cursor&lt;/code> 加 &lt;code>limit&lt;/code>、回傳 &lt;code>next_cursor&lt;/code>；opaque 編碼允許各 endpoint 底層策略不同、甚至在單一 cursor 內編多個 shard 的位置。明列 tradeoff：失去 total count 與跳頁能力。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>opaque cursor 的核心價值是「把分頁狀態的表示權留在 server 端」— client 不能解析就不能依賴內部格式、這是介面演化自由度的直接來源。「犧牲跳頁換一致性與效能」是明示的產品決策、不是技術妥協。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.7 集合介面設計（anchor）、分頁爭論文章。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://slack.engineering/evolving-api-pagination-at-slack/">Evolving API Pagination at Slack（Slack engineering blog）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供 pagination 決策最乾淨的工程紀錄：明示 tradeoff 的產品決策。</p>
<h2 id="觀察">觀察</h2>
<p>Slack 記錄 offset 的兩個失效模式：<code>LIMIT / OFFSET</code> 深頁掃描的丟棄成本、高寫入頻率下 page window 漂移造成跳項或重複。遷移到 Base64 opaque cursor（受 Relay GraphQL spec 啟發）、介面收斂為 <code>cursor</code> 加 <code>limit</code>、回傳 <code>next_cursor</code>；opaque 編碼允許各 endpoint 底層策略不同、甚至在單一 cursor 內編多個 shard 的位置。明列 tradeoff：失去 total count 與跳頁能力。</p>
<h2 id="判讀">判讀</h2>
<p>opaque cursor 的核心價值是「把分頁狀態的表示權留在 server 端」— client 不能解析就不能依賴內部格式、這是介面演化自由度的直接來源。「犧牲跳頁換一致性與效能」是明示的產品決策、不是技術妥協。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.7 集合介面設計（anchor）、分頁爭論文章。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://slack.engineering/evolving-api-pagination-at-slack/">Evolving API Pagination at Slack（Slack engineering blog）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C38 Stripe 冪等設計哲學：retry 是 client-server 協作</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/idempotency-stripe-design-blog/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/idempotency-stripe-design-blog/</guid><description>&lt;p>這個案例的核心責任是說明冪等是 client-server 的協作協議、server 端 replay 快取只解一半。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Stripe 用 &lt;code>Idempotency-Key&lt;/code> header 讓 POST 取得 exactly-once 語意；文章拆三種失敗點（連線建立前、執行中、回應遺失）並說明各自的 replay 行為；client 端責任是 exponential backoff 加 jitter（Ruby SDK 內建）、避免 thundering herd。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>冪等的教學重點是協作性：server 提供 replay 快取、client 不帶 backoff 的 retry 仍會把故障放大。三種失敗點的分類可直接對應 API 層冪等章的錯誤時序分析骨架。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.8 API 層冪等設計（anchor）、11.9 對外流量語意（retry / backoff）交叉。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://stripe.com/blog/idempotency">Designing robust and predictable APIs with idempotency（Stripe blog）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是說明冪等是 client-server 的協作協議、server 端 replay 快取只解一半。</p>
<h2 id="觀察">觀察</h2>
<p>Stripe 用 <code>Idempotency-Key</code> header 讓 POST 取得 exactly-once 語意；文章拆三種失敗點（連線建立前、執行中、回應遺失）並說明各自的 replay 行為；client 端責任是 exponential backoff 加 jitter（Ruby SDK 內建）、避免 thundering herd。</p>
<h2 id="判讀">判讀</h2>
<p>冪等的教學重點是協作性：server 提供 replay 快取、client 不帶 backoff 的 retry 仍會把故障放大。三種失敗點的分類可直接對應 API 層冪等章的錯誤時序分析骨架。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.8 API 層冪等設計（anchor）、11.9 對外流量語意（retry / backoff）交叉。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://stripe.com/blog/idempotency">Designing robust and predictable APIs with idempotency（Stripe blog）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C39 Stripe 冪等鍵契約條款：24h 保存、500 也重放</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/idempotency-stripe-api-contract/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/idempotency-stripe-api-contract/</guid><description>&lt;p>這個案例的核心責任是提供冪等鍵「可承諾的 what」、跟 C38 的「why」互補。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Stripe API reference 條款：key 最長 255 字元、建議 UUIDv4；保存至少 24 小時、逾期後同 key 視為新請求；replay 回傳首次請求的 status code 加 body、包含 500 錯誤也照樣快取重放；同 key 不同參數直接報錯；只作用於 POST、GET / DELETE 無效。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>「500 也重放」是自建實作最容易做錯的條款 — 快取的是「該次請求的結局」而非「成功結果」、否則同 key 重試會在 server 錯誤後產生第二次執行。「參數不符即錯」把 key 綁定到請求語意、防止 key 被當 session id 濫用。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.8 API 層冪等設計（anchor）、idempotency key 標準化爭論文章。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://docs.stripe.com/api/idempotent_requests">Idempotent requests（Stripe API reference）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供冪等鍵「可承諾的 what」、跟 C38 的「why」互補。</p>
<h2 id="觀察">觀察</h2>
<p>Stripe API reference 條款：key 最長 255 字元、建議 UUIDv4；保存至少 24 小時、逾期後同 key 視為新請求；replay 回傳首次請求的 status code 加 body、包含 500 錯誤也照樣快取重放；同 key 不同參數直接報錯；只作用於 POST、GET / DELETE 無效。</p>
<h2 id="判讀">判讀</h2>
<p>「500 也重放」是自建實作最容易做錯的條款 — 快取的是「該次請求的結局」而非「成功結果」、否則同 key 重試會在 server 錯誤後產生第二次執行。「參數不符即錯」把 key 綁定到請求語意、防止 key 被當 session id 濫用。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.8 API 層冪等設計（anchor）、idempotency key 標準化爭論文章。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://docs.stripe.com/api/idempotent_requests">Idempotent requests（Stripe API reference）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C40 IETF Idempotency-Key draft：標準化停在 expired</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/idempotency-ietf-key-header-draft/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/idempotency-ietf-key-header-draft/</guid><description>&lt;p>這個案例的核心責任是記錄冪等鍵標準化的現狀：業界實作先行、正式標準停滯。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>IETF httpapi WG 的 Internet-Draft 定義 &lt;code>Idempotency-Key&lt;/code> request header、使 POST / PATCH 可容錯重試；取代更早的個人 draft；推進到版本 07 後過期、狀態為 expired（不再 active）。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>「de facto 先於 de jure」的具體例：Stripe / PayPal 等支付商的實作是事實標準、IETF 標準化跟進但停滯。寫 API 時該遵循的是 draft 的語意骨架加具體 vendor 的契約細節、且不能宣稱「這是 RFC」— 引用時必須標明 expired draft 狀態。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.8 API 層冪等設計、idempotency key 標準化爭論文章。邊緣（狀態需明示）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/">The Idempotency-Key HTTP Header Field（IETF draft、expired、v07）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄冪等鍵標準化的現狀：業界實作先行、正式標準停滯。</p>
<h2 id="觀察">觀察</h2>
<p>IETF httpapi WG 的 Internet-Draft 定義 <code>Idempotency-Key</code> request header、使 POST / PATCH 可容錯重試；取代更早的個人 draft；推進到版本 07 後過期、狀態為 expired（不再 active）。</p>
<h2 id="判讀">判讀</h2>
<p>「de facto 先於 de jure」的具體例：Stripe / PayPal 等支付商的實作是事實標準、IETF 標準化跟進但停滯。寫 API 時該遵循的是 draft 的語意骨架加具體 vendor 的契約細節、且不能宣稱「這是 RFC」— 引用時必須標明 expired draft 狀態。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.8 API 層冪等設計、idempotency key 標準化爭論文章。邊緣（狀態需明示）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/">The Idempotency-Key HTTP Header Field（IETF draft、expired、v07）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C41 PayPal-Request-Id：同語意、不同契約的冪等實作</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/idempotency-paypal-request-id/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/idempotency-paypal-request-id/</guid><description>&lt;p>這個案例的核心責任是當 Stripe 冪等契約（C39）的對照組、展示無標準狀態下的實作分歧。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>PayPal 的冪等 header 名為 &lt;code>PayPal-Request-Id&lt;/code>；並非所有 API 支援、保存期只寫「a period of time」、細節要查各 API reference；replay 回傳「前次請求的最新狀態」；明示同 ID 並發請求時第二個可能失敗。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>與 Stripe 的對照有三點：header 命名不同（無標準的直接後果、呼應 C40）；replay 語意不同 — Stripe 重放「首次結局快照」、PayPal 回「最新狀態」、後者對 async 操作友善但失去 exactly-once 回應保證；契約精確度不同 — Stripe 承諾 24h、PayPal 模糊。這組差異本身就是比較教材。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.8 API 層冪等設計、idempotency key 標準化爭論文章。邊緣（對照組）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://developer.paypal.com/api/rest/reference/idempotency/">Idempotency（PayPal developer docs）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是當 Stripe 冪等契約（C39）的對照組、展示無標準狀態下的實作分歧。</p>
<h2 id="觀察">觀察</h2>
<p>PayPal 的冪等 header 名為 <code>PayPal-Request-Id</code>；並非所有 API 支援、保存期只寫「a period of time」、細節要查各 API reference；replay 回傳「前次請求的最新狀態」；明示同 ID 並發請求時第二個可能失敗。</p>
<h2 id="判讀">判讀</h2>
<p>與 Stripe 的對照有三點：header 命名不同（無標準的直接後果、呼應 C40）；replay 語意不同 — Stripe 重放「首次結局快照」、PayPal 回「最新狀態」、後者對 async 操作友善但失去 exactly-once 回應保證；契約精確度不同 — Stripe 承諾 24h、PayPal 模糊。這組差異本身就是比較教材。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.8 API 層冪等設計、idempotency key 標準化爭論文章。邊緣（對照組）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://developer.paypal.com/api/rest/reference/idempotency/">Idempotency（PayPal developer docs）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C42 IETF RateLimit headers：政策與狀態拆兩個 header</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/ratelimit-ietf-header-fields/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/ratelimit-ietf-header-fields/</guid><description>&lt;p>這個案例的核心責任是提供限流語意標準化的現行進展與它明文的承諾邊界。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>IETF httpapi WG 的 active draft（版本 11、2026-05、intended Standards Track）定義兩個 response header：&lt;code>RateLimit-Policy&lt;/code>（靜態配額政策：quota / window / partition key、Structured Fields 語法）與 &lt;code>RateLimit&lt;/code>（動態剩餘量）。明文「client MUST NOT assume 正配額保證下一請求會被服務」— 配額資訊僅供參考。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>政策（policy）與即時狀態（status）拆成兩個 header 是關鍵設計：政策可快取、狀態逐請求變動。「informational only」條款界定了限流 header 的承諾邊界 — 它是禮貌性預警、不是 SLA、這直接回答「對外流量語意承諾到什麼程度」。仍是 draft、引用需標版本與狀態。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.9 對外流量語意（anchor、draft 狀態需明示）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/">RateLimit header fields for HTTP（IETF draft、active、v11）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供限流語意標準化的現行進展與它明文的承諾邊界。</p>
<h2 id="觀察">觀察</h2>
<p>IETF httpapi WG 的 active draft（版本 11、2026-05、intended Standards Track）定義兩個 response header：<code>RateLimit-Policy</code>（靜態配額政策：quota / window / partition key、Structured Fields 語法）與 <code>RateLimit</code>（動態剩餘量）。明文「client MUST NOT assume 正配額保證下一請求會被服務」— 配額資訊僅供參考。</p>
<h2 id="判讀">判讀</h2>
<p>政策（policy）與即時狀態（status）拆成兩個 header 是關鍵設計：政策可快取、狀態逐請求變動。「informational only」條款界定了限流 header 的承諾邊界 — 它是禮貌性預警、不是 SLA、這直接回答「對外流量語意承諾到什麼程度」。仍是 draft、引用需標版本與狀態。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.9 對外流量語意（anchor、draft 狀態需明示）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/">RateLimit header fields for HTTP（IETF draft、active、v11）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C43 GitHub 雙層限流：primary / secondary 與 x-ratelimit 契約</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/ratelimit-github-primary-secondary/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/ratelimit-github-primary-secondary/</guid><description>&lt;p>這個案例的核心責任是提供大平台限流對外契約的實作切片、跟 IETF draft（C42）對照。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>GitHub REST API 提供四個 header：&lt;code>x-ratelimit-limit&lt;/code> / &lt;code>-remaining&lt;/code> / &lt;code>-used&lt;/code> / &lt;code>-reset&lt;/code>（UTC epoch 秒）；超限回 403 或 429（文件未明確劃分兩者使用時機）；secondary limit 命中且有 &lt;code>retry-after&lt;/code> 時要求等滿秒數；primary（每小時額度）與 secondary（並發、單端點吞吐、CPU、內容建立速率）分層；持續打限流請求可能導致 integration 被 ban。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>secondary limit 的存在說明單一維度配額擋不住真實濫用模式。GitHub 是前標準時代 x- 前綴 header 的代表、與 IETF 命名並存 — 遷移期的現實是新 API 該出標準 header、client 仍要能讀 x- 系。「403 / 429 混用未明確化」是文件可指出的語意瑕疵（fact：文件確實未區分）。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.9 對外流量語意（anchor）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api">Rate limits for the REST API（GitHub docs）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供大平台限流對外契約的實作切片、跟 IETF draft（C42）對照。</p>
<h2 id="觀察">觀察</h2>
<p>GitHub REST API 提供四個 header：<code>x-ratelimit-limit</code> / <code>-remaining</code> / <code>-used</code> / <code>-reset</code>（UTC epoch 秒）；超限回 403 或 429（文件未明確劃分兩者使用時機）；secondary limit 命中且有 <code>retry-after</code> 時要求等滿秒數；primary（每小時額度）與 secondary（並發、單端點吞吐、CPU、內容建立速率）分層；持續打限流請求可能導致 integration 被 ban。</p>
<h2 id="判讀">判讀</h2>
<p>secondary limit 的存在說明單一維度配額擋不住真實濫用模式。GitHub 是前標準時代 x- 前綴 header 的代表、與 IETF 命名並存 — 遷移期的現實是新 API 該出標準 header、client 仍要能讀 x- 系。「403 / 429 混用未明確化」是文件可指出的語意瑕疵（fact：文件確實未區分）。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.9 對外流量語意（anchor）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api">Rate limits for the REST API（GitHub docs）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C44 Google AIP-151：長時操作實體化成 Operation resource</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/longrun-google-aip151/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/longrun-google-aip151/</guid><description>&lt;p>這個案例的核心責任是提供長時操作介面模式的系統化規範。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>AIP-151 規定長時方法回傳 &lt;code>google.longrunning.Operation&lt;/code>（類比 Future / Promise）、必須標注 &lt;code>response_type&lt;/code> 加 &lt;code>metadata_type&lt;/code>；client 經 Operations service 輪詢；狀態欄位 &lt;code>done&lt;/code> / &lt;code>response&lt;/code> / &lt;code>error&lt;/code>（&lt;code>google.rpc.Status&lt;/code>）；操作約 30 天過期；validate-only 請求應直接回 &lt;code>done=true&lt;/code> 的完成 operation、免除狀態管理。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>把「進行中的工作」實體化成可 GET 的 resource、是 202 加 Location 輪詢模式的系統化版本：回應型別在 annotation 先宣告、client 可以統一寫一套 polling 邏輯。&lt;code>done=true&lt;/code> 的 validate-only 條款示範「同一介面形狀涵蓋同步捷徑」的設計手法；30 天過期是 operation resource 生命週期需明訂的實例。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.7 集合介面設計（長時操作段、anchor）、11.10 治理交叉（AIP 體系的一篇）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://google.aip.dev/151">AIP-151: Long-running operations（Google AIP）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供長時操作介面模式的系統化規範。</p>
<h2 id="觀察">觀察</h2>
<p>AIP-151 規定長時方法回傳 <code>google.longrunning.Operation</code>（類比 Future / Promise）、必須標注 <code>response_type</code> 加 <code>metadata_type</code>；client 經 Operations service 輪詢；狀態欄位 <code>done</code> / <code>response</code> / <code>error</code>（<code>google.rpc.Status</code>）；操作約 30 天過期；validate-only 請求應直接回 <code>done=true</code> 的完成 operation、免除狀態管理。</p>
<h2 id="判讀">判讀</h2>
<p>把「進行中的工作」實體化成可 GET 的 resource、是 202 加 Location 輪詢模式的系統化版本：回應型別在 annotation 先宣告、client 可以統一寫一套 polling 邏輯。<code>done=true</code> 的 validate-only 條款示範「同一介面形狀涵蓋同步捷徑」的設計手法；30 天過期是 operation resource 生命週期需明訂的實例。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.7 集合介面設計（長時操作段、anchor）、11.10 治理交叉（AIP 體系的一篇）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://google.aip.dev/151">AIP-151: Long-running operations（Google AIP）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C45 Twilio 2013 計費事故：無冪等防線的重複扣款（反例）</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/idempotency-twilio-billing-postmortem/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/idempotency-twilio-billing-postmortem/</guid><description>&lt;p>這個案例的核心責任是提供冪等缺席的事故反例：冪等不只是對外 API header、內部 side-effect 動作同樣需要閘門。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Twilio 2013 年的 post-mortem：Redis master 重啟時讀錯設定、以自己的 slave 身份開機進入 read-only；餘額資料遺失歸零且無法寫回；auto-recharge 在「餘額為零、扣款成功、餘額寫不回去」的循環中對約 1.4% 客戶的信用卡重複扣款。修復含：餘額不可寫時禁止扣款與停權的 fail-safe、對獨立 double-bookkeeping 資料庫做即時驗證。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>扣款動作的觸發條件（餘額低）在扣款後未被消除、等效於無限重放的非冪等操作。教學映射：冪等閘門的通用形式是「執行紀錄先寫、後執行」；fail-safe（狀態寫不進去就不准產生金流 side effect）是 write-path 依賴的斷路器。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.8 API 層冪等設計（反例）、11.4 錯誤狀態下的降級決策交叉。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.twilio.com/en-us/blog/company/communications/billing-incident-post-mortem-breakdown-analysis-and-root-cause-html">Billing Incident Post-Mortem（Twilio blog、2013）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供冪等缺席的事故反例：冪等不只是對外 API header、內部 side-effect 動作同樣需要閘門。</p>
<h2 id="觀察">觀察</h2>
<p>Twilio 2013 年的 post-mortem：Redis master 重啟時讀錯設定、以自己的 slave 身份開機進入 read-only；餘額資料遺失歸零且無法寫回；auto-recharge 在「餘額為零、扣款成功、餘額寫不回去」的循環中對約 1.4% 客戶的信用卡重複扣款。修復含：餘額不可寫時禁止扣款與停權的 fail-safe、對獨立 double-bookkeeping 資料庫做即時驗證。</p>
<h2 id="判讀">判讀</h2>
<p>扣款動作的觸發條件（餘額低）在扣款後未被消除、等效於無限重放的非冪等操作。教學映射：冪等閘門的通用形式是「執行紀錄先寫、後執行」；fail-safe（狀態寫不進去就不准產生金流 side effect）是 write-path 依賴的斷路器。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.8 API 層冪等設計（反例）、11.4 錯誤狀態下的降級決策交叉。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.twilio.com/en-us/blog/company/communications/billing-incident-post-mortem-breakdown-analysis-and-root-cause-html">Billing Incident Post-Mortem（Twilio blog、2013）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C46 Google AIP：規範即提案系統的治理模式</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/governance-google-aip-model/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/governance-google-aip-model/</guid><description>&lt;p>這個案例的核心責任是記錄「規範即提案系統」的治理模式原型。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>AIP-1 定義 AIP 為 API 開發的高階精簡設計文件、以 GitHub 維護、編號提案形式累積。治理採編輯團制（7 位 approver、editorship 採現任編輯邀請制）；提案進入 Reviewing 需 1 位編輯核可且無正式異議、進入 Approved 需 2 位非作者 approver 正式 signoff；TL 是流程的最終決策者與 escalation 終點。動機明言：Google API 生態擴張後需要一套可供 producer 與 reviewer 引用的文件語料。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>AIP 把 API 規範治理做成有編號、狀態機、簽核門檻的提案系統 — 本質是把 IETF RFC 流程內化到單一組織、重點是決策可追溯與規範可演進、而非一次性文件。編輯邀請制加 TL 終審顯示它仍是中心化治理、社群貢獻是輸入不是決策權。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.10 API 規範治理（anchor、與 Zalando Guild 制對照）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://google.aip.dev/1">AIP-1: AIP Purpose and Guidelines（Google AIP）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄「規範即提案系統」的治理模式原型。</p>
<h2 id="觀察">觀察</h2>
<p>AIP-1 定義 AIP 為 API 開發的高階精簡設計文件、以 GitHub 維護、編號提案形式累積。治理採編輯團制（7 位 approver、editorship 採現任編輯邀請制）；提案進入 Reviewing 需 1 位編輯核可且無正式異議、進入 Approved 需 2 位非作者 approver 正式 signoff；TL 是流程的最終決策者與 escalation 終點。動機明言：Google API 生態擴張後需要一套可供 producer 與 reviewer 引用的文件語料。</p>
<h2 id="判讀">判讀</h2>
<p>AIP 把 API 規範治理做成有編號、狀態機、簽核門檻的提案系統 — 本質是把 IETF RFC 流程內化到單一組織、重點是決策可追溯與規範可演進、而非一次性文件。編輯邀請制加 TL 終審顯示它仍是中心化治理、社群貢獻是輸入不是決策權。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.10 API 規範治理（anchor、與 Zalando Guild 制對照）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://google.aip.dev/1">AIP-1: AIP Purpose and Guidelines（Google AIP）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C47 Zalando API-first：Guidelines、Guild、Zally、Portal 四件套</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/governance-zalando-api-first/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/governance-zalando-api-first/</guid><description>&lt;p>這個案例的核心責任是展示「規範文件 + 執行機制」的完整治理系統。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Guidelines repo 開宗明義「Great RESTful APIs look like they were designed by a single team」、CC-BY 4.0、約 3.2k stars、自述為 living document。Engineering blog 描述治理配套：8,000+ 服務、300+ 團隊規模下、API Guild（API 愛好者與架構師組成）擁有 guidelines 的 ownership、所有人可貢獻；API spec 以 PR 提交 peer review、重要 API 由 Guild 介入審查；另有 API Portal 集中所有已部署服務的 spec。核心哲學是 API-as-a-Product / API-First。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>Guidelines（文件）、Guild（人的治理）、Zally（自動化）、Portal（可發現性）構成完整系統 — 缺一環都會退化成書架文件。Guild 模式介於中心化委員會與完全去中心之間：ownership 集中、貢獻開放、適合跟 Google 編輯團制（C46）對照。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.10 API 規範治理（anchor）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/zalando/restful-api-guidelines">Zalando RESTful API and Event Guidelines（GitHub repo）&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://engineering.zalando.com/posts/2019/04/developing-zalando-apis.html">Developing Zalando APIs（Zalando engineering blog、2019）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是展示「規範文件 + 執行機制」的完整治理系統。</p>
<h2 id="觀察">觀察</h2>
<p>Guidelines repo 開宗明義「Great RESTful APIs look like they were designed by a single team」、CC-BY 4.0、約 3.2k stars、自述為 living document。Engineering blog 描述治理配套：8,000+ 服務、300+ 團隊規模下、API Guild（API 愛好者與架構師組成）擁有 guidelines 的 ownership、所有人可貢獻；API spec 以 PR 提交 peer review、重要 API 由 Guild 介入審查；另有 API Portal 集中所有已部署服務的 spec。核心哲學是 API-as-a-Product / API-First。</p>
<h2 id="判讀">判讀</h2>
<p>Guidelines（文件）、Guild（人的治理）、Zally（自動化）、Portal（可發現性）構成完整系統 — 缺一環都會退化成書架文件。Guild 模式介於中心化委員會與完全去中心之間：ownership 集中、貢獻開放、適合跟 Google 編輯團制（C46）對照。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.10 API 規範治理（anchor）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://github.com/zalando/restful-api-guidelines">Zalando RESTful API and Event Guidelines（GitHub repo）</a></li>
<li><a href="https://engineering.zalando.com/posts/2019/04/developing-zalando-apis.html">Developing Zalando APIs（Zalando engineering blog、2019）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C48 Microsoft REST Guidelines：單一 repo 內的分軌治理</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/governance-microsoft-rest-guidelines/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/governance-microsoft-rest-guidelines/</guid><description>&lt;p>這個案例的核心責任是提供大型組織規範分軌的實況證據。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>repo 含三條 guidance track：核心 &lt;code>Guidelines.md&lt;/code>、&lt;code>/azure&lt;/code> 資料夾（Azure 服務團隊專用）、&lt;code>/graph&lt;/code> 資料夾（Microsoft Graph 團隊專用）。約 23.3k stars、vNext 分支 949 commits、CC-BY 4.0。README 自述目的包含「fostering dialogue and learning in the API community at large」、鼓勵其他組織發展自己的 guidelines。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>同一組織內分軌（Core vs Azure vs Graph）證明單一 guideline 無法覆蓋差異巨大的產品線、規範會沿組織邊界分化 — 跟 Zalando「像同一團隊設計」的理想（C47）形成有用的張力。公開 repo 加對外喊話也顯示大廠把內部規範開源當成社群影響力工具。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.10 API 規範治理（anchor、治理模式比較的第三型）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/microsoft/api-guidelines">Microsoft REST API Guidelines（GitHub repo）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供大型組織規範分軌的實況證據。</p>
<h2 id="觀察">觀察</h2>
<p>repo 含三條 guidance track：核心 <code>Guidelines.md</code>、<code>/azure</code> 資料夾（Azure 服務團隊專用）、<code>/graph</code> 資料夾（Microsoft Graph 團隊專用）。約 23.3k stars、vNext 分支 949 commits、CC-BY 4.0。README 自述目的包含「fostering dialogue and learning in the API community at large」、鼓勵其他組織發展自己的 guidelines。</p>
<h2 id="判讀">判讀</h2>
<p>同一組織內分軌（Core vs Azure vs Graph）證明單一 guideline 無法覆蓋差異巨大的產品線、規範會沿組織邊界分化 — 跟 Zalando「像同一團隊設計」的理想（C47）形成有用的張力。公開 repo 加對外喊話也顯示大廠把內部規範開源當成社群影響力工具。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.10 API 規範治理（anchor、治理模式比較的第三型）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://github.com/microsoft/api-guidelines">Microsoft REST API Guidelines（GitHub repo）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C49 Spectral 與 Zally：guidelines 變成可執行檢查</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/governance-linting-spectral-zally/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/governance-linting-spectral-zally/</guid><description>&lt;p>這個案例的核心責任是說明規範 machine-checkable 化的工具譜系與工具治理的選型訊號。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Spectral 是 JSON / YAML linter、內建 OpenAPI 2.0 / 3.0 / 3.1、AsyncAPI 2.x、Arazzo 1.0 rulesets、支援自訂 rule / function、整合 GitHub Actions、git hooks、IDE、CI pipeline；約 3.1k stars、8.1k dependent projects、2026-06 仍在發版、README 列 Adidas / Azure / Box / Zalando 的實際 ruleset。Zally 是 Zalando 的 OpenAPI linter、預設 ruleset 直接執行 Zalando guidelines、提供 API / CLI / Web UI 三介面；最近 release 停在 2022-12。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>規則寫成 machine-checkable ruleset、在設計期給 early feedback、把治理成本從人工 review 前移到 CI — 這是 guidelines 落地的關鍵一步。Spectral 的 8.1k dependents 對比 Zally 的停滯顯示：「通用 linter 加組織自帶 ruleset」比「單一組織專用 linter」更能存活、是工具治理選型的判讀訊號。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.10 API 規範治理（linting 進 CI 段、anchor）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/stoplightio/spectral">stoplightio/spectral（GitHub repo）&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/zalando/zally">zalando/zally（GitHub repo）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是說明規範 machine-checkable 化的工具譜系與工具治理的選型訊號。</p>
<h2 id="觀察">觀察</h2>
<p>Spectral 是 JSON / YAML linter、內建 OpenAPI 2.0 / 3.0 / 3.1、AsyncAPI 2.x、Arazzo 1.0 rulesets、支援自訂 rule / function、整合 GitHub Actions、git hooks、IDE、CI pipeline；約 3.1k stars、8.1k dependent projects、2026-06 仍在發版、README 列 Adidas / Azure / Box / Zalando 的實際 ruleset。Zally 是 Zalando 的 OpenAPI linter、預設 ruleset 直接執行 Zalando guidelines、提供 API / CLI / Web UI 三介面；最近 release 停在 2022-12。</p>
<h2 id="判讀">判讀</h2>
<p>規則寫成 machine-checkable ruleset、在設計期給 early feedback、把治理成本從人工 review 前移到 CI — 這是 guidelines 落地的關鍵一步。Spectral 的 8.1k dependents 對比 Zally 的停滯顯示：「通用 linter 加組織自帶 ruleset」比「單一組織專用 linter」更能存活、是工具治理選型的判讀訊號。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.10 API 規範治理（linting 進 CI 段、anchor）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://github.com/stoplightio/spectral">stoplightio/spectral（GitHub repo）</a></li>
<li><a href="https://github.com/zalando/zally">zalando/zally（GitHub repo）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C50 JSON:API：以停止 bikeshedding 為賣點的格式標準</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/standards-jsonapi-antibikeshedding/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/standards-jsonapi-antibikeshedding/</guid><description>&lt;p>這個案例的核心責任是記錄「跨組織標準想解組織內治理問題」的代表性嘗試。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>官網明言設計目標：「If you&amp;rsquo;ve ever argued with your team about the way your JSON responses should be formatted, JSON:API can help you stop the bikeshedding」。現行版本 1.1（2022-09 定稿、距 1.0 約 7 年）。賣點包含共享慣例帶來的工具重用、以及 client 端可高效快取 response、有時能完全省掉網路請求。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>JSON:API 把價值主張放在組織成本（消除格式爭論）而非技術能力。版本節奏極慢可作兩面判讀：spec 穩定成熟、或演進動能有限。教學上對照 in-house guidelines 路線 —「採現成標準」vs「自建規範加治理機制」是規範治理的核心選擇題。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>styles/standards/「JSON:API 與 OData 的標準化嘗試」（anchor）、11.10 採標準與自建規範段（已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://jsonapi.org/">JSON:API 官方網站&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄「跨組織標準想解組織內治理問題」的代表性嘗試。</p>
<h2 id="觀察">觀察</h2>
<p>官網明言設計目標：「If you&rsquo;ve ever argued with your team about the way your JSON responses should be formatted, JSON:API can help you stop the bikeshedding」。現行版本 1.1（2022-09 定稿、距 1.0 約 7 年）。賣點包含共享慣例帶來的工具重用、以及 client 端可高效快取 response、有時能完全省掉網路請求。</p>
<h2 id="判讀">判讀</h2>
<p>JSON:API 把價值主張放在組織成本（消除格式爭論）而非技術能力。版本節奏極慢可作兩面判讀：spec 穩定成熟、或演進動能有限。教學上對照 in-house guidelines 路線 —「採現成標準」vs「自建規範加治理機制」是規範治理的核心選擇題。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>styles/standards/「JSON:API 與 OData 的標準化嘗試」（anchor）、11.10 採標準與自建規範段（已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://jsonapi.org/">JSON:API 官方網站</a></li>
</ul>
]]></content:encoded></item><item><title>11.C51 OData：ISO 認證救不了生態萎縮（反例）</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/standards-odata-decline/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/standards-odata-decline/</guid><description>&lt;p>這個案例的核心責任是提供跨組織標準化嘗試的退場反例。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>OData v4 是 OASIS 標準且有 ISO/IEC 認證（20802-1/2:2016）、定位是可查詢、可互通的 RESTful API 建構協議、生態工具以 .NET（Restier）與 Java（Apache Olingo）為主。二手分析（Ben Morris、2013）記錄 Netflix 低調關閉 OData catalogue、eBay 同步棄用、歸因三點：生態侷限於 .NET、Microsoft 出身的信任問題、技術面「magic box」批評 — 暴露資料庫內部細節、自動生成 generic 介面而非刻意設計的 API。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>ISO 認證救不了生態萎縮 — marquee adopter 離場的訊號比標準機構背書更能預測標準命運。查詢協議把 repository 直通到 wire 的設計、跟「API 是刻意設計的契約」的治理理念直接衝突 — 這是它進不了 guidelines 主流的深層原因、不只是出身問題。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>styles/standards/「JSON:API 與 OData 的標準化嘗試」（反例）、11.10 採標準與自建規範段（已引用）。退場分析屬二手來源、標明。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.odata.org/">OData 官方網站&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.ben-morris.com/netflix-has-abandoned-odata-does-the-standard-have-a-future-without-an-ecosystem/">Netflix has abandoned OData — does the standard have a future without an ecosystem?（Ben Morris、2013、二手分析）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供跨組織標準化嘗試的退場反例。</p>
<h2 id="觀察">觀察</h2>
<p>OData v4 是 OASIS 標準且有 ISO/IEC 認證（20802-1/2:2016）、定位是可查詢、可互通的 RESTful API 建構協議、生態工具以 .NET（Restier）與 Java（Apache Olingo）為主。二手分析（Ben Morris、2013）記錄 Netflix 低調關閉 OData catalogue、eBay 同步棄用、歸因三點：生態侷限於 .NET、Microsoft 出身的信任問題、技術面「magic box」批評 — 暴露資料庫內部細節、自動生成 generic 介面而非刻意設計的 API。</p>
<h2 id="判讀">判讀</h2>
<p>ISO 認證救不了生態萎縮 — marquee adopter 離場的訊號比標準機構背書更能預測標準命運。查詢協議把 repository 直通到 wire 的設計、跟「API 是刻意設計的契約」的治理理念直接衝突 — 這是它進不了 guidelines 主流的深層原因、不只是出身問題。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>styles/standards/「JSON:API 與 OData 的標準化嘗試」（反例）、11.10 採標準與自建規範段（已引用）。退場分析屬二手來源、標明。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.odata.org/">OData 官方網站</a></li>
<li><a href="https://www.ben-morris.com/netflix-has-abandoned-odata-does-the-standard-have-a-future-without-an-ecosystem/">Netflix has abandoned OData — does the standard have a future without an ecosystem?（Ben Morris、2013、二手分析）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C52 OpenAPI Initiative：從 Swagger 捐贈到開放治理</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/standards-openapi-initiative-evolution/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/standards-openapi-initiative-evolution/</guid><description>&lt;p>這個案例的核心責任是記錄 API 描述標準的成功轉軌路徑、跟 OData（C51）形成直接對照。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>OAI 官網記載 OpenAPI Specification 源自 SmartBear 捐贈的 Swagger Specification；OAI 在 Linux Foundation 下以 open governance 運作、強調 vendor neutrality、治理機構含 Technical Steering Committee 與 Technical Oversight Board、細節公開於 GitHub。範圍已擴展到 OpenAPI 之外：Arazzo（多 API workflow）與 Overlay（API description 自動更新）。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>與 OData 的關鍵差異：轉移時 Swagger 已是事實標準、治理轉移是把既有動能中立化、而非用標準機構背書去創造動能。OAI 站穩後沿「描述 API 的周邊問題」擴張版圖（Arazzo / Overlay）、是標準組織生命週期的可觀察模式。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>styles/standards/「OpenAPI 與 AsyncAPI 生態」（anchor）、11.10 採標準與自建規範段（已引用）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.openapis.org/about">About the OpenAPI Initiative&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄 API 描述標準的成功轉軌路徑、跟 OData（C51）形成直接對照。</p>
<h2 id="觀察">觀察</h2>
<p>OAI 官網記載 OpenAPI Specification 源自 SmartBear 捐贈的 Swagger Specification；OAI 在 Linux Foundation 下以 open governance 運作、強調 vendor neutrality、治理機構含 Technical Steering Committee 與 Technical Oversight Board、細節公開於 GitHub。範圍已擴展到 OpenAPI 之外：Arazzo（多 API workflow）與 Overlay（API description 自動更新）。</p>
<h2 id="判讀">判讀</h2>
<p>與 OData 的關鍵差異：轉移時 Swagger 已是事實標準、治理轉移是把既有動能中立化、而非用標準機構背書去創造動能。OAI 站穩後沿「描述 API 的周邊問題」擴張版圖（Arazzo / Overlay）、是標準組織生命週期的可觀察模式。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>styles/standards/「OpenAPI 與 AsyncAPI 生態」（anchor）、11.10 採標準與自建規範段（已引用）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.openapis.org/about">About the OpenAPI Initiative</a></li>
</ul>
]]></content:encoded></item><item><title>11.C53 AsyncAPI：刻意相容 OpenAPI 的補位策略</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/standards-asyncapi-complement/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/standards-asyncapi-complement/</guid><description>&lt;p>這個案例的核心責任是記錄「補位式標準化」策略：不另起爐灶、以相容換採用。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>官方文件自述 AsyncAPI 起於 OpenAPI specification 的 adaptation、刻意維持相容並重用 OpenAPI schema；結構差異為 Paths 對 Channels、HTTP verbs 對 Publish / Subscribe、加入 Correlation Id 與 protocol bindings 等 protocol-agnostic 概念；3.0 進一步把 operations 從 channels 拆離。補位論證：「systems don&amp;rsquo;t have just REST APIs or events, but a mix of both」、支援跨同步 / 非同步的 schema 重用與多協議。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>AsyncAPI 明確承認 OpenAPI 的生態位、只填 event-driven 空白 — 以相容性換採用曲線。教學意義：描述格式的邊界即治理邊界 — 組織同時有 REST 加 event 時、規範治理需要兩份 spec 格式但一套 schema 來源。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>styles/standards/「OpenAPI 與 AsyncAPI 生態」（anchor）、11.10 採標準與自建規範段（已引用）、03 訊息佇列模組交叉。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.asyncapi.com/docs/tutorials/getting-started/coming-from-openapi">Coming from OpenAPI（AsyncAPI docs）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是記錄「補位式標準化」策略：不另起爐灶、以相容換採用。</p>
<h2 id="觀察">觀察</h2>
<p>官方文件自述 AsyncAPI 起於 OpenAPI specification 的 adaptation、刻意維持相容並重用 OpenAPI schema；結構差異為 Paths 對 Channels、HTTP verbs 對 Publish / Subscribe、加入 Correlation Id 與 protocol bindings 等 protocol-agnostic 概念；3.0 進一步把 operations 從 channels 拆離。補位論證：「systems don&rsquo;t have just REST APIs or events, but a mix of both」、支援跨同步 / 非同步的 schema 重用與多協議。</p>
<h2 id="判讀">判讀</h2>
<p>AsyncAPI 明確承認 OpenAPI 的生態位、只填 event-driven 空白 — 以相容性換採用曲線。教學意義：描述格式的邊界即治理邊界 — 組織同時有 REST 加 event 時、規範治理需要兩份 spec 格式但一套 schema 來源。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>styles/standards/「OpenAPI 與 AsyncAPI 生態」（anchor）、11.10 採標準與自建規範段（已引用）、03 訊息佇列模組交叉。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.asyncapi.com/docs/tutorials/getting-started/coming-from-openapi">Coming from OpenAPI（AsyncAPI docs）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C54 White House API Standards：規範制定後棄置（反例）</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/governance-whitehouse-api-standards-archived/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/governance-whitehouse-api-standards-archived/</guid><description>&lt;p>這個案例的核心責任是提供「guidelines 制定但治理缺席」的乾淨反例。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>repo 於 2022-03 正式 archived、轉唯讀、總計僅 34 commits。內容是完整的 RESTful API guidelines（URL 結構、HTTP verbs、錯誤處理、版本策略、分頁）、README 強調平衡 RESTful 介面與 developer experience。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>文件品質不差、但沒有 Guild / linter / review 流程等執行機制、也沒有持續的 ownership、停在 34 commits 後封存。與 Zalando 四件套（C47）並排可直接論證：規範的存活取決於配套組織機制、不取決於文件本身寫得多好。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.10 API 規範治理（反例）。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/WhiteHouse/api-standards">WhiteHouse/api-standards（GitHub repo、已 archived）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供「guidelines 制定但治理缺席」的乾淨反例。</p>
<h2 id="觀察">觀察</h2>
<p>repo 於 2022-03 正式 archived、轉唯讀、總計僅 34 commits。內容是完整的 RESTful API guidelines（URL 結構、HTTP verbs、錯誤處理、版本策略、分頁）、README 強調平衡 RESTful 介面與 developer experience。</p>
<h2 id="判讀">判讀</h2>
<p>文件品質不差、但沒有 Guild / linter / review 流程等執行機制、也沒有持續的 ownership、停在 34 commits 後封存。與 Zalando 四件套（C47）並排可直接論證：規範的存活取決於配套組織機制、不取決於文件本身寫得多好。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.10 API 規範治理（反例）。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://github.com/WhiteHouse/api-standards">WhiteHouse/api-standards（GitHub repo、已 archived）</a></li>
</ul>
]]></content:encoded></item></channel></rss>