プロトコル

スキーマファーストとは?OpenAPI起点の並行開発と破壊的変更検知を解説

フロントエンドとバックエンドが同時に動き出すとき、最初に決まっていないと詰まるのは「どんなJSONが返るか」です。この取り決めを実装より先に機械可読な定義として固定し、コードもドキュメントもテストもそこから導く進め方をスキーマファーストと呼びます。扱う範囲は、スキーマの置き場と所有者、生成物をどこまでリポジトリへ入れるか、モックと検証プロキシで乖離を止める手順、破壊的変更をCIで遮断する仕組み、そして採用を見送ってよい場面までです。定義そのものの書き方はJSON Schemaによる構造検証の記法に譲り、ここでは開発の進め方に絞ります。

まとめ|先に決める4項目とスキーマファーストが効く境界

着手前に決めるのは4つです。スキーマの置き場と、変更をマージできる所有者。生成物をコミットするかしないか。CIに置く検査の並び(規約検査→再生成差分→破壊的変更判定)。そして破壊的変更を出すと決めたときの畳み方。この4つが文書として存在すれば、スキーマは「更新されない仕様書」に退化しません。逆に1つでも欠けると、半年後には実装だけが進み、定義ファイルは参照されなくなります。

効くのは、作り手と使い手が別チームで、境界の寿命が長く、複数の実装言語が並ぶ場面。3つとも当てはまるなら迷わず採用してよいでしょう。一方、1人が両側を書いていて仕様が日単位で動く探索段階では、定義の更新が純粋な作業増になります。全体を一律に決める必要はありません。外部へ公開する境界だけ先に固定し、内側は後から追いつかせる部分適用が現実的な着地点になります。

スキーマファーストとは|仕様を先に固定してコードを後から合わせる設計

唯一の正はスキーマ|実装とドキュメントの二重管理をやめる考え方

スキーマファーストの中心にあるのは、正しさの置き場を1か所に決めるという判断です。OpenAPIやGraphQLのSDL、あるいは.protoで書かれた定義ファイルが唯一の正であり、サーバの実装もクライアントの型もドキュメントもテストも、そこから導出されるか、そこと照合されます。

この配置が効く理由は、二重管理が消える点にあります。実装とは別にAPI仕様書を書く体制では更新が2回必要になり、片方が必ず遅れ、遅れたほうを信じた誰かが手戻りを踏む。定義を先に置けば、更新箇所は1つに畳まれます。

コードファーストとの違い|生成の向きが逆になると何が変わるか

コードファーストは、実装のアノテーションやコメントからOpenAPI文書を出力する進め方です。生成の向きが逆になるだけに見えますが、実務上の差は小さくありません。

いちばん大きいのは、合意の時点が変わることでしょう。コードファーストでは、仕様が確定するのはサーバの実装が終わった後です。クライアント側は待つか、想像で先行実装して後から直すことになります。スキーマファーストなら、定義ファイルのレビューが終わった瞬間に両側が着手できます。

もう1つは書ける範囲です。実装から出力する方式では、フレームワークが表現できない仕様(複数レスポンス例、詳細なバリデーション、非標準のメディアタイプ)が落ちます。定義から書けば、その制約は生じません。

スキーマ駆動開発やAPIファーストとの呼び分けと指す範囲の差

日本語圏では「スキーマ駆動開発」がほぼ同義で使われます。「デザインファースト」も同じ進め方を指す語です。少しずれるのが「APIファースト」で、こちらは組織の方針を指す言葉として使われることが多く、社内機能をまずAPIとして切り出すという設計思想まで含みます。

対象とする境界の違いにも触れておきます。本記事が扱うのはリクエストとレスポンスが1往復するAPIの境界です。メッセージが非同期に流れる境界の記述には別の仕様があり、AsyncAPIとOpenAPIの違いとイベント駆動APIの設計にまとめてあります。考え方は共通ですが、検査に使う道具が変わる点だけ押さえてください。

スキーマの置き場と所有者|単一リポジトリと専用リポジトリの選び分け

置き場の判断基準|配布先が社内に閉じるか外部へ及ぶかで分ける

置き場は2択です。サーバ実装と同じリポジトリに置くか、定義だけの専用リポジトリを立てるか。判断の軸は、そのスキーマを参照する主体が何チームあるかに置きます。

