スタイルガイド

プロト定義を最適に構造化する方法に関する指針を提供します。

このドキュメントは、.proto ファイルのスタイルガイドを提供します。これらの規則に従うことで、プロトコルバッファメッセージ定義とその対応するクラスが整合性があり、読みやすくなります。

以下のスタイルガイドラインの適用は、enforce_naming_style によって制御されます。

標準ファイル形式

  • 行の長さを80文字に保ちます。
  • 2スペースのインデントを使用します。
  • 文字列にはダブルクォーテーションを優先して使用します。

ファイル構造

ファイル名は lower_snake_case.proto とします。

すべてのファイルは次の順序で記述する必要があります。

  1. ライセンスヘッダー (該当する場合)
  2. ファイルの概要
  3. Syntax
  4. パッケージ
  5. インポート (ソート済み)
  6. ファイルオプション
  7. その他すべて

識別子の命名スタイル

Protobuf識別子には、次のいずれかの命名スタイルを使用します。

  1. TitleCase
    • 大文字、小文字、数字を含みます。
    • 最初の文字は大文字です。
    • 各単語の最初の文字が大文字です。
  2. lower_snake_case
    • 小文字、アンダースコア、数字を含みます。
    • 単語は単一のアンダースコアで区切られます。
  3. UPPER_SNAKE_CASE
    • 大文字、アンダースコア、数字を含みます。
    • 単語は単一のアンダースコアで区切られます。
  4. camelCase
    • 大文字、小文字、数字を含みます。
    • 最初の文字は小文字です。
    • 続く各単語の最初の文字は大文字です。
    • 注: 以下のスタイルガイドでは、.protoファイル内のどの識別子にもcamelCaseを使用していません。一部の言語の生成コードが識別子をこのスタイルに変換する場合があるため、ここで用語を明確にしているだけです。

すべての場合において、略語は単一の単語として扱います: GetDNSRequest ではなく GetDnsRequestd_n_s_request ではなく dns_request を使用します。

識別子内のアンダースコア

名前の最初または最後の文字にアンダースコアを使用しないでください。アンダースコアの後には常に文字が続きます (数字や2つ目のアンダースコアではありません)。

この規則の動機は、各protobuf言語実装が識別子をローカル言語スタイルに変換する可能性があることです。.protoファイルの song_id という名前は、言語によって SongIdsongId、または song_id と大文字にされたフィールドのアクセサになる可能性があります。

アンダースコアを文字の前にのみ使用することで、あるスタイルでは異なる名前が、他のスタイルに変換された後に衝突する状況を避けることができます。

たとえば、DNS2DNS_2 はどちらもTitleCaseでは Dns2 に変換されます。これらの名前のどちらかを許可すると、メッセージが生成コードが元のUPPER_SNAKE_CASEスタイルを保持する一部の言語でのみ使用され、広く確立された後、名前がTitleCaseに変換されて衝突する言語で後から使用された場合に、厄介な状況につながる可能性があります。

このスタイル規則を適用すると、XYZ_2 または XYZ_2V ではなく、XYZ2 または XYZ_V2 を使用する必要があります。

パッケージ

パッケージ名には、ドット区切りの lower_snake_case 名を使用します。

複数語のパッケージ名は、lower_snake_case または dot.delimited (ドット区切りのパッケージ名は、ほとんどの言語でネストされたパッケージ/名前空間として出力されます) のいずれかです。

パッケージ名は、プロジェクト名に基づいて短くも一意な名前になるようにします。パッケージ名はJavaパッケージ (com.x.y) であってはなりません。代わりに、パッケージとして x.y を使用し、必要に応じて java_package オプションを使用します。

メッセージ名

メッセージ名にはTitleCaseを使用します。

message SongRequest {
}

フィールド名

拡張を含むフィールド名にはsnake_caseを使用します。

繰り返しフィールドには複数形名を使用します。

string song_name = 1;
repeated Song songs = 2;

Oneof名

oneof名にはlower_snake_caseを使用します。

oneof song_id {
  string song_human_readable_id = 1;
  int64 song_machine_id = 2;
}

