mybook

エピローグ — API戦略とDeveloper Experience

6ヶ月後のナツミ

「お疲れ様でした、ナツミさん」

槙島CTOがビールを差し出した。Livlyのモバイルアプリがリリースされて3ヶ月。iOS・Androidあわせてダウンロード10万件を突破した。

「正直、最初はAPIって何が違うのか全然わかってませんでした」

「今は?」

「APIはビジネスの血管だと思います。設計が悪いとどこかで詰まる。良い設計は成長を支えてくれる」

槙島CTOは頷いた。「それが理解できたら、次はDeveloper Experienceの話をしよう」


Developer Experienceとは

DX(Developer Experience)とは、APIを使う開発者が感じる体験の質。

Loading diagram...

「Stripe、Twilioを使ったことある?」槙島CTOが尋ねた。

「はい。本当に使いやすかったです」

「なぜか考えたことある?ドキュメントが充実していて、エラーメッセージが丁寧で、サンドボックスが用意されていて、コピペできるコード例がある。APIの機能以上に使われ続ける理由がある」


優れたDXを構成する要素

1. はじめの5分でHello Worldができる

# curl一発でAPIを試せる
curl -H "Authorization: Bearer YOUR_TOKEN" \
     https://api.livly.jp/v1/properties | jq .
# Rubyクライアント(SDK)が公開されている
require 'livly'
 
Livly.api_key = 'your_api_key'
 
properties = Livly::Property.list(
  prefecture: '東京都',
  per_page: 5
)
 
properties.each do |p|
  puts "#{p.name}: #{p.price}円/月"
end

2. エラーが教えてくれる

// 悪いエラーメッセージ
{
  "error": "invalid_request"
}
 
// 良いエラーメッセージ
{
  "type": "https://api.livly.jp/errors/validation_failed",
  "title": "Validation Failed",
  "status": 422,
  "detail": "入力内容に誤りがあります",
  "errors": [
    {
      "field": "price",
      "message": "は0以上の値にしてください",
      "code": "greater_than_or_equal_to",
      "docs": "https://docs.livly.jp/api/v1/properties#price"
    }
  ]
}

3. サンドボックス環境がある

# 本番とサンドボックスを切り替えるだけ
Livly.configure do |config|
  config.api_key = ENV['LIVLY_API_KEY']
  config.environment = :sandbox  # または :production
end

Rubyクライアント(SDK)の設計

# lib/livly.rb
module Livly
  class << self
    attr_accessor :api_key, :environment, :timeout
 
    def configure
      yield self
    end
 
    def base_url
      case environment
      when :sandbox    then 'https://api.sandbox.livly.jp/v1'
      when :production then 'https://api.livly.jp/v1'
      else                  'https://api.livly.jp/v1'
      end
    end
  end
 
  # デフォルト設定
  self.environment = :production
  self.timeout = 30
end
# lib/livly/property.rb
module Livly
  class Property
    attr_reader :id, :name, :price, :area, :location, :created_at
 
    def self.list(params = {})
      response = Client.get('/properties', params)
      response['data'].map { |d| new(d['attributes'].merge(id: d['id'])) }
    end
 
    def self.find(id)
      response = Client.get("/properties/#{id}")
      new(response['data']['attributes'].merge(id: response['data']['id']))
    end
 
    def self.create(attributes)
      response = Client.post('/properties', { property: attributes })
      new(response['data']['attributes'].merge(id: response['data']['id']))
    end
 
    def initialize(attributes = {})
      @id = attributes[:id] || attributes['id']
      @name = attributes[:name] || attributes['name']
      @price = attributes[:price] || attributes['price']
      @area = attributes[:area] || attributes['area']
      @location = attributes[:location] || attributes['location']
    end
  end
end

APIの成長と後方互換性

セマンティックバージョニング

MAJOR.MINOR.PATCH
1     .2    .3

MAJOR: 破壊的変更(フィールド削除、型変更)
MINOR: 後方互換の機能追加(フィールド追加)
PATCH: バグ修正
Loading diagram...

廃止プロセス

# 廃止予定フィールドの処理
module Api
  module V1
    class PropertiesController < Api::V1::BaseController
      def show
        @property = Property.find(params[:id])
        response = PropertySerializer.new(@property).serializable_hash
 
        # 廃止予定フィールドを残しつつ警告ヘッダーを付ける
        response[:data][:attributes][:address] = @property.full_address  # 旧フィールド
        response.headers['Warning'] = '299 api.livly.jp "address field is deprecated, use location instead"'
 
        render json: response
      end
    end
  end
end

監視とオブザーバビリティ

本番APIを安定して運用するための監視体制。

# config/initializers/datadog.rb
Datadog.configure do |c|
  c.service = 'livly-api'
  c.env = Rails.env
 
  c.tracing.instrument :rails
  c.tracing.instrument :active_record
  c.tracing.instrument :http
 
  c.runtime_metrics.enabled = true