参照が自社の2〜3チームに閉じるなら、サーバ実装と同居させて構いません。同じプルリクエストで定義と実装が動くため、レビューが1回で済みます。専用リポジトリへ切り出すのは、外部の取引先へ配布する、複数のサーバ実装が同じ定義を共有する、定義の変更に実装とは別の承認を通したい、のいずれかが立ったときです。

ファイル分割と$refの粒度|1本のYAMLで持ち続けない境目

最初は1本のYAMLで足ります。分割を考える目安は、ファイルが1,500行を超えたあたり、あるいは同じスキーマ定義を3か所以上から参照し始めたあたりでしょう。$ref でコンポーネントを外部ファイルへ切り出し、パス単位でもファイルを分けます。

openapi/
  openapi.yaml          # ルート。paths を $ref で参照
  paths/invoices.yaml
  components/schemas/Invoice.yaml
  components/responses/Error.yaml

注意点は、道具によって外部参照の解決範囲が違うことです。生成器やモックへ渡す前に1本へ束ねる手順をビルドへ入れておくと、道具ごとの差に振り回されずに済みます。

Spectralでの規約検査|命名や必須項目の逸脱をCIで機械的に弾く

定義ファイルは自然言語より厳密とはいえ、書き手ごとの癖は残ります。operationIdの命名、説明文の有無、エラー応答の網羅。これらは目視レビューに任せると必ず抜けます。

Spectralは、JSONとYAMLに対して規約を機械的に当てるリンターです。公式リポジトリによれば、OpenAPI v3.1・v3.0・v2.0のほか、Arazzo v1.0とAsyncAPI v2.xにも対応します(2026年8月時点)。内蔵ルールセットとして spectral:oasspectral:asyncapispectral:arazzo を備え、.spectral.yaml から extends して自社の規約を足す形になります。

extends: [[spectral:oas, recommended]]
rules:
  operation-operationId: error
  operation-tag-defined: error
  my-error-response-required:
    given: $.paths[*][*].responses
    then:
      field: '400'
      function: truthy

組み込み関数はパターン検査・パラメータ検証・アルファベット順・文字数制限・キー存在の確認などを備え、足りなければ独自関数も定義できます。まずは内蔵ルールセットをそのまま当て、警告が多すぎる規則だけ個別に落としていく順序が扱いやすいでしょう。

生成物の扱い|コードジェネレータの出力をコミットするかを決める

生成コードのコミット可否|差分レビューと再現性のどちらを取るか

スキーマファーストで最初に揉めるのが、生成された型やクライアントをリポジトリへ入れるかどうかです。どちらにも理由があります。

コミットする側の利点は、レビューで差分が見えることです。スキーマを1行変えた結果、クライアントの型が何十箇所も変わったという事実がプルリクエストに現れます。開発者はビルド前に生成器を走らせる必要がなく、新しく参加した人がクローン直後に読める状態にもなります。

コミットしない側の利点は、生成器のバージョン差による混乱が起きない点でしょう。手元と CI で出力が食い違っても、成果物が残らないため衝突しません。リポジトリも小さく保てます。

判断はこう切り分けます。生成物を他チームや社外が直接読む(配布物として扱う)ならコミットする。自チーム内でビルド時に消費するだけなら、生成器のバージョンをロックしたうえでコミットしない。迷ったらコミットする側へ倒すと事故の発見が早まります。

生成範囲の決め方|型定義だけに絞るかサーバスタブまで踏み込むか

生成の深さも決めておきます。クライアント側は型定義とリクエスト関数まで生成して問題が出にくい領域です。サーバ側は、ルーティングとバリデーションの雛形(スタブ)まで生成するか、型だけ受け取って実装は手で書くかで運用が変わります。

スタブまで生成すると、仕様に無いエンドポイントを実装できなくなるため、ズレの発生源が構造的に消えます。反面、生成器の作法にフレームワーク側を合わせる必要が出て、既存プロジェクトへの後付けは難しくなるでしょう。GraphQL Code Generatorのclient preset設定のように、生成範囲を細かく切り替えられる道具なら、型だけから始めて段階的に広げられます。

