決済サービス企業「Stripe」のエンジニアであるポール・アジェス氏が、StripeのオブジェクトIDが人間にとって読みやすい形をしている理由をブログに投稿しています。

Designing APIs for humans: Object IDs - DEV Community

https://dev.to/stripe/designing-apis-for-humans-object-ids-3o5a



どんなビジネスを行う場合でも、顧客情報や注文状況などの重要なデータを保存するためのデータベースが必要です。データを一意に指定したり、保存したデータを素早く読み出したりするためにデータベースには主キーとも呼ばれるIDを設定します。

IDに関する最も単純なアプローチは連続する番号を割り当てるというもの。設定に手間がかからず、扱いやすいため良いアイデアに見えますが、悪意のある攻撃者が他のIDを推測できてしまうためセキュリティ面が問題です。

IDの候補として、「4c4a82ed-a3e1-4c56-aa0a-26962ddd0425」のように32桁のランダムな英数字で表されるUUIDを使用することも考えられます。生成が早かったり、既に十分な採用実績があったり、ほとんど衝突が発生しなかったりとさまざまな長所があるため最良の方法の一つと考えられています。

一方で、Stripeは「pi_3LKQhvGUcADgqoEM3bh6pslE」のような形式のIDを採用しています。全てのIDについて、先頭に「pi_」のようなプレフィックスが付与されており、後ろにランダム生成の文字列が続きます。「pi_」なら「PaymentIntentオブジェクト」のように、プレフィックスによってそのIDが何のIDなのかを瞬時に判別できるというメリットがあります。



PaymentIntentを作成する際にはCustomerオブジェクトやPaymentMethodオブジェクトのIDを入力しますが、同様に「cus_」や「pm_」などのプレフィックスが付いているおかげで多数のIDをすぐに区別することができます。



また、プレフィックスのおかげで誤ったIDを使用していることにすぐに気付けるというメリットも存在しています。下図はAccountオブジェクトを指定するべきシーンのため、IDのプレフィックスは「acct_」であるはずなのに代わりに「cus_」が指定されています。プレフィックスがなければこうしたミスに気付くのがはるかに難しくなるわけです。



さらにAPIの互換性を維持する上でもメリットがあります。StripeではPaymentIntentの支払い方法の指定について、過去のバージョンとの互換性を維持するため、PaymentMethodのIDである「pm_○○」形式のIDのほかカードオブジェクトのIDを指定することができるようになっています。



プレフィックスが存在しているおかげで、システムはそのIDが何のIDなのかを瞬時に判別して適切なデータベースにクエリを飛ばす事が可能です。

プレフィックスを採用していなかった場合、追加で「type」のようなパラメータを設定する必要が生じてしまい、APIが複雑化してしまいます。IDを使用する際には常にそのIDがどのオブジェクトのIDなのかというデータを付与する必要があるため、IDの文字列に埋め込んでしまうのが優れた解決策になるわけです。



プレフィックスのその他の利点として、最初の数文字でIDの種類が判別できるため、Discordサーバーなどで機密情報をブロックする設定ができる点が述べられています。「sk_live_」から始まるIDはStripeの秘密鍵であり、下図のように設定して秘密鍵を含むテキストをブロックする事でユーザーのヒューマンエラーで秘密鍵を漏えいするのを防いでいます。



「sk_live」で始まるのが本番用の秘密鍵、「sk_test」で始まるのがテスト用の秘密鍵と分かれているため、テスト用の環境において本番のデータが使用されないようにするコードを書くことも可能です。



こうした人間にとって読みやすいIDの形式としては他にもIETFのUniform Resource Name(URN)などが存在しているとのこと。StripeのIDの生成方法は明かされませんでしたが、こうしたプレフィックス付きのIDを生成するためのライブラリとしてRuby用の「Prefixed IDs」が紹介されていました。