何をしたいユーティリティか:「識別子生成」
ここでの「識別子生成」は、「一意な ID(識別子)を文字列として作る」処理を、毎回バラバラに書かず、共通ユーティリティにまとめることです。
注文番号、セッション ID、一時ファイル名、ジョブ ID、トレース ID…。
業務システムでは「他と絶対かぶってほしくない文字列」が、あちこちで必要になります。
だからこそ、
generateId(); // "20240105-093015-AB3F9C"
generateShortId(); // "k9f3a2x1"
generateTraceId(); // "20240105T093015Z-7F3A9C12"
JavaScriptのように、「識別子はこの関数を呼ぶ」と決めてしまうのが大事です。
業務用識別子に求められる条件を整理する
ざっくり、これだけは意識したい
業務で使う識別子には、だいたい次のような要件があります。
一意性(かぶらないこと)。
ある程度の長さ(短すぎず、長すぎず)。
使える文字の制限(英数字だけ、記号なし、など)。
人間が見て扱うかどうか(ログで見やすいか、コピペしやすいか)。
この「条件セット」を満たす形で、いくつかのパターンを用意しておくと便利です。
一番シンプルな「ランダム英数字 ID」
ランダム文字列生成の基本
まずは、純粋なランダム英数字 ID を作る関数から始めます。
function generateRandomId(length = 12) {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const max = chars.length;
let result = "";
for (let i = 0; i < length; i++) {
const idx = Math.floor(Math.random() * max);
result += chars[idx];
}
return result;
}
JavaScript重要ポイントをかみ砕いて説明する
chars に「使ってよい文字」を全部並べています。
ここでは大文字・小文字・数字を使っていますが、「大文字だけ」「数字だけ」などに変えることもできます。
Math.random() は 0 以上 1 未満の乱数を返します。
それに max を掛けて Math.floor することで、0 以上 max - 1 以下の整数を作り、
それをインデックスとして chars[idx] を取り出しています。
このループを length 回回すことで、「指定した長さのランダム文字列」ができます。
実際の動き
generateRandomId(); // 例: "aK3f9ZpQ1xY2"
generateRandomId(8); // 例: "F9a3k2X1"
generateRandomId(20); // 例: "k3F9aZpQ1xY2LmN7bC0"
JavaScript同じ長さ・同じ文字集合なら、呼ぶたびに違う ID が生成されます。
「時刻+ランダム」の業務向け ID
なぜ時刻を混ぜるのか
純粋なランダムでも十分強いですが、業務では「いつ生成されたか」が分かると便利なことが多いです。
ログを追うときに、「この ID はいつ作られたのか」が一目で分かる。
日付ごとに集計したいときに、ID から日付をざっくり推測できる。
そこで、「日付文字列+ランダム文字列」をくっつけた ID を作ります。
日付文字列ユーティリティを使う
先に作ったような日付フォーマット関数を使います。
function padZero(value, length = 2) {
return String(value).padStart(length, "0");
}
function formatDateTimeCompact(date = new Date()) {
const d = date instanceof Date ? date : new Date(date);
const y = d.getFullYear();
const m = padZero(d.getMonth() + 1, 2);
const day = padZero(d.getDate(), 2);
const h = padZero(d.getHours(), 2);
const min = padZero(d.getMinutes(), 2);
const s = padZero(d.getSeconds(), 2);
return `${y}${m}${day}${h}${min}${s}`; // 例: "20240105093015"
}
JavaScript時刻+ランダム ID の実装
function generateBusinessId() {
const ts = formatDateTimeCompact(); // "20240105093015"
const rand = generateRandomId(6); // 例: "AB3F9C"
return `${ts}-${rand}`; // "20240105093015-AB3F9C"
}
JavaScript実際の動き
generateBusinessId(); // 例: "20240105093015-K9F3A2"
JavaScriptこれなら、
いつ作られたか → 先頭の 14 桁で分かる。
同じ秒の中で複数作っても → 後ろのランダム部分で区別できる。
という、業務で扱いやすい ID になります。
カウンタを混ぜた「ほぼ連番」ID
同一プロセス内での衝突をさらに減らす
「同じプロセス内で、同じ秒に大量に ID を発行する」ようなケースでは、
ランダムだけに頼らず、カウンタを混ぜると安心です。
let _idCounter = 0;
function generateSequentialLikeId() {
const ts = formatDateTimeCompact(); // "20240105093015"
_idCounter = (_idCounter + 1) % 1000000; // 0〜999999 でループ
const counterStr = String(_idCounter).padStart(6, "0"); // "000001" など
return `${ts}-${counterStr}`;
}
JavaScript重要ポイント
_idCounter はモジュール内の変数として持っておきます。
関数を呼ぶたびに 1 ずつ増やし、100 万で一周するようにしています。
これで、同じ秒の中で 100 万件までなら、"20240105093015-000001" のように、秒+カウンタで一意な ID が作れます。
ランダムを混ぜなくても、プロセス内ではほぼ衝突しません。
UUID 風の ID を作る(簡易版)
完全な UUID ではないけれど「それっぽい」もの
ブラウザや Node.js では、crypto.randomUUID() が使える環境もありますが、
ここでは「自前で簡易 UUID 風 ID を作る」例も載せておきます。
function generateHexSegment(length) {
let result = "";
for (let i = 0; i < length; i++) {
const n = Math.floor(Math.random() * 16); // 0〜15
result += n.toString(16); // 16進数文字列
}
return result;
}
function generateUuidLike() {
return (
generateHexSegment(8) + "-" +
generateHexSegment(4) + "-" +
generateHexSegment(4) + "-" +
generateHexSegment(4) + "-" +
generateHexSegment(12)
);
}
JavaScript実際の動き
generateUuidLike(); // 例: "f3a9c012-7b4e-4d2a-9f01-3c7a9b2e1d4f"
JavaScript完全な UUID v4 の仕様を満たしているわけではありませんが、
「16 進数+ハイフンの形で、十分ランダムな ID」が欲しいときには、これで足ります。
実務で意識してほしい設計のポイント
「どの用途にどの ID を使うか」を決める
識別子は、用途によって求められる性質が違います。
ログのトレース ID → 人間が目で追うので、時刻が入っていると便利。
DB の主キー → 衝突しないことが最優先。長さや形式は DB の制約次第。
一時ファイル名 → OS のファイル名制約を満たす必要がある。
だからこそ、
トレース ID 用:generateBusinessId
プロセス内連番 ID 用:generateSequentialLikeId
純ランダム ID 用:generateRandomId
のように、「名前で用途が分かる関数」を用意しておくと、後から読んだときに迷いません。
「一意性の保証範囲」を意識する
ここで紹介した ID は、あくまで「かなりかぶりにくい」レベルです。
システム全体で絶対に衝突させたくない場合は、
DB の AUTO INCREMENT やシーケンスを使う。
UUID(crypto.randomUUID() やライブラリ)を使う。
といった、より強い仕組みを使うのが基本です。
今回のユーティリティは、
ログ用のトレース ID。
一時的なクライアント側 ID。
テストデータ用のダミー ID。
など、「多少の衝突リスクを許容できる場面」や、「他の仕組みと組み合わせる場面」で使うのが現実的です。
フォーマットを変えたくなったときのために
ID のフォーマットは、あとから「やっぱり日付はいらない」「ハイフンを抜きたい」などの話が出がちです。
だからこそ、「ID の見た目を決める場所」をユーティリティに閉じ込めておくのが重要です。
画面や API のコードの中で直接 Date や Math.random() を組み合わせるのではなく、
必ず generateXXXId を通すようにしておけば、仕様変更に強くなります。
少し手を動かして感覚をつかむ
コンソールで、次のようなコードを実際に打ってみてください。
generateRandomId();
generateRandomId(8);
generateBusinessId();
generateBusinessId();
generateSequentialLikeId();
generateSequentialLikeId();
generateSequentialLikeId();
generateUuidLike();
generateUuidLike();
JavaScript何度か呼んでみて、
毎回違う値になっているか。
フォーマットが一定になっているか。
「いつ作られたか」が ID から読み取れるか。
を、自分の目で確認してみてください。
そのうえで、自分のプロジェクトに
export function generateRandomId(...) { ... }
export function generateBusinessId(...) { ... }
export function generateSequentialLikeId(...) { ... }
export function generateUuidLike(...) { ... }
JavaScriptのような関数を置き、
「識別子が欲しくなったら、必ずこの“識別子生成ユーティリティ”を通す」
というルールを作ってみてください。
それだけで、あなたのシステムの ID は、場当たり的な Math.random() の寄せ集めから、
意図と一貫性を備えた「業務レベルの識別子生成」に変わっていきます。
