Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

APIの命名規則はフロントエンド・バックエンドどちらに合わせるべきか?

morimorihogeです。ちょっと色々忙しくて死んでますが、深夜の勢いで書いてみます。

ことの起こり

Twitterにてこんな発言を見かけました

元記事(翻訳)はこちら

Rails: 日付や時刻のカラム名を命名規則に合わせよう(翻訳)

本件について、Twitterではreplyしてみたのですが、文字数の都合で詳細に書きづらいということもあり、一度自分の意見をまとめてみようということで記事に考えをまとめてみました。

※本記事の内容はあくまで僕の意見であり、社内エンジニア陣の共通見解というわけではありません。この手の設計思想問題はちょいちょい社内でも衝突することがありますし、その際は都度議論しながらプロジェクトごとにコンセンサスを取っています。

そもそも「バックエンド」の中でも命名規則は衝突している

さて、ここで皆さん大好きGitHub GraphQL APIを見てみましょう。
GitHubは言わずと知れたRails backendでいい感じに複雑なこういったケースを議論するのには最適な見本の一つだと思います。

基本的な命名規則については日本語でざっと解説してくれている記事がありましたので詳細はそちらを参考にしてみてください。

さて、では実際の実装としてはどうなっているでしょうか?ここでは分かりやすいということでDateTime型なフィールドに着目してみましょう。


あれあれ?天下のGitHubでも命名が統一されていませんね 🤔

果たしてこのCommit.xxxDateなどのdate suffixはRailsの命名規約違反で良くない仕様なのでしょうか?あるいはCommitComment.publishedAtなどのat suffixはフロントエンド向けの名前が表記揺れしていて良くない仕様なのでしょうか?考察してみましょう。

※以降の考察は僕が勝手に推察したもので、GitHubの中の人に聞いた情報や公式情報ではありません。鵜呑みにしないで下さい🙏

その名前はどのシステムが命名したものなのか?

そもそもGitHubは今ではソフトウェア開発に関する何でもサービスみたいになっていますが、名前の通り裏ではGitが使われているのは周知の事実かと思います。そして、Gitにはcommitに対してauthor dateとcommit dateという概念があります

ここではGitの用語としてこれらのDateがあるということが重要なので、この二つがそれぞれ何を意味するのかという話については以下の記事を参照してみてください。

ここで重要なのはこれら二つのDateはGitが命名・作成したもので、GitHub側のバックエンドRailsアプリが命名・作成したものではないということです。

Gitが命名・作成した用語については基本そのままの用語でAPIに出す方が、システムを一気通貫して同じ用語を使うことができるという点で設計上のメリットがあります。なのでここはdateという名前がそのままAPIに出ているのですね(あくまで推測です)。

それでも意味を分かりやすくするために少し命名を弄ることもある

それならそのままのGit用語をAPIにも出しているのかというと、実はそうでもありません(ここがやや複雑)。
もう一度よく見てみましょう。

  • Git用語:author date, commiter date
  • GitHub API: authoredDate, committedDate

はい。author -> authored、commiter -> committedの名前置き換えがありますね。これらはGit用語ではなくGitHub用語に変更されています

これは完全な推察になりますが、GitオブジェクトのデータをGitHub側のシステムに取り込む際にGit用語そのままだとやや意味が不明確なので、より明確な命名にしようとして一捻りしたのではないかと思います。

こうしたシステム間結合時の再命名は、意味的には利用システムにとって分かりやすくなりますが、その分システムを跨いだときに一つのものが複数の名前を持ってしまうというシステム横断の表記揺れ問題を引き起こします。カオス。

ここまでの内容で各システム用語対応の現状をまとめてみましょう。

Git用語 GitHubシステム内部用語(憶測) GitHub GraphQL API
author date authored_date authoredDate
committer date committed_date committedDate
重箱の隅
さらに細かい話をするならば、commiter dateについてはGitの中でも微妙に表記揺れがあり、commit.hなどのソースコード上は**commit** dateですが、環境変数で上書きする際にはGIT_COMMITTER_DATEだったりします。ここではGitが外部向けに公開している(と推察される)名前のcommitter dateの方を正として採用しています

at 系フィールドも合わせて見てみる

さて、では次にatで終わる系フィールドも同じ表にまとめてみます。CommitComment.publishedAtについてはそもそも(既に存在するコミットに後付するという意味での)コミットコメントという仕様自体がGitにはないので、GitHubが命名したものだと推測できます。

Git用語 GitHubシステム内部用語(推測) GitHub GraphQL API
author date authored_date authoredDate
committer date committed_date committedDate
- published_at publishedAt
- last_edited_at lastEditedAt

さて、これで現在の命名について整理ができました。

もし外部APIフィールド名をdate suffixに統一すると?

こうしてみてみると、この対応表は以下のようにまとめられるのではないかと思います。

  • GraphQL Schemaだけ見てみると、dateとatの表記揺れがあってちょっと気持ち悪い
  • GitHubのシステム内部用語がそのままGraphQL APIに出ているので、そこの対応は分かりやすい
  • Git用語が微妙にGitHub用語に変換されているが、意味的に分かりやすくするレベルの差に留まる

では、もしこれを元Tweetにあったように「フロント側から分かりやすいようにdate suffixに統一」してみるとどうなるでしょうか?

