openapi 定義を改変する変更をレビューする際の指摘根拠。特定のプログラミング言語・コード生成器・YAML 構文に依存しない抽象度で記す。概念境界・責務の見極め方は openapi 単体で判断せず、サーバ実装とクライアントの利用実態の両面から確認する。
ルールは原則であって絶対則ではない。永続化データ・外部 API との互換、業務上の確定した理由がある場合は逸脱しうる。逸脱する変更は、その理由をレビューで明示させる。
簡潔さより明確さを優先する。 短く曖昧な名前より、やや冗長でも一読で意味が取れる名前を選ぶ。背景知識のない読み手が解凍に要する認知コストが、入力短縮の利得を上回る。
以降の命名ルール(省略語・接頭辞・意味一致等)はこの原則の具体化。
省略語を作らない。 確立した規格語・業界標準語(URL, ID, JWT, QR 等)以外は、長くなっても単語を省略せず綴る。プロジェクト固有・社内固有の略語(クラウド基盤名・社内システム名・usr/txn/cfg のような断片)を識別子・パス・ヘッダ名・enum 値に使わない。
補完が効く現代では入力短縮の利得はほぼ無く、背景知識のない作業者の逆引きコストと業界標準語との衝突リスクだけが残る。
内部実装・開発経緯を API サーフェスに漏らさない。 採用クラウド名・採用 SDK 名・通信方式・「UI 改善版」のような経緯を、パス・スキーマ名に語句として埋め込まない(例: Article の刷新版を ArticleUiImproved/ArticleNew と名付ける)。意味を表す汎用語にし、世代差は明示的な versioning 戦略(URL の /v2、メディアタイプ、ヘッダ等)で表す。
経緯ベースの名前は実装変更で嘘になり、その実装を知らない読み手に意味が伝わらない。
名前と実際の値の意味を一致させる。 名前が示す概念と中身がずれていたら改名する(並び順を表す値を「ID」と呼ばない、識別子を「分類」と呼ばない、件数の閾値を ~Until と名付けて時刻と誤読させない — 例: バッジ表示の上限件数なら badgeCountUntil でなく maxBadgeCount)。
名前と意味の乖離は利用側が値を取り違える直接原因になる。
正しい英語を選ぶ。 REST 操作に口語的動詞を使わない(see→view)。母語由来の語彙を英語圏で別の意味になるまま持ち込まない(和製英語の直訳 appeal(訴求)→promotion など。母語が何であれ同型の問題は起きる)。形容詞を名詞位置に置かない(Hashed→Hash)。非文法的な真偽値名を作らない(isUse→isInUse)。
不自然な英語は意図を曖昧にし誤解を招く。
真偽値は意味的に正しい接頭辞を前置し、状態・経験・所有・可否を表し分ける。 is(現在の状態・属性)/ has(所有、または現在完了=過去に~した経験・~済み)/ can(可否)を意味で選ぶ。同じ概念でも軸が違えば使い分ける(過去に一度でも規約に同意したか=hasAccepted、現時点で最新版の規約に同意済みか=isAgreementUpToDate)。設定の ON/OFF 状態は能力・可否ではないので can でなく is(canPush→isPushNotificationEnabled)。全 boolean を is に強制しない。
接頭辞で真偽値であることと、状態か経験かの軸が名前から読み取れる。
真偽値・enum 名に誤読・真逆解釈の余地を残さない。 disableAutoUpdate(false が二重否定で読みづらい)、isMarketingEmail(「このメールが販促メールか」か「販促メール受信が ON か」か曖昧)のような名前を、isAutoUpdateEnabled / isMarketingEmailSubscribed のように具体化する。
配列プロパティ名は複数形にする。 自然な複数形を使い、不可算名詞・複数形が不自然な概念に限り ~List/~Items を使う。
名前から要素か集合かを判断できるようにする。
スキーマ名はリソース/概念名を基底にし、用途を表す接尾辞で統一する(Article、ArticleCreateRequest、…Response 等)。HTTP メソッド名そのものを型名に焼き込まない。operation の識別は operationId 側で扱う。
概念ベースの命名なら同一表現を複数 operation で再利用でき、transport の詳細が型名に漏れない。
「省略されうる(non-required)」と「値が null になりうる」を区別する。 省略可(キーが送られないことがある)は、オブジェクトの required にそのキーを含めないことで表す(parameter は required を false に)。値が null になりうるのは別概念で、許容する場合のみ明示する(OAS 3.0 は nullable、3.1 は型に null を含める)。両者を混同せず、「省略可」を表したいだけの箇所(query parameter 等)に null 許容を付けない。
両者の混同は利用側に余計な省略可能型 / 二重 unwrap を生ませる。default は意味付けの注釈であり、値の自動補完を保証しない。
null を許容するなら「実際に null が入るユースケース」を根拠とする。 認証必須経路など null が発生しえない値は非 null で定義する。「設計上の余裕」で安易に null 許容にしない。
null を許容するなら null の意味を description に明記する。 未設定 / 非該当 / 不明 / 取得対象外 / ゼロ件 のどれかを一意に定める。enum フィールドの null は特に誤読されやすいので、可能ならサーバ側で既定値を補完して非 null にすることを検討する。
null の意味が未定義だとクライアントが独自判断(暫定既定値)するしかなく、表示の一貫性が仕様で保証されない。
「ゼロ件」と「欠落」を区別できるようにする。 読み取りレスポンスの配列は required(省略不可)とし、要素が無いときは空配列を返す。空タグ・特殊値で「存在しない」と「値が空/0」を曖昧にしない。欠落に意味を持たせる場合(部分レスポンス・権限制約・field projection 等)は、その意味を明記する。
欠落と空の混同は、クライアントに一律フォールバックを強い、未取得/異常を握り潰す。
表示に必須の値はフォールバック順序をクライアント任せにしない。 サーバ側で確定して非 null で返すか、確定順序を仕様に明記する。
仕様化されないフォールバック順はサーバ意図と乖離し表示がずれる。
書き込み系でボディが必須なら必須性を明示する。 未指定で任意に見える状態を残さない。
意味が数値なら数値型を、真偽なら boolean 型を使う。 件数・座標・金額などを文字列にしない。真偽を 0/1 や 'true'/'false' 文字列で代用しない。
ただし正確な精度・桁を保持する正当な業務理由(金額の固定小数桁、安全整数を超える値の保持 等)があれば文字列を許容する。安易な型変更は誤指摘になり得るため理由を確認する。
カウンタ・件数には整数型を使う。 小数を許す数値型を使わない。
同一値体系の ID は一貫した型・表現で統一する。 同じ ID が箇所により integer / string に分かれる状態を作らない。どの型に寄せるかのプロジェクト個別方針は本指針でなく決定ログ側に置く。
どちらにも利害がある(integer は採番方式の変更・桁あふれで破壊的変更になりうる、string は形式の機械検証が緩む)。本指針が求めるのは選択でなく統一。
日時文字列には format を付け、date-time は RFC 3339(OpenAPI の date-time format)に統一する。 瞬間は date-time、日付のみで足りる値(購入日・発売日・有効期限)は date。命名も ~At(タイムスタンプ)/ ~Date(日付)で揃える。UNIX timestamp を使う必然がある箇所のみ理由を添えて例外とする。
format 無しの string は任意文字列扱いになり、生成クライアントが日時として型安全に扱えず独自パースを強いられる。日付のみの値に時刻を持たせるとタイムゾーン依存のバグ(前日にずれる等)を生む。
整数で時刻を表すなら単位(エポック秒/ミリ秒)を description に必ず明記する。 可能なら RFC 3339 文字列を優先する。
秒/ミリ秒・タイムスタンプ/経過秒数が不明だと実装事故になる。
タイムゾーン非依存の日付(ユーザーが選んだ日付)は変換せず送受信し、解釈方針を description に書く(「TZ を考慮せずユーザが選択した日付をそのまま送信」等)。
URI/URL を表す文字列には uri format を付ける。 外部データ取り込み経路を含め不正値を弾く(format は相互運用上のヒントなので、強制検証は実装側で担保する)。
format 指定がないと接頭辞混じり等の不正 URL の混入を検知できず、調査コストにつながり得る。
数値の書式(整数か小数か)を一意に定める。 実値が小数になりうるなら型は小数とし、表示用の丸め方針(切り捨て/四捨五入)を別途明文化する。
書式を定めないと仕様・サンプル・実レスポンスで整数/小数が揺れ得る。
繰り返し現れる同一構造は共通スキーマに抽出し参照させる。 複数スキーマへの丸ごとコピー(プラットフォーム別のリンク群、通知設定フラグ群)、全パス直書きの共通ヘッダ(x-client-type 等)、共通ページングを components 化して SSOT にする。
コピーが散在すると変更時に修正漏れる。バックエンドで単一エンティティ由来なら本質的に 1 概念。
大半共通・一部差分の類似スキーマは、共通部を別スキーマに切り出して合成(allOf 等)で再利用する。 差分が識別子だけなら抽象フィールド名への統合も検討する。allOf は制約の合成(AND)であり OO 継承ではないので、共通部と派生の制約が矛盾しないよう設計する。再利用は契約の可読性を損なわない範囲で行う。
契約上の意味が同一な概念は単一のスキーマで表現する。 差が一部フィールドの有無に留まるなら別スキーマを作らず non-required フィールドで表す(例: お気に入り = 記事 + favoritedAt)。ただし作成用/更新用/公開用で必須条件や意味が異なるなら、似ていても分ける。同一概念かの判断は契約上の意味を主とし、内部で同一ドメインモデル由来かは補助根拠に留める。
クライアントに同義の型を 2 つ持たせず、変換の重複も消える。
階層関係は名前のサフィックス(1st/2nd)でなく、ネストした構造で表す。 将来の階層追加に耐える形にする。参照・更新・差分同期の対象になる要素には、表示名だけでなく安定した id を持たせる。
name のみの識別は表示名変更を破壊的変更にする。
レスポンス形状が複数あるなら構造で明示する。 相互排他なバリアントは oneOf、複数条件を同時に満たしうる場合のみ anyOf を使い、ペイロード自身が型を見分けるプロパティを持つなら discriminator を併用する。リクエストパラメータによって返る形が変わる場合は、別 operation・別メディアタイプ・別ステータスコードでの表現も検討する。
discriminator はペイロード内プロパティ値で型を判別する仕組みで、リクエストパラメータ分岐そのものは表せない。分岐が構造化されないと、どの形が返るか仕様上判別できない。
導出可能な値・既存フィールドで表せる状態は、専用フィールドを足さない。 終了日時から計算できるアーカイブ判定(endedAt があれば isArchived は不要)、期限から導出できる期限切れ判定(expiresAt があれば isExpired は不要)、遷移先 URL フィールドの有無で表せる「リンクあり」フラグなどは追加せず元データで表す。
冗長フィールドは非 MECE と誤用の元。
1 スキーマに異なる概念を密集させない。 商品基本情報・保証・リンク・遷移先・通知などは概念ごとにネストしたオブジェクトへ分割し、責務を絞る。
責務が広いオブジェクトは境界が不明瞭で利用側が必要部分を取り出しにくい。
1 つの enum / フィールドに複数の分類軸を詰め込まない。 例えば 1 つの type に broadcast(配信対象の軸)と urgent(重要度の軸)のような別軸の値を混ぜない。粒度の異なる値(汎用語と固有名詞)も混在させない。
軸が混在した enum は排他関係の根拠が不明で、存在しない組み合わせを選ぶリスクや扱い誤りを生む。
名前が表す概念と内包/並置する要素を一致させる。 「サマリー」が詳細リスト全体を内包するなら名前を実態に合わせる。総件数と未読件数のような軸の違う値を無造作に同列に並べない。
純粋なドメイン API では UI 表示専用のフラグ・文言を持たない。 API は意味論的データ(公開日時・遷移先 URL の有無・連携の有無)を返し、表示判断はクライアントに委ねる。状態名も UI 表現(VISIBLE)でなく概念(NOT_LINKED/DELETABLE)で命名する。ただし権限判定・行為可否(isDeletable 等)や server-driven UI を意図する API は例外。
shouldShowNewBadge / shouldShowShareButton のような表示専用フラグは責務分離違反で、UI 変更時に名前が陳腐化する。
同一フィールドを複数経路から書けて副作用が異なる状態を避ける。 例: 通知購読フラグを通知設定 API とプロフィール一括更新 API の両方から書けるが、外部配信基盤への購読反映は前者でしか行われない。副作用が常に伴うべきなら書き込み経路を 1 つに集約する。
2 経路の一方だけが副作用を行うと、フラグ値と実状態が乖離し、「どちらの API で更新すべきか」という暗黙知をクライアントに強いる。
異なる関心事を 1 つのパラメータに兼任させない。 例えば 1 つの値に「送信するか」と「再送間隔」を兼ねさせると、間隔を保ったまま送信だけ止める指定ができない。役割が違う制御は別パラメータに分離する。
兼任は設定が意図通り効かない死に設定を生む。
競合しうる状態の確定計算はサーバ側に置き、クライアントには結果だけ返す。 クライアントが現在値を送って上書きする設計(last-write-wins)は複数クライアントの同時更新で値が壊れる。増分はサーバでアトミックに行う。別 API でしか得られない判定結果は、判定材料を持つエンティティのフィールド(isDeletable 等)として返し往復を減らす。
外部 API のコード体系・機械的キーをそのままクライアントに露出させない。 表示用ラベルとコードのマッピングなどはサーバ側で一元的に保持し、レスポンスは「ID・ラベル・選択状態」のような意味の閉じた形で返す。排他選択は単一選択、複数選択可は別表現と、選択の性質で型を選ぶ。(ラベルの提供は「名称」という意味論的データの SSOT をサーバに置くことであり、上述の「UI 表示専用フラグを持たない」とは矛盾しない。)
要素単体で意味的属性(所属分類など)を判定する必要があるなら、その属性を各要素の表現に含める。 分類横断で取得した後に要素と分類を対応付けられない構造にしない(分類ごとにグループ化して返す設計なら不問)。
形式制約は spec に表明し、バリデーションの役割分担を明確にする。 文字数・文字種・範囲のような形式制約は spec の制約(maxLength/pattern/minimum 等)として宣言し、クライアントはそれを即時フィードバック(UX)に使う。ただしクライアント検証は UX の手段であって防御ではなく、サーバは形式・正当性を問わず全入力を検証する。値が実在するか・使用可能かの判定(そのクーポンコードは有効か、その配送先 ID は呼び出し元のものか)はサーバ側のデータを引かないと下せず、spec の制約としても表現できない。
同一概念のタグ・パス・スキーマ名・enum 値・プロパティ名の表記と単複を揃える。 point/points、myitem/myItem/myItems、iconImageId/iconId、imgUrl/imageUrl、updatedAt/modifiedAt のような揺れを 1 つの正規名に寄せる。
同一概念の表記揺れは認知負荷を高め取り違えを誘発する。
同一概念は表現方式(型・エンコーディング・書式)も揃える。 片方が string enum・もう片方が integer のような不一致や、日付書式(YYYYMMDD vs YYYY年MM月DD日)の混在を共通スキーマで一本化する。
同一リソースを扱う操作は同じ tag に揃える。 tag は利用者にとって理解しやすい論理グルーピングに使い、同一概念の操作が別グループに散らないようにする。
同一リソースの一操作だけ別 tag だと、ドキュメント上もコード生成上もグルーピングが概念とずれる。
HTTP ヘッダ名・プロパティ名・enum 値の表記規約(大文字小文字・区切り)をファイル全体で統一する。
description と実装・パラメータ名を一致させる。 条件記述(「type=all の場合のみ」等)の古い記述を残さず最新の実装名に合わせる。description が謳う区別(「全体向け/個人向け」)が実装上のフィルタとして成立していなければ、区別を実装するか値を統合する。
コントラクトと実装の乖離はクライアントに存在しない合成ケースを暫定実装させる。
タイポ・コピー起点の残骸を残さない。 descripton、別エンドポイント名のままの title、ID なのに「~名」の description などを正す。
残骸は生成コードのキー名に直結し原因究明しにくいバグになる。
ページネーション方式とパラメータ名を API 群で統一する。 offset 方式なら limit/offset のように同一命名・整数型・下限制約で揃える。cursor 方式等を採る場合もその方式と命名を統一する。
マジックナンバー・意味の取りにくい値は意味を表す列挙子にする。 正式名称が未確定なら enum 化は仕様確認後まで保留する。
生数値(accountType = 1/2)は読み手に都度の逆引きを強いる。
enum の各値は名前から意味が読めるようにし、読めない値には説明(標準の description、必要なら vendor extension)を付ける。 対義・順序・状態の関係(対義なら old/new、順位なら entry/bronze/silver/gold)も名前と説明で明確にする。
固有名詞・略語の enum 値は背景知識なしに意味不明になる。
enum は値集合を閉じて管理できる場合のみ使い、宣言したら取りうる有効値をすべて列挙する。 example 1 個で済ませない。将来値が増える/外部起源で拡張余地がある場合は、閉じた enum にせず string + 既知値の説明にして前方互換を保つ。
二値で意味が落ちる概念は enum 化する。 例えば検索の一致方式を isFullMatch: bool で表すと、false 側(前方一致)の意味が名前から読めない。queryMatchMode: {exact, prefix} のような戦略 enum にし、意図を型で表し将来拡張に備える。
description は単独で意味を成すよう主語と判定対象を明示する。 「PUSH 通知判定」でなく「PUSH 通知が送信されたか(true: 送信された / false: 送信されなかった)」のように、何を・true/false が何を意味するかを書く。
複雑なドメインは命名で複雑性を隠さず、名前に表出させて一読で理解できるようにする(「簡潔さより明確さ」の適用)。名前で表しきれない方向性・主語は description で補う(「クライアントが受信した通知」等)。
同一概念を指す語の綴りを揃える(例: パスの withdrawConfirmation とスキーマの withdrawalConfirmation のような不一致を作らない)。
微妙なスペル差は同一性の認識を妨げる。
使われていない/移植時に引き継いだだけのフィールド・パラメータは削除する。 常に null を返すだけ、クライアントが受け取るが利用しない、入力されるがどこにも使われない、廃止済み技術への依存(サービス終了済みの enum 値)などを残さない。
死にフィールドは誤用と混乱の元。
全エンドポイント共通の必須リクエストヘッダーは再利用可能な parameter 定義(in: header、components/parameters)に一元化し、各 operation に直書きで繰り返さない。 レスポンスヘッダーは components/headers を使う。資格情報として機能するヘッダー(API キー等)のみ security scheme で表し、非認証の共通メタヘッダーを security scheme に載せない(仕様の意味論とずれる)。
「要るなら全部・要らないなら全部」で済む共通ヘッダー群は、reusable parameter を各 operation から参照する。
spec は利用者(オーディエンス)単位でスコープし、その利用者が呼ばない operation を混在させない。 例えばクライアントが呼ばない server-to-server webhook を同じ spec に含めると、生成クライアントに無意味なメソッドが生え、security: [] のような特例も必要になる。
外部 API 由来のスキーマは、受領定義そのままでなく実 API の挙動と一致していることを根拠に書く。 パス・レスポンス形式・必須認証フィールド・ハッシュ方式・ID フィールド名の食い違いがないか確認する(疎通検証は実装側で行い、その結果を spec に反映する)。
受領定義をそのまま転記すると、定義と実態が乖離し得る。
社内固有の不透明な列挙子(拠点コード等)をクライアントに露出させず、意味のある抽象で表現する。
example・description に実在の資格情報・トークン・個人情報を書かない。 サンプル値は明らかなダミー(user@example.com 等)にする。認証が必要な operation には該当する security scheme を適用し、認証不要を意図する operation にのみ明示的に security: [] を付ける — 「指定漏れ」と「意図した非認証」を区別できるようにする。
spec は配布物であり、実値の混入は漏えいそのもの。認証要件が spec 上不明だと、生成クライアントも人間も試行錯誤で確かめるしかない。
spec と実装の整合を保証する。 spec と実サーバ実装が乖離した状態(手編集スナップショットの取り残し等)を残さない。code-first / design-first いずれでも、唯一の真実源と配布物を一致させる。
真実源と配布物がずれると、配布 spec に実装に無い null 許容指定が混入し得る。
spec は OpenAPI validator と採用ツール群で検証済みの妥当な状態に保つ。 構文的に壊れた仕様を配布せず、生成・配布前にバリデーションをかける。
構文エラーでツールのパース・型生成が落ち得る。
1 箇所の型/命名/書式の指摘は「同種が各所に複数ある」前提で、ファイル全体へ横展開して直す。
個別修正だけでは規約の揺れが残る。
命名の由来(外部システム名・歴史的経緯)が不明なまま流用しない。 実態と命名根拠を把握し、実態に合わない命名は改名する。
異なる API・データソースが返す同名 ID が同一の値体系(採番元・形式)を指す保証を確認してから突き合わせ前提の設計をする。 採番元が異なるなら対応表を用意するか、突き合わせを前提としない設計に切り替える。