列挙型

enum型名にはTitleCaseを使用します。

enum値名にはUPPER_SNAKE_CASEを使用します。

enum FooBar {
  FOO_BAR_UNSPECIFIED = 0;
  FOO_BAR_FIRST_VALUE = 1;
  FOO_BAR_SECOND_VALUE = 2;
}

最初にリストされる値はゼロ値のenumであり、_UNSPECIFIED または _UNKNOWN のいずれかのサフィックスを持つ必要があります。この値は不明/デフォルト値として使用でき、明示的に設定されることが期待される意味的な値とは区別されるべきです。指定されていないenum値の詳細については、Protoベストプラクティスページを参照してください。

Enum値のプレフィックス付け

Enum値は、それらを囲むenum名によってスコープが指定されていないと意味的に考えられるため、2つの兄弟enumで同じ名前は許可されません。たとえば、次のコードは、2つのenumで定義された SET 値が同じスコープにあると見なされるため、protocによって拒否されます。

enum CollectionType {
  COLLECTION_TYPE_UNSPECIFIED = 0;
  SET = 1;
  MAP = 2;
  ARRAY = 3;
}

// Won't compile - `SET` enum name will clash
// with the one defined in `CollectionType` enum.
enum TennisVictoryType {
  TENNIS_VICTORY_TYPE_UNSPECIFIED = 0;
  GAME = 1;
  SET = 2;
  MATCH = 3;
}

enumがファイルのトップレベル (メッセージ定義内にネストされていない) で定義されている場合、名前の衝突のリスクが高くなります。その場合、兄弟には同じパッケージを設定する他のファイルで定義されたenumが含まれ、コード生成時にprotocが衝突が発生したことを検出できない場合があります。

これらのリスクを回避するために、次のいずれかを行うことを強くお勧めします。

  • すべての値にenum名 (UPPER_SNAKE_CASEに変換) をプレフィックスとして付ける
  • enumを囲むメッセージ内にネストする

どちらのオプションも衝突のリスクを軽減するのに十分ですが、問題を軽減するためだけにメッセージを作成するよりも、プレフィックス付きの値を持つトップレベルのenumを優先してください。一部の言語では「構造体」型内にenumが定義されることをサポートしていないため、プレフィックス付きの値を優先することで、バインディング言語全体で一貫したアプローチが保証されます。

サービス

サービス名とメソッド名にはTitleCaseを使用します。

service FooService {
  rpc GetSomething(GetSomethingRequest) returns (GetSomethingResponse);
  rpc ListSomething(ListSomethingRequest) returns (ListSomethingResponse);
}

避けるべきこと

必須フィールド

必須フィールドは、ワイヤーバイトを解析するときに特定のフィールドが設定されていることを強制し、そうでなければメッセージの解析を拒否する方法です。必須の不変条件は、通常、メモリ内で構築されたメッセージには強制されません。必須フィールドはproto3で削除されました。エディション2023に移行されたProto2のrequiredフィールドは、field_presence機能をLEGACY_REQUIREDに設定して対応できます。

スキーマレベルでの必須フィールドの強制は直感的に望ましいですが、protobufの主要な設計目標の1つは、長期的なスキーマ進化をサポートすることです。今日の時点ではどんなに必須に見えるフィールドであっても、将来的にはそのフィールドが設定される必要がなくなるという妥当な未来が存在します (例: int64 user_idは将来的にはUserId user_idに移行する必要があるかもしれません)。

特に、実際に処理する必要のないメッセージを転送するミドルウェアサーバーの場合、requiredのセマンティクスは、長期的な進化の目標にとって有害すぎることが判明したため、現在は非常に強く推奨されていません。

必須は強く非推奨を参照してください。

グループ

グループは、ネストされたメッセージの代替構文およびワイヤー形式です。グループはproto2で非推奨とされ、proto3から削除され、エディション2023では区切り表現に変換されます。グループ構文を使用する代わりに、ネストされたメッセージ定義と、ワイヤー互換性のためにmessage_encoding機能を使用するその型のフィールドを使用できます。

グループを参照してください。