新紙幣をきっかけに、ユーザーフレンドリーなID(識別子)を考えてみる
マイベストのプロダクト開発部(UXチーム/ポイントユニット)でバックエンドエンジニアをしているゆーだいです。
マイベストテックブログ連載:「みんなでお買い物サポートクラブ」リリースまでの開発の裏側の2日目を担当します。
この記事で書くこと
エンジニアなら普段ID(識別子)を使ったり、見たりすることが多いと思います。
中でもUUIDは、よく使用されており、「聞いたことあるIDランキング」があれば、必ずトップ3に入るでしょう。🥇
そんなIDですが、ユーザーの目に直接触れる機会は少ないものだと私は考えます。
今回は、IDがユーザーの目に触れる場所に登場し、問い合わせなどの際はそのIDを用いる。
そのように使われるIDの場合、どのようなものを使うのが良いのか?ということについて考えました。
具体的にどんなケースで使用するIDなのか
みんなでお買い物サポートクラブでは、クチコミ投稿やアンケート回答で貯めたポイントを現金に交換することができます。
その交換を申請する際に、使用する「申請ID」が今回の主役になります。
この「申請ID」は主に、以下の画像にある2箇所でユーザーの目に触れることになります。
表側の制約
「M8rLhr7JEpJlqFGUMmOxg==
みたいなIDはユーザーフレンドリーじゃない。」というメモがこのタスクを依頼された時に書かれていました。
「ユーザーフレンドリーではないID」
まずは、これがどういうことなのかを考えます。
もし、ユーザーが交換申請を進める中で困ったことがあった場合は、お問い合わせの際に、この「申請ID」を記載してもらい、それを用いて調査することになります。
ユーザーに見える&使ってもらう可能性がある文字列という観点から、以下の2つの要素に分解しました。
- 一般のユーザーが「976a9097-0252-4afa-b517-f8ee400e99cb」みたいな文字列を見た時に、少なからず「なんだこの長い文字列?」みたいに感じる人がいるのではないだろうか。
- 記号が入っている場合、1つのまとまりの文字列として判定されず、コピーしにくい。
いずれも、とても細かいことだと思います。
ただ、入力することを考えた時に、長いよりは短い方が良いし、コピーしづらいよりはしやすい方が良いですよね。
「コピーボタンを設置すれば良いのでは?」と思う方もいるかもしれませんが、サービス内で申請IDが表示されるのは、交換申請時のみで、申請IDが欲しい時は、ほとんどのユーザーはメール本文から、コピーすることになります。
そのため、コピーボタンを置くという対策は、あまり意味のない対策になってしまいます。
裏側の制約
ユーザーに見える部分の話もありますが、裏側の仕様上の制約も存在します。
みんなでお買い物サポートクラブでは、自社で付与したポイントを現金化し、その現金を楽天銀行のかんたん振込(メルマネ)を利用して送金しています
メルマネのシステムを利用する際に、ID(識別子)が必要になるため、そこに「申請ID」を使用しています。
メルマネの仕様として、そのIDは「20文字以内の半角英数字」である必要があります。
また、お金関係の話であることから、今回のIDの特性上、「推測できない値」であることが望ましいです。
今回作成したいID(識別子)の制約
以上の表側と裏側の制約をまとめると、以下のようになります。
- 20文字以内の半角英数字で構成されていること。
- 推測ができないこと。
- コピペや入力がなるべく行いやすいこと。
また、交換申請が行われた際に、生成するIDなので、超高速で生成される。みたいな必要があまりないことも特徴のひとつです。
普段使っているサービスでは、どのような値が使われているのか
作成したいIDの条件が整ったので、世の中にはどのようなIDがあるのか、普段自分が使っているサービスでIDが目に触れるものがないか調査を行いました。
その際に参考になった記事を2つ紹介しておきます。
ただ、ここで紹介されているものは「20文字以内の英数字」の制約を満たしていないものばかりでした。
そのため、次に自分が使っているサービスのIDについて考えました。
最初に浮かんだのは、PayPayでした。
ミスドを食べた時の支払い履歴があったので、それを見に行きました。(いつどこでミスドを食べていたかバレても全然良いけど、一応隠しておきます🍩)
PayPayでは、支払いごとに取引番号を確認することができ、20桁の数字で構成されていました。
余談ですが、コピペボタンが設置されており、ユーザーに優しいUIになっていました。
そして、本当に余談ですが、ミスドではハニーチュロ推しです。
他にも、Gitでコミットを行った際に生成されるコミットIDやクレジットカードの番号や郵便番号、口座番号などなどについても考えてみました。
紙幣(お金)のIDが参考になりそう??
さまざまな識別子を見ていく中で、ふと新紙幣についてのニュースを思い出しました。💰
「紙幣の札番号(画像の水色枠部分)がゾロ目のものがマニアの中で高値で取引されるので、新しくなった今がゲットするチャンス!」みたいなやつです。
「そういえば、超身近なお金にも識別子が存在しているな〜」とか思いながら、札番号の説明が書いてあるページを訪れました。
2024年(令和6年)7月3日に発行が開始された銀行券の記番号は、6桁のアラビア数字をはさんで、アルファベットが頭と末尾に2文字組み合わされ、「AA123456BB」や「CD777777EF」というように表されます。アルファベットは全部で26文字ありますが、I(アイ)とO(オー)は数字の1、0に間違いやすいため、これらを除く24文字が使われています。また、数字は「000001」から「900000」までの90万通りが使われています。これらの組み合わせにより、記番号は2,985億 9,840万枚で一巡します。
「ふむふむ、アルファベット4文字と数字6文字で構成されているのか〜」と読んでいると、IとOが除かれていることが記載されていました。
アルファベットは全部で26文字ありますが、I(アイ)とO(オー)は数字の1、0に間違いやすいため、これらを除く24文字が使われています。
「おお!これは、ユーザーフレンドリー!!」
たしかに、小文字のlや大文字のI、Oなどは数字と読み間違えることが多いので、納得感がありました。
札番号は20文字以内の英数字という制約も満たしており、コピペもしやすく、生成の方法をランダムにすれば、推測されることもありません。
そして、アルファベット4文字と数字6文字を使うと、3,300億以上のパターンを表現することができます。
実装
ということで、紙幣の札番号を参考に、「申請ID」を作成することにしました。
実装は非常にシンプルで、以下のようになります。
ポイント交換の情報を管理するモデル(Point::Exchange)で、「申請ID」を取得するため、Concernとして定義しています。
メソッド自体は至ってシンプルで、アルファベットの桁数と数字の桁数をgenerate_identifier
に渡すことによって、識別子を生成してくれます。
module UserFriendlyIdentifierGeneratable
extend ActiveSupport::Concern
included do
def generate_identifier(alphabet_num, numerical_num)
"#{generate_alphabet(alphabet_num)}#{generate_numerical(numerical_num)}"
end
def generate_alphabet(alphabet_num)
# IとOは1と0に見えるため、除外
alphabets = ('A'..'Z').to_a - ['I', 'O']
Array.new(alphabet_num) { alphabets.sample }.join
end
def generate_numerical(numerical_num)
Array.new(numerical_num) { rand(0..9) }.join
end
end
end
Point::Exchangeモデル側で、UserFriendlyIdentifierGeneratableをincludeすることでgenerate_identifierメソッドを使用可能にします。
モデル側には、set_identifierメソッドを用意し、万が一、いや億が一、重複が起こらないようにするため、リトライ処理を加えてあげることで、完成です。
module Point
class Exchange < ApplicationRecord
include UserFriendlyIdentifierGeneratable
...
def set_identifier
return if identifier.present?
tries = 0
loop do
raise 'Failed to generate identifier' if tries > MAX_TRIES
self.identifier = generate_identifier(4, 6)
break unless self.class.exists?(identifier: identifier)
tries += 1
end
end
...
end
仮に、1000万回交換申請が行われたとして、IDが1発で衝突する可能性は0.003%くらいです。
MAX_TRIESを仮に10に設定したとしたら、約5.9×10^-46になるので、このset_identifierメソッドがエラーを吐くことはなさそうですね。
ちなみに、現時点では、500円から交換申請を行うことができるので、1,000万回交換申請が行われているということは50億円分のポイントが交換されたことになります😇
おわりに
今回は、ユーザーフレンドリーなIDの生成について、些細なタスクから、少し飛躍させて考えてみました。
実際のところ、「英数字を組み合わせた文字列をIDに使う。」ということは、他のサービスや札番号を参考にせずとも、思いつくことができると思います。
しかし、ただなんとなく「こんな感じが良さそう」という意見ではなく、ちょっとしたソースや根拠があると、提案を受け入れる側も「それで行こう!」と言いやすいのではないでしょうか?
みんなでお買い物サポートクラブがたくさんのユーザーに使用され、申請IDがコピペされる時がくれば良いな!と思います!
まあでも、申請IDがコピペされる時って、お問い合わせが発生してる時だから、コピペされることがない方がいいのか!!!
おわり
株式会社マイベストのテックブログです! 採用情報はこちら > notion.so/mybestcom/mybest-information-for-Engineers-8beadd9c91ef4dc2b21171d48a4b0c49
Discussion