Git用語 GitHubシステム内部用語(推測) GitHub GraphQL API
author date authored_date authoredDate
committer date committed_date committedDate
- published_at publishedDate
- last_edited_at lastEditedDate

ちょっとわかりにくいので、全部snake_caseに直して単語レベルだけで比較してみます。

現行のGitHub APIでは以下の通りです。

Git用語 GitHubシステム内部用語(推測) GitHub GraphQL API
author_date authored_date (←と同じ)
committer_date committed_date (←と同じ)
- published_at (←と同じ)
- last_edited_at (←と同じ)

date suffix変換を噛ますとこうなります

Git用語 GitHubシステム内部用語(推測) GitHub GraphQL API
author_date authored_date (←と同じ)
committer_date committed_date (←と同じ)
- published_at published_date
- last_edited_at last_edited_date

はい。GraphQL APIを見るフロントエンド側からは分かりやすくなりましたが、システム全体での用語統一という観点では名前が増えました

このようにフロントエンドとバックエンドで違う名前を使うようになると、お互いの使う用語が異なるので仕様のやり取りをする際常に用語管理テーブルを挟まねばならず、脳のリソースを持って行かれてしまいます。

GitHub APIの仕様から推測した命名規則

というわけで、ここまでのことから推測できるGitHub(及びGitHub API)の命名規約をまとめてみると、システム全体として、

  • GitHubバックエンドアプリが作成し管理するフィールドについてはRailsの命名規約に従う
  • 外部のシステム(Git等)が扱うデータを取り込んで利用するフィールドについては外部のシステム側の命名規約に従う。ただし、意味的に分かりにくいものを多少修正することはある
  • (GraphQL)APIにはGitHubバックエンドアプリから見える名前をそのまま利用する

というポリシで命名しているのではないかという推測が立てられました。
また、ここまでの中では挙げてきませんでしたが、そもそも今回のGraphQLのようなスキーマを持つAPIの場合、命名のうちデータ型を想起させる部分の単語については型の定義情報を合わせて見ればDateTimeと分かるんだからいいだろうという事情もありそうです。

この設計思想のバランス感覚、僕は割と良い落とし所ではないかと感じました。自分が設計するときも参考にしたい。

まとめ的なもの

結局の所この辺りは設計上のトレードオフで、唯一の正しい正解がない中でbetterなものをプロジェクトごとに探す、ということなのではないかと思います。
こういった複数システム横断するものを設計するときは、一部だけを切り出して見たときの整合性に着目するのではなく、全体として見たときに設計思想が統一されているかを重視するのが大事です。

今回、フロント側だけの視点では「suffixがdateに統一されていないのは使いにくい・不整合だ」と感じたのかもしれませんが、システム全体としては date suffixとat suffixが混じっている方が統一されているという見方もできるわけです。

もしどうしても使っているフレームワークなどの事情から自分達に合わせた命名を使いたいということであれば、それはそのシステムの中で変換を噛ますという手もあります。RailsならActiveSupport#alias_attributeがありますし、他の言語・フレームワークでもこの手の外部APIと内部APIの命名をbridgeするような機能は大抵備えているでしょう(最悪setter/getterを作れば良い)。

そもそもAPIというもの自体異なるシステム間を繋ぐものなので、違う言語やフレームワーク同士を繋ぐ以上ある程度規則上の不整合はどうしても発生します。こういったときに「ここがこうなってるのはこういう事情によるものです」と納得のいく説明ができるかが設計上大事な部分であり、こういったものを積み重ねたものが「設計思想」になっていくのだと僕は思います。

ここからが本当の地獄だ(ゴゴゴゴ・・・・・

うん、きれいに説明できたな、ヨシ!で終わっても良かったのですが、さらなるカオスをチラ見せします。
GitHub APIのCommitオブジェクトにはpushedDateというものが存在します。

これまでの解説に従うと「GitHub側で付けた命名ならat suffixのはずだから、これはGitのプログラム内用語かな?」という推測になるのですが、Git自体にpushed_dateという概念はありません。ナゼダー 😇😇😇😇😇

morimorihogeの解釈
そもそもGitにおけるpushはリポジトリ間でコミットやブランチを転送するコマンドであり、いつ転送したかという情報はGitの興味の対象ではない、という事情からだと思われます。

元々Gitそのものは分散型リポジトリであり単一のoriginサーバーを前提としていないこともありますし、pushed_dateをもしGitデータ構造の中に持ってしまった場合、各所のリポジトリによってpushed_dateが違うとバイナリレベルでの互換性が保てない(保持しているpushed dateだけが違うリポジトリ間でpushしたときにconflictしてしまう)などの問題が発生することが予想されるため、Gitそのものがpushed dateを保持しないのは設計思想上そういうものだと思って良いでしょう。

ちなみにこの問題に説明を付ける候補は僕の中に数個あるのですが、どれも憶測でしかないこともあり、ここではあえて解説しないことにします。
こうした「答えがない中でbetterな正解を探す」のは良い頭の訓練になったり、チーム内の価値観すり合わせに繋がるので皆さんの開発チームなどでZoom飲み会の話題にしてみてもらってもいいかもしれません。

ではでは、色々書きたいことが他にもあるのですがとりあえず今は仕事に戻ることとします。


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。