CIでの差分検知|再生成して差分が出たらビルドを止める仕組み

生成物をコミットする方式を選んだなら、コミット漏れを人の注意力に任せてはいけません。CIで再生成し、差分が残ったら落とします。

- run: npm run generate:api
- run: git diff --exit-code -- src/generated

この2行があるだけで、「スキーマは直したが生成し忘れた」プルリクエストは通らなくなります。生成器のバージョンはロックファイルかコンテナイメージで固定してください。固定しないと、CIの実行日によって差分が出たり出なかったりする不安定な検査になります。

並行開発の進め方|モックサーバと検証プロキシで乖離を止める手順

モックサーバの立て方|静的な例と動的生成のどちらを使うか決める

スキーマファーストの見返りが最も分かりやすいのが、サーバ実装を待たずにクライアントを進められる点です。Prismは、公式リポジトリによればOpenAPI 2・3.0・3.1とPostman Collectionをそのままモックサーバへ変換します(2026年8月時点)。

prism mock openapi.yaml           # 静的: 仕様内の example を返す
prism mock -d openapi.yaml        # 動的: フェイカーで値を生成

静的方式は仕様に書かれた example をそのまま返すため、画面の表示確認に向きます。動的方式はフェイカーライブラリで値を作るので、文字数の揺れや境界値に強い画面かを見るのに向くでしょう。実務では静的を既定にし、一覧画面の見た目検証だけ動的へ切り替える使い分けが扱いやすくなります。Prism以外の選択肢を含めた方式ごとの比較と、モックが本番からずれたときの検知はAPIモッキングによるモックサーバーの方式選定と契約ドリフト対策にまとめています。

なお、静的方式で意味のある応答を返すには、スキーマ側に example を書き込む手間が要ります。省くと中身が空のモックになり、画面側のレビューが進みません。定義を書く時点で例も添えると規約に含めてください。

検証プロキシの当て方|実装の応答が仕様どおりかを通信で確かめる

並行開発の後半で問題になるのは、実装が仕様から静かにずれることです。Prismにはモックのほかに検証プロキシのモードがあり、実際のHTTP通信をOpenAPI文書と突き合わせて検証します。

prism proxy openapi.yaml http://localhost:3000

クライアントの向き先をプロキシに変えると、リクエストとレスポンスの双方が仕様と照合され、逸脱が記録されます。E2Eテストの実行時にこのプロキシを挟んでおくと、テストが通っているのに仕様と食い違っている状態を拾えるでしょう。動作にはNode.js 18.20.1以上が要ります。

契約テストへの接続|モックの正しさを消費者側の期待で担保する

モックと検証プロキシで守れるのは「仕様と実装の一致」までです。仕様そのものが消費者の必要を満たしているかは、別の仕組みで確かめます。消費者側が期待する応答を記述し、提供側の実装に対して再生する消費者駆動契約によるテストが、その役目を担います。

両者は排他ではありません。スキーマは境界の形を固定し、契約テストは境界の使われ方を固定する。前者だけだと使われていないフィールドが残り続け、後者だけだと型の網羅が緩みます。CIでは、規約検査・再生成差分・契約テストの順に並べておくと、失敗の原因が切り分けやすくなるでしょう。

破壊的変更の検知|oasdiffとbuf breakingをCIに置いて差分を止める

oasdiffの使い方|breakingとchangelogを段階的に当てる

スキーマを唯一の正にした以上、そこへの変更は全消費者に波及します。人が目視で「これは壊れるか」を判定し続けるのは無理があるため、機械へ渡します。

oasdiffは、OpenAPI仕様を比較して破壊的変更を検出するCLIです。公式リポジトリによれば、サブコマンドは breaking(既存クライアントを壊す変更のみ)・changelog(消費者へ影響する変更を人間可読で)・diff(HTML・JSON・YAML等の機械可読出力)・summary(件数)・validate(RFC違反の検査)が用意されています(2026年8月時点)。

oasdiff breaking base.yaml revision.yaml --fail-on ERR
oasdiff changelog base.yaml revision.yaml

判定の精度にも触れておきます。エンドポイントの照合ではパスパラメータの改名や重複を考慮し、スキーマ側は allOf をマージした結果と nullability の変化まで踏まえて判定されます。導入時は changelog のコメント運用から始め、傾向を把握してから breaking でビルドを落とす順が無難でしょう。

