API Spec Review Checklist — 30 個問題抓住 Contract / Error / Version 漏洞

API spec review 不能套用一般 spec review checklist。Endpoint contract、錯誤碼、版本控制、idempotency 是 API 獨有的痛點。這篇給你一份能在會議前 20 分鐘掃完的 API spec 專用 checklist。

API Spec Review 的 6 大面向

mindmap
  root((API Spec<br>Review))
    Contract
      Endpoint
      Method
      Request schema
      Response schema
      欄位型別
    Error
      錯誤碼
      錯誤 payload
      HTTP status
      Retry 策略
    Auth
      認證方式
      授權邏輯
      Token TTL
      多租戶隔離
    Versioning
      URL 版本
      Header 版本
      Breaking change
      Deprecation
    Performance
      Rate limit
      Pagination
      Caching
      Idempotency
    Data
      欄位完整性
      Schema 嚴格
      時區
      隱私敏感

一、Contract(契約清晰度)

必問 5 題

  1. 每個 endpoint 用什麼 HTTP method? GET / POST / PUT / PATCH / DELETE 各有語意
  2. Path 設計符合 REST? /users/{id}/orders 不是 /getUserOrders?uid=
  3. Request body schema 完整嗎? 每欄型別、必填、長度、格式
  4. Response schema 完整嗎? 含 success / error 兩種
  5. 嵌套物件結構是否合理? Flat vs Nested 取捨

反例(spec 太薄)

POST /orders
Request: { items, total }
Response: { id, status }

問題

  • items 是什麼結構?array of object?欄位有什麼?
  • total 包稅嗎?幣別?
  • id 是 int 還 string?UUID?
  • status 有哪些可能值?

好例子

POST /api/v1/orders
Request:
  Content-Type: application/json
  Body:
    items:
      type: array
      minItems: 1
      maxItems: 50
      item:
        sku: string (3-20 chars, /^[A-Z0-9-]+$/)
        qty: integer (1-99)
    payment_method: enum [credit_card, paypal, apple_pay]
    coupon_code: string (optional, 6-12 chars)
    shipping_address: <ShippingAddress object>
Response 201:
  id: string (UUID v4)
  status: enum [pending, confirmed]
  total: number (含稅,TWD)
  estimated_delivery: string (ISO 8601)

二、Error 處理(最容易漏的)

必問 6 題

  1. 每個 endpoint 可能回什麼 HTTP status? 200 / 201 / 400 / 401 / 403 / 404 / 409 / 422 / 429 / 500
  2. 錯誤 payload 結構長什麼樣? 統一格式還是各 endpoint 不同?
  3. 錯誤碼有列表嗎? ERR_INSUFFICIENT_BALANCE 還是 error.code = 1042
  4. 錯誤訊息要不要多語系? 後端傳 code 還是已翻譯字串?
  5. 5xx 何時發生?什麼時候是 4xx vs 5xx 邊界?
  6. Retry 策略寫了嗎? 哪些錯能 retry,哪些不行(idempotency)

標準錯誤 payload 建議

{
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Your balance is too low for this purchase",
    "details": {
      "current_balance": 100,
      "required": 500,
      "currency": "TWD"
    },
    "trace_id": "abc-123-def",
    "doc_url": "https://docs.example.com/errors/INSUFFICIENT_BALANCE"
  }
}

code 給程式判斷、message 給開發 debug、details 給 UI 顯示、trace_id 給 support 查、doc_url 給開發者學習。統一格式整 API 共用

HTTP status 對照(常被搞混)

Status 用法 反例
400 Bad Request Request 格式錯(JSON 壞、missing required) ❌ 拿來表達「業務邏輯失敗」
401 Unauthorized 沒帶 token / token 無效 ❌ 拿來表達「沒權限」
403 Forbidden 有身份但沒權限 ❌ 拿來表達「token 過期」
404 Not Found 資源不存在 ❌ 拿來代替 403(怕資訊洩漏可以)
409 Conflict 業務狀態衝突(重複建立、狀態不允許) ❌ 用 400 表達
422 Unprocessable Schema 對但業務驗證 fail ❌ 用 400 表達
429 Too Many Rate limit
500 Internal Server bug ❌ 業務邏輯錯用 500

