蒸留所のない夜
十杯目の夜。
路地裏の扉を押した。いつもの重さ。いつもの軋み。——でも、中の空気が違った。
カウンターに、見知らぬ人が座っている。マスターの正面。紙のようなものを広げて、何かを指さしながら話していた。
足が止まった。九回通って初めて、自分以外の客がマスターと話している場面に入っていく。
マスターがこちらに気づいた。軽く会釈する。
「いらっしゃいませ。——どうぞ、おかけになってください」
いつもの穏やかな声。でも一瞬の間があった。対応を切り替える、ほんの小さな呼吸。
いつもの席に座った。二席分の空間を挟んで、ゲスト客がいる。紙に描かれた箱と矢印が覗いた。——先月スマホで見ていた自社のシステム構成図と似た、箱と線の図。
声の断片が耳に入る。「……3年前に設計したんですが……使われていなくて……」
ゆるやかな声だった。怒っていない。急いでもいない。私は心の中で「設計図さん」と呼ぶことにした。図面を広げている姿が、建築事務所の人みたいだったから。
マスターがゲスト客との会話を一段落させて、私の前にグラスを置いた。
カウンターの奥に、見慣れないボトルがある。ラベルが古い。紙の端が巻き上がっていて、他のどのボトルにもない歳月の手触りがあった。
「……それ、ずいぶん古いボトルですね」
自分から聞いている。最初の夜は「おすすめを」としか言えなかった。六杯目の夜に自分で選んで失敗した。八杯目で「なぜこれを選んだのか」が気になり、九杯目で「書かれていないこと」に気づいた。今夜は、ボトルの外見から何かを読み取ろうとしている自分がいる。
「ええ。——軽井沢でございます」
「軽井沢?」
「2000年に閉鎖された蒸留所です。新しいボトルはもう二度と作られません。今夜お出しするのは——この世に残っている、最後の数本のうちの一つです」
グラスに注がれた琥珀色は、これまでのどの夜とも違った。深い。沈んだ琥珀の中に赤みが差していて、照明を通すと宝石のようだった。
一口含んで、言葉が出なかった。複雑なのに、静か。煙の奥に果実がいて、果実の奥にスパイスがいて、スパイスの奥に——時間がいる。何年も何十年もかけて、樽の中で静かに変わり続けた時間の味がした。
「……すごい。これだけの味なのに、もう作れないの?」
「蒸留所がなくなれば——原酒を作る人も、場所も、消えます。設計図は残っていても、もうこの味は再現できません」
設計図。設計図さんが広げていた紙と、同じ言葉だった。偶然かもしれない。でも、マスターの言葉に偶然はないことを、九回の夜で学んでいる。
視線がボトルの並びを辿った。棚からカウンターへ。——取り置きボトル。麻布をかぶったまま。
一席分の距離。
もう手を伸ばすまでもなく、手が届く。麻布から覗く琥珀色が、今飲んでいる軽井沢に似ている。古いウイスキーと、布の下のウイスキー。
八杯目の夜に手を伸ばして「まだ、早いですよ」と止められた。九杯目は伸ばさなかった。今夜も伸ばさない。——でも理由が違う。前は止められたから。今夜は、「もう少し待てる自分がいる」から。
マスターが三者の場を改めた。「——お客さま、ご紹介させていただいてもよろしいですか」。設計図さんが頷く。
「こちらは常連のお客さまです」
設計図さんが軽く頭を下げた。「初めまして。——ソフトウェアのアーキテクトをやっている者です」
「アーキテクト。建築家みたいな?」
少し笑った。「ええ、ソフトウェアの設計図を描く仕事です。——ただ、今夜は……描いた設計図の話を聞いていただきたくて」
PCを開く動作がゆっくりだった。手順書さんのような焦りも、ネクタイさんのような几帳面さも、リーダーさんのような疲弊もない。設計図さんは急いでいなかった。「見てほしい」のではなく、「話を聞いてほしい」という空気。
「3年前、私がこのシステムのレポート機能を設計しました」
画面にコードが映った。
| |
「Report::Formatter は基底クラスです。CSV、JSON、XML——将来いろんなフォーマットに対応できるように設計しました。Role::Exportable も汎用のエクスポート機能として切り出した。compression は、いつか出力を圧縮する日のために」
一呼吸。
「3年経ちました。実装されたフォーマッターは——CSVだけです」
静かに言った。後悔というより、事実を確認しているような声だった。
「……3年」
設計図さんの言葉が、胸のどこかに引っかかった。
「うちの前のCTOが——同じこと言ってたの。『将来必要になる』って作った抽象化層が3つあって。もう3年、一度も使われてない」
意識的に口にしている。先月までの私なら、他人の話に触発された独り言で済んでいただろう。でも今夜は違う。設計図さんの話が自社と重なることを認識した上で、言っている。
「……ええ」
マスターの声が、ほんの少しだけ柔らかかった。普段の「ええ」とは違う。何か馴染みのある言葉を聞いたかのような響き。——設計図さんの方を見ていたから、マスターの表情までは見えなかった。
テイスティング
マスターが画面に視線を移した。コードを上から下へ、ゆっくりたどっていく。
「Report::Role::Exportable——このロールを使っているクラスは、いくつありますか」
「……一つだけです。Report::Generator だけ」
「Report::Formatter を継承しているクラスは」
「Formatter::CSV だけです」
「compression を設定して呼び出したことは」
「一度も」
「format_footer の中身は」
「……空文字を返すだけです」
短い沈黙。マスターが一つずつ並べたのは、使われていないものの一覧だった。問診のような正確さ。
「これは——Speculative Generality と呼ばれます。推測に基づく一般化」
設計図さんは驚かなかった。
「……知っています。Martin Fowler の本で読んだことがあります。『将来のために念のため作ったが、使われなかったコード』」
「お客さまは名前をご存じの上で——それでも消せずにいらっしゃる」
苦い微笑。
「……ええ。頭ではわかっているんです。後任のエンジニアに『これ消していいですか?』と聞かれて。技術的には消していい。わかっているのに——即答できなかった自分に驚きました」
設計図さんに向き直った。
「消せないのは——なぜ?」
「……いつか必要になるかもしれないから、と思っていたんですけどね。でも正直に言うと——自分が設計したものを消すのが怖いんだと思います。消したら、あの設計は間違いだったと認めることになる」
黙って聞いた。経営者として、「損切り」の難しさは知っている。うまくいかなかった事業を畳む判断の重さも。
「……わかる気がする。私も、うまくいかなかった新規事業のページをウェブサイトから消すとき、同じことを思ったことがある」
「失礼ですが——エンジニアの方ですか?」
「いいえ、会社をやってるの。でも——作ったものを手放す痛みは、同じなんじゃないかしら」
マスターが軽井沢のグラスを少し傾けた。琥珀色が照明を通す。
「この蒸留所が閉鎖されたあと——残されたボトルは倉庫を占め、管理記録が必要になり、温度と湿度の管理にコストがかかり続けました。飲まれないまま、場所と手間だけを要求し続けた」
「……維持コスト」
「ええ。使われないコードにも維持コストがかかります。新しい機能を追加する開発者は、その抽象化層を理解しなければならない。テストは動くが目的がわからないメソッドがある。コードレビューで『これは何のため?』と聞かれるたびに、誰かが説明する時間がかかる」
「使われてないのに、置いてあるだけで場所を取る……」
呟くように言った。この言葉が口をついたのは、例のフレーズ——「動いてるから」——を言おうとしたからではなかった。ただ、置いてあるだけで場所を取るものの重さが、今夜はやけにはっきりと感じられた。
「新しいエンジニアがこのシステムに参加したとき——Report::Role::Exportable を見て、何を考えるでしょうか」
設計図さんの表情が変わった。
「……『このロールを使うクラスが他にもあるはずだ』と思う。探し回る。見つからない。『なぜ1クラス用のロールがあるのか』を考え始める。そこに時間を使う」
「存在するだけで、問いを生む。『これは何のため?』 『将来使うのか?』 『消していいのか?』——コードを読む人に、本来不要な認知負荷をかけ続けます」
マスターがカウンターの下からメニューの革張りのファイルを取り出した。ゲスト客に見せるのではなく、自分の手元で開いている。
指先が、メニューの一ページをなぞった。文字があった。しかし——消されている。かすかに凹みが残った跡。インクの名残。かつてカクテルの名前が書いてあった場所。
「私も——誰も注文しないカクテルを、メニューに載せていたことがあります」
十回通って初めて見る仕草だった。マスターが自分の過去について話している。
「いつか誰かが頼んでくれるかもしれない、と思っていました。材料を仕入れ続け、レシピを更新し続けた。しかし——注文は、ついに来なかった」
バーに短い沈黙が落ちた。設計図さんが息を飲んでいる。マスターの言葉が、自分の話と重なったからだ。
「メニューから消すのに、二年かかりました」
目を離せなかった。マスターの声が、いつもと少し違う。穏やかさは変わらないのに、その穏やかさの質が違った。カウンター越しに誰かを諭すのではなく——自分自身に語りかけているような響き。
「——それは、いつの話ですか?」
聞いてしまった。
マスターが顔を上げた。穏やかな微笑。
「ずいぶん昔の話でございます」
それだけだった。それ以上は語らなかった。
ブレンド
設計図さんがコードに目を戻した。
「でも——消した後、本当にその機能が必要になったらどうするんですか。一から書き直しになる」
「ええ。書き直しになります」
少し驚いたように見えた。否定されると思ったのだろう。
「しかし——3年前に推測で書いたコードが、3年後の実際の要件に合致する可能性はどれほどでしょうか」
「……低い、でしょうね。3年前は想像もしなかった要件がたくさんあります」
「YAGNI ——“You Aren’t Gonna Need It”。必要になるまで作るな、という原則です。これはただの怠惰ではありません。推測で作った設計は、実際の要件が判明した時点で、ほぼ必ず修正が必要になる。であれば、実際のユースケースが現れた時点で設計する方が——結果的に正確で、安価です」
「推測が外れた設計は——修正するより一から書いた方が早いこともある」
「ええ。しかもその間、推測的なコードは維持コストを払い続けています。読む人を混乱させ、認知負荷を上げ、本当に必要な変更を複雑にする」
マスターが設計図さんに向き直った。
「今のこのシステムに必要なのは——CSVレポートを出力する機能だけです」
| |
設計図さんが画面を見比べている。Before と After を交互に。
「ロールが消えている。基底クラスも。フォーマッターの階層も。compression も predicate も clearer も——全部なくなっている」
「残っているのは delimiter ひとつだけです。これは現実に必要なパラメータでしょうか」
「ええ。TSV出力が必要な場面が実際にあります」
「つまり、この一つだけが——推測ではなく、実体験に基づいた設計判断です」
「前のコードと今のコードで——やってることは同じなの? CSVを出すって」
設計図さんが頷いた。「同じです。出力は変わりません」
「じゃあ何が違うの? 要らない部分を消しただけ?」
「消しただけ、です」とマスター。「しかし——その"だけ"が大きい。Report::Role::Exportable のことを思い出していただけますか」
設計図さんが頷いた。
「新しいエンジニアはあのロールを見て、『他にも使っているクラスがあるはずだ』と探し回る。見つからない。今度は Report::Formatter を見て、『CSVの他にJSONやXMLがあるはずだ』と探す。——見つからない。compression を見て、『圧縮処理を呼んでいる箇所があるはずだ』と探す。見つからない。その間ずっと、本来やるべきCSVレポートの修正は一行も進んでいません」
「在庫の棚に、ラベルのない箱が置いてあるようなもの?」
マスターがこちらを向いた。
「中身を確認するのに毎回時間がかかって、結局何も入っていないの。それが10個も20個もあったら——本当に必要な在庫が見つからなくなる」
「ええ。まさにそうです」
設計図さんがこちらを見ていた。さっきの「エンジニアの方ですか?」とは違う表情で。
「将来のために準備するのは——経営では当然のことだと思ってた。先を読んで、先に手を打つ。でもそれって、コードだと違うの?」
マスターが静かに答えた。
「経営でも同じではないでしょうか。予測が外れた先行投資にどう対処するか」
「……損切り。事業がうまくいかなかったとき、いつ撤退するかの判断」
「ええ。コードにおいても——使われていない抽象化を残しておくことは、在庫を抱え続けることと同じです。いつか使うかもしれない在庫が場所を取り、棚卸しのたびに確認が必要になる」
「……で、結局使わない」
設計図さんが静かに笑った。
「3年、使いませんでした」
ラストオーダー
設計図さんがPCをゆっくり閉じた。紙も鞄にしまった。急いでいない。
「月曜日に——後任のエンジニアに連絡します。『消していい。あれは消していい』と」
立ち上がる。マスターに一礼。
「3年、言えなかったんです。自分の設計を消していいと。——でも今夜、消された文字を見て、少し楽になりました」
メニューの消えた文字。マスターの指がなぞったあの跡を、設計図さんも見ていたのだ。
こちらに目を向けた。ゆるやかに頷いた。——経営者と設計者。立場は違うけれど、「手放す痛み」を知っている同士の、言葉のない挨拶。軽く手を上げて、扉を押して出ていった。
バーに沈黙が落ちた。軽井沢のグラスに最後の一口が残っている。
さっき飲み込んだ軽井沢の余韻が、まだ舌の奥にいる。蒸留所が消えた酒。設計図が残って、酒だけが残って、蒸留所は消えた。——うちのCTOが残した抽象化層も同じだ。設計した人は辞めて、設計だけが残っている。
「……マスター」
「はい」
「さっきの話——メニューのカクテル。あれは、バーを始める前の話ですか?」
マスターはカウンターを拭いていた。手を止めない。
「ずいぶん昔のことでございます」
同じ返し。でも私は諦めなかった。
「カクテルを、じゃなくて——何かを設計して、誰にも使われなかった経験がある。そういうことですか?」
マスターの手が、一瞬だけ止まった。ほんの一瞬。すぐにまた動き出した。
「……いかがでしょうか。今夜の軽井沢は、お口に合いましたか」
話をそらしている。穏やかに、しかし確実に。十回通えば、マスターが話をそらすときの空気くらいは読めるようになる。
沈黙。まっすぐマスターを見た。
「マスター。——うちのコードを見てほしいんです」
声は震えていなかった。不安でも衝動でもない。九回の夜を通って、ここまで来た。設計図さんの「消していい」。マスターの「メニューから消すのに二年かかった」。使われないものを手放す勇気の話を聞いた夜に——自分の会社のコードに向き合うことを、決めた。
「前のCTOの抽象化層。3年使われてないやつ。辞めたCTOしか知らない設定。手順書がないとデプロイできないこと。矢印がたくさん並んでるのも。——全部、名前がある種類の問題なんでしょう?」
九回分の断片が口をついて出た。正確じゃないかもしれない。名前を間違えているかもしれない。でも、「問題がある」と知っている。「匂い」がわかるようになったことを、今夜はっきりと自覚していた。
マスター、カウンターを拭く手を止めた。こちらを見た。穏やかだが、いつもとほんの少し違う目。九回見てきた接客の顔ではない何かが、一瞬だけ透けて見えた——ような気がした。
「もう少しだけ、待ちましょう」
「……なぜ?」
「あと二杯分、お付き合いいただけますか」
答えになっていない。でも、拒絶でもなかった。——「待ちましょう」と言ったのは、待つ先に何かがあるからだ。
グラスを空にして、カウンターに置いた。
視線が取り置きボトルへ行く。一席分の距離。もうすぐ手が届く。麻布の下の琥珀色が、さっき飲んだ軽井沢に似ている気がした。
「このボトルも——いつか飲めるんですか」
「ええ。もう少しだけ」
同じ言葉。コードを見てほしいと頼んだときと、同じ返し。
——あのボトルと、私のコードは、どこかで繋がっているのだろうか。
「おやすみなさい、マスター」
「おやすみなさいませ。お気をつけて」
扉を押した。路地裏の夜風。
マスターはカクテルを消すのに二年かかったと言った。設計図さんは三年越しで「消していい」と言えるようになった。——私は、どれだけかかるんだろう。
でも今夜、「見てほしい」と言えた。それだけは確かだ。
「もう少しだけ」。あと二杯。マスターが何を待っているのかはわからない。でも——九回待ったのだから、あと二杯くらいは待てる。
路地裏を歩きながら、軽井沢の余韻を舌で探した。もう二度と作れない酒の味。——でも味を覚えている。覚えていることには、意味があるはずだ。
🥃 マスターのテイスティングノート
本日の銘柄: 軽井沢(終売蒸留所)
お客さまの症状: 推測的汎化(Speculative Generality)
ノージング(香り)── 問題の検知
実装が1つしかない抽象クラス。1つのクラスでしか使われていないロール。predicate や clearer が定義されているのに一度も呼ばれていない属性。空の format_footer。——「将来使うかもしれない」という推測の残り香が漂っていたら、Speculative Generality を疑いましょう。
パレット(味わい)── 問題の本質
使われないコードにも維持コストがかかります。新しいメンバーは「これは何のため?」と問い、答えが返ってこない。認知負荷が上がり、コードレビューのたびに「消していいのか」が議論され、結局誰も手をつけない。存在するだけで問いを生む——それが推測的汎化の味です。
フィニッシュ(余韻)── 解決の方針
YAGNI ——必要になるまで作らない。不要な基底クラスを消し、ロールを元のクラスに戻し、使われていない属性を削除する。推測で設計した抽象化は、実際の要件に出会ったときにほぼ必ず修正が必要です。必要になった時点で作る方が、正確で安価です。
ペアリング(相性の良いパターン)
- YAGNI 原則(You Aren’t Gonna Need It)
- Collapse Hierarchy(階層の折りたたみ)
- Three Strikes and You Refactor(3回則)
「設計図は残っていても——蒸留所がなくなれば、もうあの味は再現できません」
