仮説階層モデル
従来の仕様は、実装前に正解を固定し、実装をその正解へ一致させるための記述だった。
しかし、ソフトウェア開発で本当に不確実なのは、実装が仕様通りに作れるかだけではない。そもそもその機能に価値があるのか、ユーザーが想定通りに行動するのか、業務的に何を正しいとみなすべきか、どの相互作用なら使えるのかも不確実である。
LLMとエージェントによって、選択肢生成・プロトタイピング・実装修正のコストは大きく下がった。これにより、仕様は「正解を先に固める文書」から、「仮説を構造化し、どのエビデンスで採択・棄却するかを明示する文書」へ変わる。
したがって、LLM時代の仕様とは、何を作るかを詳細に命令する文書ではない。価値仮説・行動仮説・ドメイン仮説・相互作用仮説・実装仮説を分け、それぞれの採択基準と観測方法を明らかにする、学習のための構造である。
ただしひとくちに「仮説」といっても、そこには層がある。
実装の作りが要件を満たすかという仮説
ユーザーがそう行動するかという仮説
そもそもこの機能に価値があるかという仮説
これらを別物として扱わないと噛み合わない。安くなったのは下位レイヤー(実装やテストで決着がつく仮説)のコストである。上位レイヤーに行くほど、リリース・ユーザーの反応・運用データといった現実世界の観測が律速になり、検証期間も意思決定者も変わる。どの層をどのエビデンスで棄却するかを意識的に選ぶ必要が、以前より高まるだろう。
仮説階層
仮説とは、不確実性を含んだ主張である。「ユーザーは履歴から再注文する」「クエリは500ms以内に返る」のいずれも、書いた時点では真偽が決まらない。
エビデンスとは、その不確実性を減らすために集める観測(ex. アクセスログ、ヒアリング、ベンチマーク結果、リリース後の指標)。
学習とは、エビデンスを元として仮説の不確実性が減ることを指す。学習の結果、仮説は採択されるか、棄却されるか、書き換えられる。Eric RiesはこれをValidated Learningと呼んだ。 仮説は、不確実性の質の違いによって5層に分かれる。
table:table
層 問い 例
価値仮説 それは価値があるか 注文履歴があると問い合わせが減る
行動仮説 ユーザーはそう行動するか ユーザーは履歴から再注文する
ドメイン仮説 業務的に何が正しいか キャンセル済み注文も履歴に表示すべきである
相互作用仮説 どのUI/操作なら使えるか 日付降順の一覧が最も探索しやすい
実装仮説 この作りで要件を満たせるか 注文テーブルへの読み取り専用クエリで、想定負荷下でもレスポンスタイムが要件を満たす
行動仮説とドメイン仮説は実務だと混ざりやすい。「キャンセル済み注文も履歴に表示すべきだ」は一見ドメインの話だが、根拠が「表示しないとユーザーが再注文を諦めるから」ならそれは行動仮説で、検証はユーザー観察に向かう。根拠が「会計上、注文の発生事実は消えないから」ならドメイン仮説で、検証は業務ルールとの整合に向かう。同じ仕様でも、何を根拠にしているかで層が変わり、当てるエビデンスも変わる。
「仕様」の変容
ビルドコストが下がると、仕様の役割そのものが変わる。
これまで仕様は、実装の前に決めて、できるだけ動かさないものだった。作るのに時間も金もかかるので、作り始める前に十分に詰めて、後戻りを減らすしかなかった。仕様変更はコストであり、できれば避けたいものとして扱われてきた。
作るコストが下がると、この前提が崩れる。決めてから作るより、作って確かめたほうが安い場面が出てくる。そうなると仕様は「凍結しておくもの」ではなく「エビデンスをもとに書き換えていくもの」に変わる。仕様変更はコストではなく、学習結果そのものになる。Martin FowlerがSDDに関する考察で「効果的なSDDツールは反復的なアプローチを支えるものでなければならない。仕様を最初に全部作ろうとするウォーターフォール問題を避けることが肝要だ」と述べているのも、この方向の延長にある。 仕様の中身は次のように変わる。
安く検証できる範囲では曖昧さを残したまま進めてよい
仕様を分けて書くこと自体は別段新しくはないが、その層は仮説の種類をもとにする。
従来:工程で切る(要件→設計→実装)。上流が決まるまで下流に進めない
これから:仮説の種類で切る(価値・行動・ドメイン・相互作用・実装)。下層の実装仮説をプロトタイプで先に検証して上層の行動仮説にフィードバックする、といった逆向きの動きが許される
仕様変更の扱いも反転する。従来は「仕様を動かさないために、最初に頑張って固める」。これからは「仕様を動かしやすく書くために、判断の基準を明示する」。
仕様駆動開発
現在の仕様駆動開発には、次のような批判がある。
これらの批判が当たっているSDD実践は、たいてい仕様を従来のまま(事前に固める設計文書として)扱っている。詳細を書き切ってからエージェントに渡し、出力を仕様と突き合わせてレビューする。仕様の量と完備性が品質を担保するという前提で動くので、層の区別なくすべてを書こうとして肥大化し、書いた直後から陳腐化していく。エージェントが従わないのは、仕様が判断基準ではなく実装指示として書かれているため、現場の状況に対して融通が利かないせいもあるだろう。
仕様駆動開発でまずやるべきは、「仕様」を実装をエージェントに正確に伝える契約書ではなく、どのエビデンスでどの層を棄却するかを明示する判断基準として再定義することだろう。層ごとにエビデンスは違う。
価値仮説:リリース後の指標
行動仮説:ユーザー観察
ドメイン仮説:業務ルールとの照合
相互作用仮説:ユーザビリティテスト
実装仮説:ベンチマークやレビュー
事前に固める対象は採択基準であって、実装の細部ではない。
仮説階層モデルにおける開発シナリオ
仮説階層モデルでの開発シナリオを、「カスタマーサポートへの『前に何を注文したか分からない』という問い合わせを減らしたい」という架空の業務課題をもとに書いてみる。
価値仮説:何で問い合わせを減らすか
最上位の採択基準は「リリース後30日で、注文関連の問い合わせ件数が15%以上減ること」。ここから下位への分岐は、解決策の選択肢として現れる。
A. 注文履歴ページを設ける
B. CSチャットボットに「過去の注文を答える」機能を追加する
C. 注文確認メールの内容を改善し、保存性を上げる
D. アプリのプッシュ通知で配送ステータスを通知する
D:新しい注文に対する施策なので、過去の注文を思い出せない問題には効かない → 棄却
C:メール開封率が12%しかなく、保存されている前提が崩れる → 棄却
B:チャットボットの精度に依存し、30日では決着しにくい → 保留
A:先行させ、効果が見えなければBを後追いする → 採択
ここで仕様に書かれるのは「履歴ページの仕様」ではなく、「Aを採るがBを保留する」という判断と、その根拠である。
行動仮説:履歴ページを採ったとして、ユーザーはどう行動するか
Aを採った時点で、行動仮説の選択肢が分岐する。
A1. ユーザーは履歴を一覧で眺めて、再注文したい商品を探す
A2. ユーザーは特定の注文を「あの時のあれ」と探して、注文番号や日付を確認する
A3. ユーザーは配送状況を確認しに来る(履歴ページを配送追跡の入口として使う)
それぞれ採択基準が違う。
A1:履歴ページからの再注文率
A2:履歴→詳細画面への遷移率
A3:履歴ページから配送追跡画面への遷移率
プロトタイプを5人に触ってもらってエビデンスを取ると、A1とA2が混在し、A3はほぼゼロだった。さらに想定外が出る。「履歴」という言葉でメニューを探さず、「注文」「マイページ」「購入」を順に試す。仕様を書き換える。
A1とA2を主眼に置く(A3は外す)
採択基準に「ヘッダーから1クリックで履歴に到達できる」を追加
メニュー名は「注文履歴」ではなく「ご注文」に変更
ドメイン仮説:履歴に何を載せるか
A1とA2を支えるドメイン仮説も分岐する。
D1. 確定した注文(発送済み・配達完了)だけを履歴と呼ぶ
D2. キャンセル済みも履歴に含める(再注文・参照需要のため)
D3. 下書き・カート放棄も履歴に含める(買い直し導線として)
D1が最初の仮置きだった。ここはユーザー観察ではなく、業務ルールとの整合で判断する。CS・経理・物流にヒアリングする。
CSから「キャンセル済みを再注文したい問い合わせが月10件ある」→ D2を支持
経理から「キャンセル済みは会計上『発生事実』として残るので、表示しても整合する」→ D2を支持
法務から「下書き・カート放棄は契約成立前なので『注文』として表示すると誤認を生む」→ D3を棄却
採択:D1+D2。キャンセル済みは灰色表示+「キャンセル」ラベルで区別する。検証コストは安い(実装せずヒアリングで済む)が、間違えると下位の相互作用・実装をすべて壊すので手は抜かない。継続的実験の文献でも、上位の不確実性を残したまま下位を実装すると手戻りが膨らむことが繰り返し指摘されている。 相互作用仮説:どう見せるか
ドメインがD1+D2に決まると、UIの選択肢が分岐する。
I1. 単純な日付降順リスト(キャンセル含む)
I2. ステータス別タブ(「お届け済み」「キャンセル」を分ける)
I3. カレンダー形式で月ごとに集約
社内ユーザビリティテストでI1〜I3を比較する。
I3:再注文導線として使いにくい(A1の採択基準を満たさない) → 脱落
I2:タブ切り替えが追加操作になり、A2(特定の注文を探す)でかえって遅くなる → 脱落
I1:採択
さらに細部の選択肢を絞る。
1ページあたり:10件 / 20件 / 50件 → 20件(テストで違和感が最少)
検索・絞り込み:初版に入れる / 入れない → 入れない(リリース後ログで判断)
商品名表示:全件 / 先頭1点+他N点 → 先頭1点+他N点(縦スクロール量を抑制)
実装仮説:どう作れば要件を満たすか
UIが決まると、実装の選択肢が分岐する。
E1. 既存の orders テーブルから直接読み取る
E2. 履歴専用の集計テーブルを新設し、夜間バッチで更新する
E3. 検索・絞り込みを見越してElasticsearchにインデックスを張る
採択基準は「想定負荷下で500ms以内」「保守コストを増やさない」。
E3:初版で検索を入れない以上オーバースペック → 棄却
E2:新テーブルとバッチ運用が増える → 保留
E1:採択(ベンチマークが基準を満たさなければE2に切り替える条件付き)
実装後のベンチでは、ユーザーIDだけのインデックスでは800msかかった。仕様は変えず(500ms以内のまま)、(user_id, created_at) の複合インデックスに差し替えて420ms。E1で確定。