QA 在 spec review 看到所有錯誤都用 400 → 舉手問清楚

三、Auth 與授權

必問 5 題

  1. 認證方式? Bearer JWT / OAuth / API key / Session cookie
  2. Token 怎麼取得?怎麼 refresh? TTL?
  3. 每個 endpoint 需要什麼 scope / role?
  4. 多租戶隔離怎麼做? Tenant ID 在 token、URL、還是 header?
  5. Service-to-service 怎麼認證? 跟使用者 API 一樣 token 嗎?

常見漏洞

  • Token 過期沒明確錯誤:401 但沒告訴你是 token 過期還是無效 → 客戶端不知道要 refresh 還是重新登入
  • 403 vs 404 混用:為了防資訊洩漏(不告訴你「資源存在但你沒權限」),有時 spec 寫 403、有時 404 → 要統一
  • 跨租戶資料GET /users/{id} 沒驗 caller 跟 user 同租戶 → 嚴重資安

四、Versioning 與 Breaking Change

必問 5 題

  1. 版本怎麼標? URL(/v1/)、Header(API-Version: 2)、Accept(application/vnd.example.v1+json
  2. 什麼算 breaking change? 改欄位型別、刪欄位、改 enum 值、改錯誤碼
  3. Breaking change 的廢棄流程? 多久 deprecation period?怎麼通知?
  4. Backward compatibility 保證多久?
  5. 新欄位怎麼加? 預設值?optional?

Non-breaking vs Breaking

flowchart TD
    Change["API 改動"] --> Q1{加新欄位?}
    Q1 -->|是| OK1["✅ Non-breaking<br>客戶端可忽略"]
    Q1 -->|否| Q2{改欄位型別<br>或 enum 值?}
    Q2 -->|是| BAD1["❌ Breaking<br>需 deprecate"]
    Q2 -->|否| Q3{刪欄位?}
    Q3 -->|是| BAD2["❌ Breaking<br>需 deprecate"]
    Q3 -->|否| Q4{改錯誤碼?}
    Q4 -->|是| BAD3["⚠️ 可能 Breaking<br>看客戶端是否依賴"]
    Q4 -->|否| OK2["✅ 多半 Safe"]

    style OK1 fill:#10b981,color:#fff
    style OK2 fill:#10b981,color:#fff
    style BAD1 fill:#ef4444,color:#fff
    style BAD2 fill:#ef4444,color:#fff
    style BAD3 fill:#f59e0b,color:#fff

Deprecation 標準流程

v1: 上線
v2: 上線(v1 仍可用,response 加 Deprecation header)
v1: 6 個月後正式下架

API spec 沒寫 deprecation policy = 等於沒有 versioning。

五、Performance(Rate limit / Pagination / Cache)

必問 5 題

  1. 有 rate limit 嗎? 多少 req / sec / user / IP?回什麼 429 payload?
  2. List endpoint 有分頁嗎? Cursor-based 還 offset?最大 page size?
  3. Cache header 怎麼設? Cache-ControlETagLast-Modified
  4. 大檔案上傳 / 下載? Streaming?multipart?S3 pre-signed URL?
  5. 同步 vs 非同步? Long-running task 要不要拆 job + status endpoint?

Pagination 設計差異

方式 優點 缺點
Offset (?page=2&size=20) 簡單、可跳頁 資料新增時頁面會位移
Cursor (?after=abc&size=20) 穩定、適合 infinite scroll 不能跳頁
Token 後端可控、cursor 加密 客戶端不能解

建議:使用者 feed 用 cursor、後台管理頁用 offset。spec 沒寫的話 → 追問。

六、Data / Idempotency

必問 4 題

  1. POST 是 idempotent 嗎? 同 payload 打兩次會建兩筆?怎麼防止?
  2. 時區處理? API 用 UTC 還 local?輸入接受哪些格式?
  3. 敏感資料? Email、phone、身分證 — masked 還是明碼?日誌會印嗎?
  4. 欄位 nullable 規則? null vs missing vs empty string

Idempotency 實作標準

POST /orders
Headers:
  Idempotency-Key: <UUID>  # 客戶端產生

Server 行為:
  - 第一次:建單、儲存 (Idempotency-Key, order_id)
  - 重複請求:回傳一樣的 order_id、不重複建單
  - Key TTL:24 hr(避免無限儲存)

spec 沒寫 Idempotency-Key 流程 → POST 重複扣款是時間問題。

完整 30 題 checklist 一覽

打勾用,會議前 20 分鐘掃完:

Contract

  • [ ] 每個 endpoint 的 HTTP method 對嗎?
  • [ ] Path 符合 REST 慣例(資源 / 動作分離)?
  • [ ] Request schema 含每欄型別、必填、長度、格式?
  • [ ] Response schema 含 success / error 兩種?
  • [ ] 巢狀物件結構合理?

Error

  • [ ] 列出每個 endpoint 可能的 HTTP status?
  • [ ] 錯誤 payload 統一格式?
  • [ ] 錯誤碼有完整列表?
  • [ ] 錯誤訊息多語系策略?
  • [ ] 4xx vs 5xx 邊界清楚?
  • [ ] Retry 策略寫了嗎?

Auth

  • [ ] 認證方式明確?
  • [ ] Token 取得 / refresh 流程?
  • [ ] Endpoint scope / role 列表?
  • [ ] 多租戶隔離方式?
  • [ ] Service-to-service auth?

Versioning

  • [ ] 版本標示方式?
  • [ ] Breaking change 定義?
  • [ ] Deprecation period?
  • [ ] Backward compatibility 保證?
  • [ ] 新欄位加法?

Performance

  • [ ] Rate limit 數字?
  • [ ] List 分頁方式?
  • [ ] Cache header 策略?
  • [ ] 大檔案處理?
  • [ ] 同步 vs 非同步?

Data

  • [ ] POST idempotent?
  • [ ] 時區處理?
  • [ ] 敏感資料是否 masked?
  • [ ] Nullable 規則?

用 OpenAPI / Swagger 強化

API spec 用 OpenAPI 3.0 寫的話,很多面向有結構化欄位強制你填:

  • required — 必填
  • enum — 限制值
  • format — date-time、email、uri
  • pattern — regex
  • minimum/maximumminLength/maxLength

QA 看 OpenAPI spec 的優勢

  1. openapi-generator 自動生 client → 對 contract
  2. Dredd / Schemathesis 自動測 contract → 抓 spec 與實作差異
  3. UI(Swagger UI / Redoc)讓 PM / 前端都能 review

沒用 OpenAPI 的話,這 30 題人工問

反模式(API spec 警訊)

  1. 「依前端需求調整」 — 沒設計、見步行步、最後變亂麻
  2. 「跟 v1 一樣」 — v1 漏的東西繼承到 v2
  3. 「等開發完再寫 doc」 — 永遠不會寫
  4. 「Postman collection 就是文件」 — Postman 不是 contract spec
  5. 錯誤碼用整數error_code: 1042 整數沒可讀性、難維護
  6. 欄位命名混亂user_id / userId / uid 三種寫法都出現
  7. 時間用 unix timestamp — 客戶端跨時區算錯,應該用 ISO 8601 string

工作流建議

flowchart LR
    A[PM 寫 PRD] --> B[Backend tech<br>寫 API spec]
    B --> C{QA 用本<br>checklist review}
    C -->|有問題| D[列澄清問題<br>會議討論]
    D --> B
    C -->|清晰| E[轉 OpenAPI<br>檔案]
    E --> F[前後端<br>同時開發]
    E --> G[QA 寫<br>contract test]
    F --> H[Integration]
    G --> H

    style A fill:#06b6d4,color:#fff
    style B fill:#06b6d4,color:#fff
    style C fill:#f59e0b,color:#fff
    style D fill:#ef4444,color:#fff
    style E fill:#10b981,color:#fff
    style F fill:#a855f7,color:#fff
    style G fill:#a855f7,color:#fff
    style H fill:#10b981,color:#fff

關鍵:QA review 不過 → 退回 backend 補。前後端不該並行開發到一半才發現 contract 沒對齊。

最後

API spec review 是 QA 影響力放大器 — 一個漏洞在 spec 階段抓到,省下後續前端、後端、客戶端 SDK、文件全要改的時間。30 題打勾掃過、會議裡用問題形式問 PM/Backend,省的不只你的時間