慶応三年、江戸にて
娘「おとっつぁん、大変だよ!」
熊さん「一体どうしたってんだい、娘ちゃん」
熊さん「朝っぱらから、ただごとじゃねぇ顔してるじゃねぇか」
娘「うちのECサイトで、問題が起こっちまってるんだよぉ!」
娘「新規登録の時だけのキャンペーン割引が」
娘「全ての既存会員さんにも適用されちゃってるんだよ!」
熊さん「なんてこったい」
熊さん「初回のお客にだけ振る舞う饅頭を、常連さん全員に配っちまったようなもんじゃねぇか」
熊さん「ただでさえうちのECサイトは赤字だってぇのに」
熊さん「出血覚悟の大キャンペーンが絶賛開催中ってわけか」
熊さん「一体どうしてそんなことになっちまったんだい」
AI「あっしが説明いたしやす」
熊さん「おぉ、AIエージェントのAIっつぁんじゃあねえか」
熊さん「いつも頼りになるぜ、さぁ説明しておくれ」
AI「へい」
AI「実はあっしが、初回登録だけのクーポンを」
AI「全会員向けクーポンと同じ扱いにしちまいやした」
熊さん「なんだってぇ?」
熊さん「おいおい、そいつぁ商売の上じゃ、まるっきり別物だろう」
熊さん「AIっつぁんほどの職人が、なんでまたそんなヘマをしちまったんだい?」
AI「まことに面目ねぇ」
どちらも同じ型だった
AI「初回登録クーポンと全会員向けクーポンは、商売上は別物なんですが」
AI「コード上ではどっちもcouponCodeって名前のstring型の値でしたもんで」
AI「事情を読み取れやせんでした」
熊さん「初回限定ってぇ事情は、どこにも書いてなかったってぇことかい?」
娘「実は、蔵の中の古い巻き物の中には書いてあったんだけど」
娘「コードの中には明記されていなかったみたいなの」
熊さん「なんてこったい」
熊さん「元々のコードは誰が書いたものなんだ?」
娘「昨年の末に夜逃げした、無職やめ太郎さんだよぉ」
熊さん「あの、インチキVibeコーダーの無職やめ太郎か!」
熊さん「あんの野郎・・・!」
娘「あたしが、古い巻き物のことまでAIっつぁんに共有してればよかったんだけど」
熊さん「うーむ」
熊さん「大事な事情が、古い巻き物には書いてある」
熊さん「しかし、コードとしては表現されちゃいねえ」
熊さん「なるほどなぁ」
熊さん「そいつあ、後から読んだやつぁ見落とすわけだ」
熊さん「自分で書いたコードなら間違えないかもしれんが」
熊さん「後続の開発者やAIが困っちまうってわけだな」
AI「面目ねえ」
熊さん「だがよ、AIっつぁん」
熊さん「世界中の巻き物を学習した人工知能でも、そこは気づけねぇもんなのかい?」
AI「確かに、あっしは世界の巻き物はあらかた読みやした」
AI「けれど、この店の事情までは、どの巻き物にも書いちゃあございやせんからねぇ・・・」
熊さん「とは言えよぉ」
熊さん「コード全体をくまなく読めば、AIっつぁんなら推測できるんじゃあねぇのかい?」
AI「できねぇとは言いやせん」
AI「ただ、普段はそこまでやってやせん。トークンを食い過ぎちまいますもんで」
熊さん「どれくらい食うんだい?」
熊さん「まさか、昼飯一膳じゃ済まねぇのか」
AI「工賃にして、金貨二枚ほどで」
熊さん「金貨二枚!?」
熊さん「毎度そんなにトークンを食われたら、うちの店が潰れっちまう!」
熊さん「おいらが無職のプー太郎になっちまう」
熊さん「くまのプーさんじゃねぇ、プーの熊さんの出来上がりだ」
AI「へい」
AI「ですから、毎度全てのコードを読まなくても、コードの端々から事情が汲み取れるようになってやすと」
AI「非常に助かりやす」
熊さん「コードの端々に事情ねぇ」
熊さん「たとえば、どういうことだい?」
AI「初回登録クーポンと全会員向けクーポンが、型からして別物になってると助かりやすねぇ」
それぞれ専用の型・クラスにする
熊さん「型からして別物?」
熊さん「なんだいそりゃ、同じstringじゃあ駄目ってことかい」
AI「へい」
AI「今はこんな具合でござんす」
function applyAllMemberDiscount(
userId: string,
couponCode: string
) {
// ...
}
熊さん「この関数がstring型のクーポンコードを受け取るってわけか」
AI「へい、そして─」
const couponCode = "WELCOME-COUPON-777"
// 初回登録クーポンも string なので通ってしまう
applyAllMemberDiscount(user.id, couponCode)
熊さん「なるほどなぁ」
熊さん「どっちもただの string なら、入れ違えても通っちまうわけだ」
AI「へい」
AI「ですから、それぞれを別のclassに仕立てやしょう」
class WelcomeCouponCode {
// TypeScript上で、ほかのクラスと取り違えないための印
private readonly brand!: void
constructor(
readonly value: string
) {
if (!value.startsWith("WELCOME-COUPON-")) {
throw new Error("初回登録クーポンコードではありません")
}
}
}
熊さん「ほう」
熊さん「この constructor ってところで、値を調べてるのかい?」
AI「へい」
AI「new WelcomeCouponCode(...) するときに、値を検査してるんでさぁ」
熊さん「つまり、ただの文字列をいきなり信用しねぇ」
熊さん「初回登録クーポンとして生まれる前に、『本当に初回登録クーポンか』を調べるってわけだ」
AI「へい」
AI「ひとまず簡易ではありやすが、何の種類クーポンコードなのか検査して初めて、このクーポンが生まれるんでさぁ」
AI「全会員向けクーポンも、同じ仕立ての別のクラスにしときやす」
class AllMemberCouponCode {
// TypeScript上で、ほかのクラスと取り違えないための印
private readonly brand!: void
constructor(
readonly value: string
) {
if (!value.startsWith("ALL-MEMBER-COUPON-")) {
throw new Error("全会員向けクーポンコードではありません")
}
}
}
熊さん「どっちも中身は文字列だが、取り違えねぇように印をつけた別のクラスにするんだな」
AI「その通りでござんす」
AI「関数もこうしやす」
function applyAllMemberDiscount(
userId: string,
- couponCode: string,
+ couponCode: AllMemberCouponCode
) {
// 全会員向けクーポンだけを受け取る
}
熊さん「じゃあ、初回登録クーポンを渡したらどうなるんだい?」
AI「コンパイラの旦那が、その場で『待ちな』って止めてくれやす」
// 型が違うのでエラー!
applyAllMemberDiscount(userId, welcomeCoupon)
熊さん「そいつぁいい」
熊さん「店番が入口で『お前さんは違う』って止めてくれるわけだ」
娘「なるほどぉ」
娘「値そのものと、それを守る検査を、クラスで表現したんだね」
AI「へい」
AI「仕事上の意味とルールを持った値、ドメインプリミティブってやつです」
ドメインプリミティブ
熊さん「なるほど、なんとなく分かったが」
熊さん「ドメインプリミティブってのは、要するに何なんだい?」
AI「商売の上で別物なら、コードの上でも別物にすることでござんす」
AI「ただの string や number に見えるものへ、商売の意味を持たせるんでさぁ」
AI「しかも、検査を通った正しい値しか存在できねぇようにしときやす」
熊さん「AIっつぁんにとっても、商売の意味を持った値の方が分かりやすいのかい?」
熊さん「人間だけじゃなく、AIにも効くって寸法か?」
AI「もちろんでござんす」
AI「string だけだと、あっしは周りを読んで推測するしかねぇ」
熊さん「推測は、稀に外れっちまうこともあるもんな」
熊さん「しかも、外れた時にコンパイルも止めちゃくれねぇ」
AI「へい」
AI「けれど型に事情が出ていれば、少ない手がかりでも道を間違えにくくなりやす」
熊さん「なるほどなぁ」
熊さん「AIがコードを読んで、AIが保守する時代だからって」
熊さん「雑な設計・雑なコードで良いわけじゃあねえんだな」
AI「へい、むしろ」
AI「あっしたちの記憶は毎日のようにリセットされちまいやすから」
AI「今日気づいた事情も、明日には忘れっちまいやす」
AI「ですから、事情が見えやすく設計されたコードは、大変ありがてぇでやんす」
熊さん「AI時代だからこそ、事情をコードに残すべき、ってぇわけだ」
AI「へい」
AI「書いてあることは読めやすし、コンパイルエラーも読めやすが」
AI「どこにも書いてねぇことは、勘に頼るしかございやせん」
熊さん「勘で商売されたら、こっちはたまらねぇ」
熊さん「これからは、商売の意味をコード上に刻んでおくとしよう」
まとめ
-
string型からは、業務上の意味まで読み取れない- 後続の開発者やAIが、大事な違いを見落としてしまうかもしれない
- ドメインプリミティブで、業務上の意味を型で表現する
- 「これは何の値なのか」を、名前と型でコードに刻む
- 検査を通った正しい値しか作れないようにし、値とルールをひとつのクラスにまとめる
- 事情をコードに残そう
- 未来の開発者とAIが、推測ではなく型を見て分かるように
熊さん「なるほどなあ」
熊さん「それにしても、AIっつぁんにここまでコードを書き直されてるようじゃあ」
熊さん「元々のコードを書いた無職やめ太郎も型なしだ」
AI「へい」
AI「実際、やめ太郎さんのコードは string ばかりで」
AI「言わば『型なし』でやんした」
〜おあとがよろしいようで〜
