「私の設計は正しい。だって設計書通りに作ったんですから」
10年間、この言葉を盾にしてきた。
入社3年目に作った「ドキュメント生成スクリプト」は、いつの間にか社内標準ツールに昇格していた。議事録、日報、週報、障害報告書——17種類のテンプレートを自動生成するこのツールは、私の最高傑作だ。SIer歴10年、サブリーダー。34歳。後輩たちには「三橋さんのツール、毎日使ってます」と感謝され、私のデスクの付箋には改善要望が日々貼られている。
昨日、上長からSlackが飛んできた。
「来期から全ドキュメントに
approval_statusフィールド追加ね。コンプライアンス対応」
1フィールドの追加。簡単な作業のはずだった。
コードを開いた。17種類のテンプレートクラス。それぞれのコンストラクタに、全フィールドがハードコードされている。approval_status を追加するには、17箇所の new_template メソッドを、1箇所ずつ、手で、直す必要がある。
4時間かけて全部直した——はずだった。
翌朝、経理部から連絡が来た。「障害報告書のフォーマットが壊れてます」。確認すると、3箇所の修正漏れ。メタデータの division フィールドが空になっている障害報告書が、お客様に送られていた。
「完璧な設計書通り」に作ったはずの私のコードが、1フィールドの追加で崩壊した。
来院
「コード診療所」——同僚の木村が教えてくれた場所だ。雑居ビルの2階にあるらしい。
「三橋さんのツール、一度診てもらったほうがいいですよ。俺も前に行ったことあるんですけど、マジで腕がいいんで」
正直、私のコードに問題があるとは思っていない。設計書通りに作った。テストも通っている。おそらく環境の問題——Perlのバージョンか、モジュールの依存関係か。プロの目で確認してもらえれば、そう証明できるはずだ。
磨かれたリノリウムの廊下を進む。重厚な鉄の扉に、「コード診療所」とだけ書かれたプレート。
ドアを引いた瞬間、O’Reillyの技術書が視界を埋め尽くした。天井近くまで積み上げられた本の壁。その隙間から、受付カウンターが覗いている。
「いらっしゃいませ。ご予約の方ですね?」
白衣を着た女性——助手のナナコさんが、穏やかに立ち上がった。
「はい。三橋です。ドキュメント生成ツールの件で」
「大丈夫ですよ、ここはコード診療所です。症状をお聞かせくださいね」
ナナコさんの背後。トリプルディスプレイに向かう男の背中。HHKBの打鍵音がぴたりと止まった。
「……何種類?」
振り返りもしない。低い声だけが聞こえた。
「え?」
ナナコさんが微笑んだ。「テンプレートの種類数をお聞きしていますよ」
「17……種類、です」
ドクターの肩が微かに揺れた。溜息——のようにも聞こえたが、背中からでは判別がつかない。
触診
ドクターがゆっくりと椅子を回転させた。初めて顔が見えた。鋭い目つき。表情は無い。
「見せろ」
私はノートPCを開き、コードを表示した。
| |
ドクターがスクロールした。次のクラス。また同じ構造の new_template。その次も。その次も。17クラス。company も division も fiscal_year も、全て同じ値が17回繰り返されている。
指がピタリと止まった。
「……17回。毎回 new」
「はい。各テンプレートは独立しているべきですから。設計書にもそう書いて——」
「遺伝子異常(Genetic Disorder)」
診断
ナナコさんが穏やかに、しかし的確に補足した。
「先生がおっしゃっているのは、遺伝子異常の連鎖ですね。同じ 遺伝子 ——共通フィールドが、17の細胞——テンプレートクラスにハードコードされています。1つの遺伝子変異—— approval_status の追加が入ると、17の細胞すべてに転移するんです」
「でも、それぞれ独立したクラスにするのが正しい設計——」
ドクターが無言で画面をスクロールした。metadata のブロック。17クラス、完全に同一のハッシュリファレンス。
「……転移」
ナナコさんが頷いた。「17の臓器に、まったく同じ遺伝子がハードコードされているんです。company も fiscal_year も、全部同じですよね? 1箇所を変異——修正すれば済むはずのものが、17箇所に分散しているから、修正漏れが起きるんですよ」
「3箇所の修正漏れで障害報告書が壊れたのは——」
「はい。転移した遺伝子異常が、3つの臓器で発症したということですね」
ドクターがさらにスクロールした。ある箇所で指が止まる。
「……癒着」
ナナコさんの表情が少し厳しくなった。
「もうひとつ、副症状がありますね。先生が指しているのは metadata のハッシュリファレンスです。もし誰かがこのテンプレートを “コピー” して使おうとした場合——浅いコピーでは、ネストされた metadata が参照を共有します。一方を変更すると、もう一方も壊れる。臓器の癒着ですね」
「え……そこ、バグ出てませんけど……」
「……まだ」
ドクターの一言が、診察室に重く響いた。
ナナコさんが小さく付け加えた。「時限爆弾ですよ。今は発症していないだけです」
私は黙った。「完璧な設計」の中に、2つの病巣が潜んでいた。
処方箋
ドクターが振り返り、トリプルディスプレイに向き直った。黙々とキーボードを叩き始める。
ナナコさんが私の隣に来て、画面を指差しながら解説してくれた。
「先生は今から、 原型——プロトタイプ を作ります。17の設計図を毎回引き直すのではなく、完成品を1つ保管して、必要な時に複製するんですよ」
原型の培養
まず、すべてのテンプレートの「原型」となるクラスが定義された。
| |
「ここが遺伝子の 原本 です」ナナコさんが画面を指差した。「共通フィールドは 1箇所だけ に定義されます。17箇所のコピペは、もう必要ありません」
「でも、テンプレートごとに sections は違いますよね? 議事録は『出席者・議題・決定事項・次回予定』で、日報は『本日の作業・進捗・課題・明日の予定』で——」
「ええ。だから clone するんです」
複製の処方
ドクターの指が clone メソッドを打ち込んでいく。
| |
「dclone」ナナコさんが言った。「Storable モジュールの 深層複製 ですね。ネストされた metadata や sections も、参照共有ではなく完全に独立したコピーを作ります。先ほどの癒着——shallow copy の時限爆弾は、これで除去されますよ」
「dclone で全部コピーしてから、%overrides で差分だけ上書き……」
「そうです。原型を壊さず、必要な部分だけカスタマイズするんです」
保管庫の構築
続いて、プロトタイプの「保管庫」—— Registry が組み上がっていく。
| |
「register で原型を保管し、create で複製する。シンプルですね」ナナコさんが微笑んだ。
外科手術
ドクターがデフォルトのレジストリを構築していく。
| |
「あ」
私は声を上げた。17クラスに散らばっていた共通フィールド—— department、author、date、version、metadata——が、すべて Prototype クラスの1箇所に集約されている。各テンプレートの register では、 違う部分だけ ——type と sections だけを指定している。
「テンプレートを使うときはこうなります」ナナコさんが画面を指差した。
| |
「doc と doc2 は完全に独立しています。deep clone ですから、metadata を変更しても——」
「もう一方には影響しない……」
「そうです。癒着は解消されました」
ドクターがテストを実行した。画面に結果が流れる。
| |
全テスト、グリーン。
「そして」ナナコさんが付け加えた。「来期の approval_status 追加ですが——」
「Prototype クラスに has approval_status を1行追加するだけ……?」
「はい。17箇所の手修正は、もう必要ありませんよ」
術後経過
修復されたコードは、驚くほどシンプルだった。
17個の独立したクラスファイルは、1つの Prototype クラスと1つの Registry に統合された。フィールド追加は原型を1箇所変えるだけ。各テンプレートは clone で原型の遺伝子を受け継ぎ、sections だけがそれぞれの個性を持つ。
私はノートPCを閉じ、帰り支度を始めた。PCを鞄にしまおうとした拍子に、天板に貼っていた付箋——後輩たちからの改善要望メモ——が1枚、ひらりと滑り落ちた。
拾おうとしたが、ドクターのほうが速かった。一瞬——ほんの一瞬だけ、メモに目を落とした。そして何も言わずに、天板に貼り直した。
(……先生が、私のメモを読んだ?)
胸が少し熱くなった。後輩たちの要望が書かれたあのメモ。「日報に『気づき欄』を追加してほしい」「障害報告書にSeverityフィールドがほしい」。私が10年間、一枚一枚集めてきた声。
(まさか先生は、あのメモの内容を覚えた—— clone した——のでは。私のチームの声を、先生の頭の中にも複製してくれた……?)
ナナコさんが苦笑した。
「先生はメモを拾っただけですよ。風で飛ばないように戻しただけだと思います」
ドクターは既にトリプルディスプレイに向き直っていた。視線が一瞬泳いだように見えたのは、おそらく気のせいだ。
ドクターが背を向けたまま、ぽつりと言った。
「感謝は、このコードに」
ナナコさんが穏やかに微笑んだ。
「先生が求めるのはお金ではなく、コード品質へのコミットメントです。17個の new を、もう増やさないでくださいね。お大事に」
重厚な鉄の扉を押して廊下に出ると、HHKBの打鍵音が遮断された。
私は廊下で立ち止まり、スマホを開いた。Slackの上長のメッセージが表示されている。
「来期から全ドキュメントに
approval_statusフィールド追加ね」
10年前の私なら、17個のクラスファイルを開いて、1つずつ approval_status を書き加えていただろう。
今日の私は、Prototype クラスに has approval_status を1行追加する。
「完璧な設計図」を信じていた。でも本当に「完璧」だったのは、完成品を1つ作り、それを複製して差分だけを変える——という、もっとシンプルな原則のほうだった。
17種類の子供たちは、もう独立して生きていく必要がない。原型の遺伝子を受け継ぎながら、それぞれの個性だけを纏って。
処方箋まとめ
| 症状 | 適用すべき | 経過観察 |
|---|---|---|
同じフィールド構成のオブジェクトを複数箇所で new している | ✓ | |
| フィールド追加のたびに複数クラスを手修正する必要がある | ✓ | |
| ハッシュリファレンスの浅いコピーでネストデータが参照共有している | ✓ | |
| テンプレートの「種別」は違うが「構造」はほぼ同じ | ✓ | |
| オブジェクトの構造がシンプルで種別が2〜3種類しかない | ✓ | |
| 各オブジェクトの構造が根本的に異なり、共通部分がほぼない | ✓ |
治療のステップ
- 原型(Prototype)クラスの定義 — 共通フィールドを1つのクラスに集約し、
cloneメソッドを実装 - 深層複製の導入 —
Storable::dcloneを使い、ネスト構造も含めた完全独立なコピーを保証 - 差分カスタマイズ —
clone(%overrides)で変更が必要な部分だけを上書き - Registry の構築 — テンプレートの原型を名前で管理し、
createで複製を取得 - 既存クラスの統合 — 17個の独立した
new_templateを、Registry からのcreateに順次置換
助手より
「設計書通りに作ったから正しい」——その信念は、決して間違いではありませんでした。ただ、設計にはもう1つ大切な原則があります。 「同じことを繰り返さない」 ということです。
先生は無口ですが、患者さんの付箋メモを拾ったとき、あの一瞬だけ目を落としていたのは事実ですよ。もしかしたら後輩の方々の声が、先生の中にも少しだけ残ったのかもしれませんね。
10年間育ててきたツールは、これからもっと育てやすくなるはずです。17の子供たち、良い遺伝子を受け継いでくれますように。お大事に。
——ナナコ