buf breakingの4カテゴリ|通信互換と生成コード互換を分けて守る

gRPCを使う境界では、同じ役目をbufが担います。ここで見習いたいのは、互換という一語を分解している点です。protoファイルの書き方と採用判断で扱った定義に対し、buf breaking は現行スキーマを過去版と比較して非互換を検出します。

カテゴリ 守る対象 壊れる例
FILE 生成コードの互換 フィールド名の改名
PACKAGE パッケージ単位の互換 型の別パッケージ移動
WIRE_JSON JSON表現の互換 JSON名の変更
WIRE バイナリ表現の互換 フィールド型の変更

フィールドを改名すると生成コードは壊れますが、バイナリ表現は保たれます。型を変えれば直列化済みのデータまで壊れる。この粒度差をカテゴリとして持つおかげで、社内利用のみのサービスは WIRE だけ守る、公開APIは FILE まで守る、といった強度の使い分けができます。比較元はGitブランチやBSRモジュール、ローカルディレクトリなどを指定できるため、mainブランチを基準にした検査をCIへ素直に置けるでしょう。

バージョニングとの接続|壊すと決めたときの畳み方を先に決める

検知はゴールではありません。壊す判断そのものは残ります。判定で赤が出たとき、取れる選択肢は3つです。変更を取り下げる。互換を保つ形へ設計し直す(新フィールドを追加し旧フィールドは非推奨として残す)。破壊的変更として版を切る。

3つ目を選ぶなら、切り方と終了期日の決め方を先に文書化しておきます。セマンティックバージョニングとAPIバージョニングの運用設計で整理したとおり、版を分けた瞬間から旧版の保守コストが積み上がる点は見落とされがちです。並走期間を無期限にしないでください。

採用の境界|スキーマファーストが向かない場面と部分適用の設計

採用してよい3条件|境界が複数チームにまたがり寿命が長いこと

次の3つが揃うなら、初期コストを払う価値があります。第一に、APIの作り手と使い手が別チームであること。合意を先に固定する行為が、そのままコミュニケーション費用の削減になります。第二に、その境界が年単位で生き続ける見込みがあること。第三に、消費者側の実装言語が複数あること。型の生成が言語ごとに効きます。

3条件のうち2つ止まりでも、外部公開APIなら採用してよいでしょう。仕様が文書として存在すること自体が、取引先との合意の根拠になるためです。

見送ってよい場面|作り手と使い手が同一で仕様が日々動く初期段階

逆に、次の場面では素直に見送ります。1人ないし1チームが両側を書いていて、画面を作りながらAPIの形が日単位で変わる探索段階。この状況で定義ファイルを先に直す運用を入れると、1日に何度も同じ変更を2か所へ書くことになります。

社内限定で消費者が1つ、寿命が数か月の管理画面向けAPIも同様で、実装から仕様を出すコードファーストで足ります。判断の分かれ目は、仕様変更1回あたりの調整相手が何人いるかに置くと迷いません。相手が自分だけなら、先に固定する意味は薄いでしょう。

部分適用の設計|外部へ公開する境界だけスキーマを先に固定する

全体を一律に決める必要はありません。システムの中に、外部や別チームへ露出する境界と、内部で閉じる境界が混在するのが普通です。前者だけスキーマファーストにし、後者はコードファーストのまま残す構成が、実務では最も費用対効果が読めます。

線引きの目安は、その境界を跨ぐ変更に別チームの合意が要るかどうか。要るなら定義を先に置き、要らないなら実装から出します。イベントが流れる境界も同じ考え方で切り分けられますが、そちらは互換モードを持つ基盤側で担保する手もあり、スキーマレジストリの互換性モードとスキーマ進化が判断材料になるでしょう。

受託開発での取り決め|スキーマを納品物に含めて変更管理を定める

納品物への組み込み|仕様書ではなく検査できる定義として引き渡す

受託でAPIを作る場合、成果物一覧に「API仕様書」と書かれることがあります。ここを「機械可読なAPI定義ファイル一式(OpenAPI文書、規約検査の設定、破壊的変更検査の設定)」と書き換えるだけで、引き渡し後の価値がまるで変わります。

