エピローグ — API戦略とDeveloper Experience
6ヶ月後のナツミ
「お疲れ様でした、ナツミさん」
槙島CTOがビールを差し出した。Livlyのモバイルアプリがリリースされて3ヶ月。iOS・Androidあわせてダウンロード10万件を突破した。
「正直、最初はAPIって何が違うのか全然わかってませんでした」
「今は?」
「APIはビジネスの血管だと思います。設計が悪いとどこかで詰まる。良い設計は成長を支えてくれる」
槙島CTOは頷いた。「それが理解できたら、次はDeveloper Experienceの話をしよう」
Developer Experienceとは
DX(Developer Experience)とは、APIを使う開発者が感じる体験の質。
「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}円/月"
end2. エラーが教えてくれる
// 悪いエラーメッセージ
{
"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
endRubyクライアント(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
endAPIの成長と後方互換性
セマンティックバージョニング
MAJOR.MINOR.PATCH
1 .2 .3
MAJOR: 破壊的変更(フィールド削除、型変更)
MINOR: 後方互換の機能追加(フィールド追加)
PATCH: バグ修正
廃止プロセス
# 廃止予定フィールドの処理
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
endAWSでの監視ダッシュボード
重要なメトリクス
# 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ヶ月の旅を振り返って、ナツミは学んだことをまとめた。
技術的な学び
- REST設計の原則 — URLはリソース、操作はHTTPメソッド
- シリアライゼーション —
render json: @modelは最初の一歩に過ぎない - 認証・認可 — AuthNとAuthZは別物。JWTとOAuth2の使い分け
- エラー設計 — RFC 7807準拠のエラーは開発者への思いやり
- パフォーマンス — N+1、キャッシュ、CDNの3段階最適化
- GraphQL — Over-fetchingを解決するが、学習コストを忘れずに
- gRPC — 内部通信の最適解。protobufで型安全に
- テスト — リクエストスペック、VCR、Contract Testingの三本柱
- ドキュメント — テストとドキュメントの一体化(rswag)
- 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チームの指針となった。
ナツミの物語はここで一区切り。あなたの物語はこれから始まる。