end

AWSでの監視ダッシュボード

Loading diagram...

重要なメトリクス

# SLI(Service Level Indicator)の定義
# 以下を継続的に計測する
 
# 1. 可用性(Availability)
# 目標: 99.9%(月間ダウンタイム43分以内)
# 計測: successful_requests / total_requests
 
# 2. レイテンシ(Latency)
# 目標: p99 < 500ms, p50 < 100ms
# 計測: CloudWatch / Datadog でパーセンタイル計測
 
# 3. エラー率(Error Rate)
# 目標: 5xxエラー率 < 0.1%
# 計測: 5xx_count / total_count
 
# 4. スループット(Throughput)
# 計測: 1分あたりのリクエスト数(RPS)

APIエコシステムの構築

# webhookでリアルタイム通知
class WebhookDeliveryService
  def self.deliver(organization, event_type, payload)
    webhooks = organization.webhooks.active.where(
      'event_types @> ARRAY[?]::varchar[]', [event_type]
    )
 
    webhooks.each do |webhook|
      WebhookDeliveryJob.perform_later(webhook.id, event_type, payload)
    end
  end
end
 
class WebhookDeliveryJob < ApplicationJob
  retry_on StandardError, wait: :polynomially_longer, attempts: 5
 
  def perform(webhook_id, event_type, payload)
    webhook = Webhook.find(webhook_id)
    signature = compute_signature(webhook.secret, payload)
 
    response = HTTParty.post(webhook.url,
      body: payload.to_json,
      headers: {
        'Content-Type' => 'application/json',
        'X-Livly-Signature' => "sha256=#{signature}",
        'X-Livly-Event' => event_type,
        'X-Livly-Delivery' => SecureRandom.uuid
      },
      timeout: 10
    )
 
    webhook.deliveries.create!(
      event_type: event_type,
      response_status: response.code,
      success: response.success?
    )
  end
 
  private
 
  def compute_signature(secret, payload)
    OpenSSL::HMAC.hexdigest('SHA256', secret, payload.to_json)
  end
end

ナツミの成長と学び

6ヶ月の旅を振り返って、ナツミは学んだことをまとめた。

技術的な学び

  1. REST設計の原則 — URLはリソース、操作はHTTPメソッド
  2. シリアライゼーションrender json: @model は最初の一歩に過ぎない
  3. 認証・認可 — AuthNとAuthZは別物。JWTとOAuth2の使い分け
  4. エラー設計 — RFC 7807準拠のエラーは開発者への思いやり
  5. パフォーマンス — N+1、キャッシュ、CDNの3段階最適化
  6. GraphQL — Over-fetchingを解決するが、学習コストを忘れずに
  7. gRPC — 内部通信の最適解。protobufで型安全に
  8. テスト — リクエストスペック、VCR、Contract Testingの三本柱
  9. ドキュメント — テストとドキュメントの一体化(rswag)
  10. DX設計 — 使われるAPIは「すぐ動かせて」「エラーがわかりやすい」

マインドセットの変化

Before: 「APIはHTMLの代わりにJSONを返すもの」

After:  「APIはビジネス機能を外部に公開するインターフェース。
         設計の良し悪しがビジネスの成長速度を左右する」

INFO

最後に:どんなに優れた技術を使っても、APIの本質は「人と人をつなぐ」こと。iOSエンジニアの林さん、Androidの田中さん、外部パートナー——彼らが使いやすいAPIを作ること、それがAPIエンジニアの仕事の核心だとナツミは気づいた。


次のステップ

ナツミの旅はここで終わらない。次に学ぶべきテーマ:

テーマ学ぶこと
API Gateway高度活用Lambdaオーソライザー、ステージ変数、カナリアリリース
サービスメッシュIstio, AWS App Mesh によるgRPC間の通信制御
API設計パターンCQRS、BFF(Backend for Frontend)パターン
ゼロダウンタイムデプロイBlue-Greenデプロイ、データベースマイグレーション戦略
OpenAPIエコシステムコード生成、モックサーバー、契約テストの自動化

まとめ:API開発の実践知

この書籍で学んだことを一言で表すなら:

「良いAPIは、それを使う開発者への手紙だ」

  • 設計の意図が伝わるURL設計
  • 状態が明確なHTTPステータスコード
  • 原因がわかるエラーメッセージ
  • 試せるドキュメント
  • 頼れるSDK

これらすべては、会ったことのない開発者への敬意から生まれる。

ナツミはLivlyのAPI設計書の最初のページにこう書いた。

このAPIを使う人が、迷わず、早く、楽しく開発できるように

それがLivlyのAPIチームの指針となった。


ナツミの物語はここで一区切り。あなたの物語はこれから始まる。