紙の仕様書は、引き渡した翌月から実装とずれ始めます。定義ファイルと検査設定が揃っていれば、発注者側が誰に依頼しても、ずれた瞬間にCIが落ちる状態を維持できるでしょう。検査の設定ファイルまで納品物へ含めるところが要点です。

変更手続きの明文化|破壊的変更の申請と合意の経路を契約前に敷く

もう1つ決めておくのが、スキーマを変更する手続きです。誰がプルリクエストを出し、誰が承認するのか。破壊的変更と判定されたとき、誰の合意があれば通せるのか。この2点を着手前に文言として確定させます。

決めていないと、リリース直前に「この項目を必須から任意へ変えたい」という要求が出た際、判断できる人がその場にいない事態になります。判定基準は機械が持っているので、あとは承認の経路だけを人が用意すれば足ります。

引き渡し後の運用体制|スキーマの所有者と検査の維持を誰が担うか

引き渡し後にいちばん先に朽ちるのは、CIの検査です。生成器のバージョンが古くなり、警告が増え、誰かが検査を外す。半年後にはスキーマだけが取り残されます。

これを防ぐには、保守契約に「定義ファイルと検査設定の維持」を明示的な項目として入れるか、発注者側で所有者を決めておく必要があるでしょう。実務では、定義ファイルの所有を発注者側に置き、検査設定の更新だけ保守範囲に含める形へ寄せると責任が明確になります。API設計から生成・検査のCI組み込み、引き渡し後の運用設計までの相談はAPI開発・システム連携で受け付けています。

よくある質問

スキーマファーストとスキーマ駆動開発は違うものですか?

ほぼ同じ進め方を指す言葉です。日本語圏では「スキーマ駆動開発」、英語圏では design-first や schema-first が使われます。厳密な定義の差はなく、どちらも実装より先に機械可読な定義を置く点で共通しています。少し離れるのが「APIファースト」で、こちらは社内機能をまずAPIとして切り出すという組織方針まで含む語として使われる場面が多く、開発手順だけを指すとは限りません。

OpenAPIはどのバージョンから書き始めるべきですか?

新規なら3.1系以降を選んでください。OpenAPI Initiative公式によれば、最新の3.x系は2025年9月に公開された3.2.0で、タグの階層化、冪等な問い合わせ用の query メソッドと非標準メソッド向けの additionalOperations、server-sent events や JSON Lines といったストリーミング向けメディアタイプと itemSchema、OAuth 2.0のデバイス認可フローなどが加わっています(2026年8月時点)。ただし対応状況は道具側で差があるため、生成器・モック・リンターが読める版に合わせるのが実務的な選び方になります。

生成されたクライアントコードは手で直してよいですか?

直さないでください。次回の生成で消えるうえ、消えたことに気づける仕組みがありません。振る舞いを変えたいなら、生成物を包む薄い層を自分の手で書き、そこへ手当てを寄せます。どうしても生成物そのものを変えたい場合は、生成器のテンプレートを差し替える方法を先に検討してください。手直しが必要な箇所が増えてきたら、それは生成範囲を狭める合図と読めます。

既存のコードファースト構成から途中で移行できますか?

できます。手順は、まず現行の実装からOpenAPI文書を出力し、それを起点のスキーマとして専用の場所へ確定させる。次にCIで破壊的変更の検査だけを先に入れ、しばらく警告として運用する。最後に生成の向きを反転させ、スキーマ側の変更を起点にする、という順です。一度に反転させると全エンドポイントのレビューが同時に発生するため、外部公開している境界から順に移すのが安全でしょう。

GraphQLやgRPCでもスキーマファーストと言いますか?

言います。GraphQLはSDL、gRPCは .proto が定義ファイルにあたり、そこから型やスタブを生成する流れは同じです。むしろこの2つは定義を書かないと動かせない設計なので、構造的にスキーマファーストへ寄っています。差が出るのは破壊的変更の検査で、OpenAPIならoasdiff、Protocol Buffersならbuf breakingというように使う道具が変わります。考え方を共通化し、検査だけ境界ごとに揃えてください。

関連記事

資料請求

RELATED POSTS 関連記事