七杯目の夜。いつもの席。
でもグラスはまだ空のまま、カウンターの木目を見つめていた。
先週——「動いてる……わよね?」。あの言葉が、帰り道だけじゃなく、翌日のオフィスでも、次の日の会議でも、ふと浮かんでは消えた。答えは出ていない。動いているのか、動いていないのか。そもそも「動いている」ってどういう状態のことを言うのだろう。
マスターが目の前にグラスと二本のボトルを並べた。顔を上げる。
見慣れないボトルが二本。一本はスコッチウイスキー——ラベルに見覚えはない。もう一本は金色のリキュール。背が低くて丸みのあるボトルに、蜂蜜色の液体が詰まっている。
「今夜は……ウイスキーじゃないんですか?」
「カクテルでございます」
マスターが金色のボトルを手に取った。
「ラスティネイル。スコッチとドランブイの二つだけで作ります」
二つだけ。先週のアードベッグ ウーガダールは一種類のウイスキーで完結していたけれど、今夜は二種類を混ぜる。でも「二つだけ」というのは——シンプルなのだと思う。
「シンプルなんですね」
マスターが氷を入れたロックグラスにスコッチを注いだ。琥珀色が氷に触れて揺れる。次にドランブイ——金色のリキュールを、スコッチの上にゆっくりと重ねた。泡立てず、液面をすべらせるように。
「配合の順序と量が、すべてを決めます」
差し出されたグラスを受け取る。鼻を近づけると、蜂蜜のような甘さがまず来て、その奥にスコッチの温かみがある。口に含んだ。
甘い。でも甘すぎない。薬草のような複雑な香りが鼻に抜けて、スコッチのアルコールが喉をじんわり温める。先週のアードベッグの衝撃とは正反対の、なめらかさ。
「……おいしい。甘いけど、甘すぎない」
マスターが一拍置いた。それからグラスの下にコースターを滑り込ませながら。
「——では。もう一杯、逆の順番でお作りしましょうか?」
「え? 逆?」
マスターは微笑んだだけで、すぐには動かなかった。
来店——カクテルも、コードも、順番が命
カウンターの端に目をやった。取り置きボトル。麻布をかぶったままのあのボトルが——前回ずれた麻布はそのまま、琥珀色の液体が覗いている。
先週はこの琥珀色が照明を受けて輝いていた。今夜は——同じバーの同じ照明なのに、落ち着いた色に見える。暗い、というのとは違う。静かな色。
同じものなのに、印象が違う。
同じものでも、見るタイミングが変われば違って見えるのだろうか。ぼんやりとそう考えて、自分の思考に少し驚いた。先週までの私なら、こんなことは考えなかった。
扉が勢いよく開いた。
リュックを背負った短髪の男性。スタートアップのロゴが入ったTシャツにジーンズ、スニーカー。席に着く前にリュックからノートPCを取り出している。歩きながらファスナーを開けて、座る動作とPCを置く動作がほぼ同時。
「すみません、ここ——あ、合ってますよね。コードの相談できるバーって。座っていいですか、モスコミュールお願いします、あと、ちょっと急いでて」
注文と挨拶と前置きを一息で片づけようとしている。マスターが「いらっしゃいませ」と返す隙もない。前回のフリーランスさんも軽快だったけれど、彼女のそれは「身軽さ」だった。この人の速さは違う。追われている人の速さだ。
手順書さん、と心の中で呼んだ。すべてに段取りがある人。席に着く、PCを出す、ブラウザを開く——その手際が、決められた段取りを毎日こなしている人の動きに見えた。
マスターがモスコミュールを用意する間、手順書さんはPCの画面を自分に向けてコードを確認していた。指がカーソルキーを叩くリズムが速い。モスコミュールを受け取って一口。それからこちらを向いた。
「CTOやってるんですけど——今朝、プロダクションが落ちたんです」
声に切迫感があった。今朝の出来事がまだ生々しいのだと思う。
「init() のバグで。いや、バグじゃないのかな。init() を呼ばないと動かないコードがあって、新人がその順番を間違えたんです。手順書には書いてあったんですけど、手順書を読む前にデプロイしちゃって」
その話に、頭の中に自社の光景が重なった。毎月のデプロイの日。エンジニアたちがプリントアウトした手順書を横に置いて、一行ずつ指差し確認している。リリース中はSlackが凍りついたように静まり返る。あの緊張感。
——他人事じゃない。
マスターはラスティネイルのドランブイのボトルを元の位置に戻しているところだった。その手が——いつもより丁寧に見えた。ボトルの底を棚の上の定位置に、正確に、合わせるように置いている。順番通りに、定位置に。
気のせいかもしれない。手順書さんの話に引きずられて、マスターの所作に「順序」を読み取ろうとしているだけかもしれない。
手順書さんがPCをマスターに向けた。
「これが今朝落ちたコードです。僕が一年前に書きました」
| |
「load_config → connect → migrate → deploy。この順番でしか動かないんです。でもこの順番、コードのどこにも書いてない」
手順書さんが画面を指差して、付け加えた。「手順書にだけ書いてあります」
私は画面を横から覗き込んだ。コードの中身はわからない。でも die という単語が何度も出てくるのは見える。
「die って……止まるの?」
「順番を間違えると、そこでエラーになって止まります。今朝みたいに」
テイスティング——同じ材料で、別の味
マスターが二つ目のロックグラスをカウンターに置いた。
今度は——先にドランブイを注ぐ。金色のリキュールがグラスの底に溜まる。次にスコッチを、上から重ねた。さっきと逆だ。
「先ほどの問いに、お答えしましょう」
さっきの「逆の順番で作りましょうか」。あの問いの答え。
一口飲んだ。
——全然違う。
甘さが口の中を占領している。スコッチの複雑さが奥に沈んで、出てこない。舌に残るのは砂糖水のような平坦な甘さだけ。香りの奥行きがなくなっている。さっきの一杯で感じた薬草と蜂蜜のハーモニーが、どこにもない。
「……別の飲み物みたい」
マスターが二つのグラスを並べた。正しい順番で作った一杯と、逆順の一杯。見た目はほとんど同じ。色も、量も。
「同じ材料でございます。同じ量を使っています。違うのは——順番だけでございます」
手順書さんがPCから目を上げて、二つのグラスを見比べた。
「こっちに来て、飲んでみませんか」
私が声をかけた。手順書さんが少し躊躇してから、一席ずれてきた。正しい順番のほうを一口、逆順のほうを一口。
「あ……本当に全然違う」
「でしょう。同じ材料なのに」
手順書さんが苦笑した。「僕のコードと同じですね。四つのメソッド、全部あるのに、順番が違うだけで壊れる」
マスターがゲスト客のコードに目を移した。
「お客さま。この四つのメソッド——load_config、connect、migrate、deploy——は、いずれも何も返していません」
「ええ。内部の状態を設定して——」
「代わりに、同じ器——$self——の中身を書き換えています。connect は _config が設定済みであることを前提にしていますが、その前提は connect の引数には現れません」
マスターが二つのグラスを指した。引数に現れない前提——見えない前提。言葉の意味を噛み砕いている間に、マスターが続けた。
「このカクテルは、順番を間違えても見た目は同じです。色も、量も変わらない。しかし——」
「味が全然違う」と私。
「ええ。見た目で気づけない問題は、味わうまでわからない。コードも同じです。die で止まるまで、誰も気づかない」
「見えない依存って……手順書にしか書いてないってこと?」
「そのとおりです。手順書は人の記憶力に依存します。そして人は——忘れます」
マスターがカウンターの上の二つのグラスの間に、静かに手を置いた。二つのグラスの「間」にあるもの——目には見えない、順番という名の依存。
「これを——Temporal Couplingと呼びます。時間的結合」
「時間的……?」と手順書さん。
「メソッドが特定の順番で呼ばれることを暗黙に要求する結合です。その順番は引数にも戻り値にも現れず、ドキュメントと慣習の中にだけ存在します」
手順書さんが呟いた。
「暗黙に、か。僕が書いた die は——暗黙のルールを実行時にチェックしてるだけだったんだ」
「暗黙のルール」という言葉が、胸の中で引っかかった。自社のデプロイ手順書——あの三十ステップは、全部「暗黙のルール」を文書にしたものだ。でも文書にしたって、今朝の新人みたいに、間違える人は間違える。
「ねえ、手順書って、ステップが増えたら困らない? うちは三十あるんだけど」
手順書さんがこちらを向いた。
「最初は二ステップだったんですよ。機能を足すたびに増えて、今は五十ステップ。しかも手順書を更新する手順書まであります」
「手順書の手順書……!」
思わず笑ってしまった。手順書さんも苦笑いしている。でも二人とも、笑いの底に「笑い事じゃない」がある。
ブレンド——構造に、語らせる
マスターが正しい順番のラスティネイルと、逆順のラスティネイルを横に並べたまま、口を開いた。
「カクテルは、液体の物理的な性質に順番を委ねています。比重の違い——スコッチが先、ドランブイが後。物理法則がレシピの正しさを保証している。——しかしコードは違います。順番を物理法則の代わりに、構造として語らせることができます」
「構造として?」
手順書さんが身を乗り出した。さっきまでの早口が消えている。
「load_config は設定情報を返す。connect はそれを受け取って、接続情報を返す。migrate はそれを受け取って——」
手順書さんの目が動いた。
「戻り値を、次のメソッドの引数に渡す。void をやめるってことですか」
「第一歩はそこでございます。しかし、もう一歩踏み込めます」
マスターが手順書さんのPCの横に、自分のメモを置いた。整った字で書かれたコードの骨格が見える。手順書さんと一緒に覗き込んだ。
「各段階を、それぞれ別の型として表現する。設定を読み込んだ状態は Configured。接続した状態は Connected。マイグレーション済みの状態は Ready」
手順書さんが指を折って数えている。三つ。マスターが続けた。
「Configured は connect メソッドだけを持ちます。deploy メソッドは——存在しません」
「メソッドが……存在しない?」
「ええ。手順書に『この順番で呼んでください』と書く代わりに、その段階でできることだけをコードが許すのです」
| |
画面を横から見て——コードの中身はやっぱりわからない。でもさっきの die が一つもないのは見える。そして package が三つある。一つだったものが三つに分かれている。
「メソッドが少なくないですか? さっきは load_config、connect、migrate、deploy の四つがあったのに」
「四つの機能は残っています。ただし、それぞれの段階が別のクラスになっているのです」
手順書さんがゆっくりコードを読んでいる。さっきまでの指の速さが嘘みたいに、一行ずつ追っている。
「Configured には deploy がない。connect しかできない。つまり——configure した直後に deploy しようとしたら」
「Can't locate object method "deploy" が返ります。メソッドが見つかりません」
手順書さんが顔を上げた。
「でもそれ、さっきの die "Not migrated!" と同じ実行時エラーじゃないですか。何が違うんで——」
言いかけて、止まった。
「——いや、違う。タイミングが違う。die は処理を途中まで実行した結果として失敗する。メソッド未定義は呼ぼうとした時点で止まる」
マスターが静かに頷いた。
「ええ。die の前に、connect が呼ばれてデータベースに接続が張られるかもしれません。migrate が途中まで走ってスキーマが壊れるかもしれません。——しかしメソッドが存在しなければ」
「何も起きない」
「そもそも何も始まらない。副作用が残らないのです」
手順書さんが画面に目を戻した。指が止まっている。
「手順書がいらなくなる。各ステップが次のステップに必要なものだけを渡す。渡さなければ次に進めない。手順書じゃなくて、コード自体が——」
マスターが穏やかに引き取った。
「——構造が、順番を語るのでございます」
マスターがメモに一行だけ書き足した。使い方。
| |
「一行でございます。順番を間違えようがありません。connect の戻り値は Connected であり、Connected には migrate しかございません」
手順書さんが呟いた。
「メソッドチェーンに見えるけど、Fluent Interface とは違いますね。Fluent は同じオブジェクトを返す。これは別の型を返す」
マスターが頷いた。
「ええ。各段階は不可逆です。Configured に戻ることはできない」
カウンターの二杯のラスティネイルに、ちらりと目をやった。ちょうど——ラスティネイルに注いだドランブイを、ボトルに戻せないように。
私は手順書さんのほうを向いた。
「ねえ、手順書さん。うちの三十ステップも、同じ考え方でいける?」
手順書さんが考え込んだ。
「全部は無理かもしれません。でも、 die で止まるまで気づけない順序の問題を、構造で防げるものは確実にあります」
「手順書を減らすって、怖くない? ルールがなくなるみたいで」
「手順書って——ルールじゃないんですよね。人へのお願いなんです。お願い、この順番で呼んでください、って。でも構造にすれば、お願いしなくていい」
マスターがカウンターを拭いた。ゆっくりとした手つきで。
「手順書が不要になる設計。それが、Temporal Coupling の解消でございます」
ラストオーダー——同じ琥珀色が、違って見える夜
手順書さんがPCを閉じた。さっきまでの早口が嘘のように、ゆっくりとリュックにしまっている。
「明日、まず DeployService から—— いや、あの五十ステップの手順書を開くところからですね。どのステップが『お願い』で、どれが『構造で語れるか』を仕分けてみます」
立ち上がる。モスコミュールのグラスは空になっていた。
私のほうを向いて、少し照れたように笑った。
「手順書さんって呼ばれたの、初めてです。——でも、もう手順書の人じゃなくなりたいな」
軽く頭を下げて、扉を出ていった。来たときより歩く速度が遅い。急いでいない。
バーに静けさが戻った。
カウンターには二杯のラスティネイルがまだ並んでいる。正しい順番の一杯と、逆順の一杯。
正しい順番のほうを手に取った。最後の一口。蜂蜜と薬草の余韻が、ゆっくり喉を滑り落ちた。最初に飲んだときと同じ味。あの「おいしい」は正しかった。
逆順のほうには手をつけない。もう答えは知っている。
「マスター」
「はい」
「うちのデプロイ……三十ステップの手順書。あれ全部、手順書さんのコードと同じ構造なのかな。手順書がないと動かないって——」
言葉を探した。言いたいことが、まだ形になっていない。
「動いてるのに手順書がないと動かないって——それって本当に動いてるっていうの?」
マスターがグラスを下げた。私のグラスと、逆順のラスティネイルのグラス。どちらも同じ形のロックグラスだ。でも中身は全く違うものだった。
「ラスティネイルは二つの原酒だけでございます。材料は至って単純です」
マスターがグラスを洗いながら、穏やかに続けた。
「——けれど、注ぐ順番が変われば、まったく別の酒になる」
一拍の間。
「手順に頼るか。構造に語らせるか。——その違いは、お客さまの舌が、今夜証明してくださいました」
席を立った。
扉に向かう途中、カウンターの端に視線が引き寄せられた。取り置きボトル。麻布はまだずれたまま、琥珀色が覗いている。最初に席に着いたときは「落ち着いた色」に見えたのに、今は——照明は変わっていないのに——少しだけ明るく見える。
同じものでも、見る順番で違って見える。今夜カクテルで学んだことだ。
扉に手をかけた。押す。路地裏の夜気が頬に冷たい。
ふと、手順書さんの言葉が浮かんだ。「もう手順書の人じゃなくなりたい」。
私のところの手順書は、誰が書いたんだろう。そしてあの手順書がなくても動く仕組みにできるんだろうか。
——もしかして、うちも同じなのかもしれない。
扉が閉まった。
🥃 マスターのテイスティングノート
本日の銘柄: ラスティネイル
お客さまの症状: 時間的結合(Temporal Coupling)
ノージング(香り)── 問題の検知
init()、setup()、configure() のように、「先にこれを呼ばないと動かない」メソッドが並んでいたら、Temporal Coupling を疑いましょう。とくに void を返すメソッドが内部状態を書き換え合っているとき——順序依存は確実にそこにあります。
パレット(味わい)── 問題の本質
メソッドのシグネチャに順序の情報がないこと——これが問題の核心です。どのメソッドを先に呼ぶべきかは、手順書やコメントの中にしか存在しません。人の記憶力に依存する設計は、忘れた瞬間に壊れます。材料が同じでも順番が違えばまったく別の味になるカクテルと同じです。
フィニッシュ(余韻)── 解決の方針
各段階を別の型として表現し、各メソッドが「次の段階のオブジェクト」を返す設計に変えます。Configured は connect だけを持ち、deploy は存在しない——コード自体が「この段階ではこれしかできない」と語る構造です。手順書が不要になります。
ペアリング(相性の良いパターン)
- Builder パターン(段階的構築)
- Immutable Object(不変オブジェクトによる状態管理)
- Typestate パターン(型で状態遷移を表現)
「手順に頼るか。構造に語らせるか。——その違いは、お客さまの舌が証明してくださいました」
