
要件定義書の書き方の話をする前に、そこで何を作ろうとしているのかから始めます。ここを飛ばすと、あとの事故の話が伝わらないからです。
題材は、経費精算システムの入れ替えです。
いまの状態はこうです。社員が紙の申請書に領収書を貼って提出し、上長が印鑑を押し、経理が金額と科目を確認して、月次で振込データを作る。月末になると、経理が数日がかりで確認しています。
新しいシステムの目玉は、AIによる読み取りです。領収書をスマートフォンで撮ると、日付・金額・支払先を読み取って、申請を自動で作る。入力の手間がほぼゼロになります。
ここで、なぜ要件定義が必要になるのかというと、作る側と発注する側が、別の会社だからです。
自分たちで作るなら、頭の中の像がずれても、作りながら直せます。外に頼むと、ずれたまま3ヶ月進みます。 だから作る前に、何を作るかを文書で合意する。
要件定義書とは、何を作るのかを発注側と開発側で合意するための文書です。業務上の目的、対象範囲、実現する機能、性能やセキュリティの条件、そして何をもって完成とするかの判定基準を書きます。
そして今回は、AIが工程の中に入ります。 ここが従来の要件定義と違うところで、あとで効いてきます。
従来のシステムは、書かれていないことをしません。 仕様書に無い動きはしない。だから仕様書の網羅性が、そのまま品質になりました。
AIは、書かれていないことをします。 役に立とうとするからです。網羅性だけでは、品質にならない。
前置きはさておき、本題に入ります。
今日は、この要件定義を教科書どおりに書いて、動かして、そこで何が起きたかという話をしていこうと思います。
① 教科書どおりに、要件を書く

書き方の型は確立しています。機能を洗い出し、画面を数え、非機能条件を並べる。
経費精算は業務がはっきりしているので、かなり具体的に書けます。
“`
【正常系】
・領収書を撮影 → AIが日付・金額・支払先を読み取り → 申請が作成される
・申請者が内容を確認して提出
・上長に通知 → 承認または差し戻し
・経理が科目と金額を確認 → 確定
・月次で振込データを出力
【異常系】
・画像が不鮮明で読み取れない → 手入力に切り替える
・金額が上限を超える → 申請できない旨を表示
・上長が不在 → 代理承認者に回す
・締め日を過ぎた申請 → 翌月扱いにする
“`
正常系も異常系も書きました。レビューも通りました。このとき、要件定義書は分厚い一冊になっていました。
そして、動くものができます。期待どおりに動きました。 領収書を撮ると読み取り、申請が立ち、承認が回り、月次で振込データが出る。
② 数字が合わない日が来る

稼働から3ヶ月ほど経ったころ、経理から連絡が来ます。
申請したはずの精算が振り込まれていない、という問い合わせが1件あった。
調べます。申請者の画面には、申請が「提出済み」として残っています。 ところが経理側の確定一覧には、その1件がありません。前後の申請は普通に並んでいます。1件だけ、無い。
最初は表示の不具合を疑いました。次に権限、次に検索条件。どれも違いました。
原因はこうでした。その申請は、締め日を過ぎた直後の数分間に作られていました。
→ ① 締め日の締め切り処理が走り、その月の受付は閉じた
→ ② 直後に、申請者が領収書を撮影した
→ ③ AIが読み取り、申請を作成した
→ ④ 申請者の画面には「提出済み」と表示された
→ ⑤ ところが経理側は、閉じた月の申請を集計に含めない。翌月にも繰り越されなかった
申請者から見れば出した。経理から見れば受け取っていない。 どちらの画面も正しく動いています。
要件定義書には「締め日を過ぎた申請は翌月扱いにする」と書いてありました。書いてあったのに、繰り越されなかった。 なぜなら、その処理は「申請が作られたあとに締め日を過ぎた場合」を想定していて、「締め処理が走った直後に、新しい申請が作られる場合」は想定していなかったからです。
③ 抜けていたのは、正常系と異常系の「あいだ」

要件定義書に不備はあります。ただし、雑だったからではありません。
→ 正常系は書かれていました
→ 明示的な異常系も書かれていました(読み取り失敗、上限超過、承認者不在、締め日超過)
抜けていたのは、そのあいだです。
システムとしては受け付けられない状態なのに、人が来てしまう。 このケースが、誰の頭にも浮かんでいませんでした。
そして、いちばん厄介なのはこれがエラーとして飛ばないことです。
→ 例外は出ていない
→ 画面も落ちていない
→ ログにもエラーが無い
→ 申請者の画面には「提出済み」と正しく表示されている
壊れたものを探す仕組みは、全部すり抜けます。 見つかったのは、社員から問い合わせが来たからでした。
問い合わせが来なければ、この不具合は翌月も翌々月も起き続けていたことになります。そして、いくらだったかも分からないままです。
書類の厚さと、この抜けは無関係でした。 「これで網羅した」と言えるだけの厚さがあっても、思いつかなかったケースは、何ページ書いても書かれません。
④ もうひとつ — AIは、書かれていないことをする

同じ時期に、別の問い合わせも来ていました。私的な支出が精算されそうになった、という報告です。
領収書の画像に「〇〇商店」とだけ書かれていて、AIはそれを取引先との会食と読み取り、交際費として申請を作っていました。実際は本人の買い物です。
AIの精度は悪くありません。書かれている情報からは、そう読めます。 問題は、判断がつかないときに「作らない」という選択肢を与えていなかったことでした。
従来のシステムは、書かれていないことをしません。AIは、書かれていないことをします。 役に立とうとするからです。
ここが、AIを挟む要件定義における最大の差分です。
⑤ 直してみる — 列挙する対象を、機能から3つに変える

機能から発想すると抜けます。理由は単純でした。
→ 機能から発想すると、「システムができること」が並びます
→ でも事故が起きるのは、「システムができないのに、人が来たとき」です
→ できないことは機能一覧に載らないので、発想の起点になりません
そこで、列挙する対象を3つに切り替えました。
1. 受け渡し — 誰から誰へ、何を、どれだけ渡すか
業務を「部署の役割」で分けると、絵はきれいになりますが抜けます。事故は役割の中ではなく、役割と役割のあいだで起きるからです。
書くのは組織図ではなく、渡すものの一覧です。AIを挟むなら、AIも「渡す相手」の1つとして同じ表に並べます。
2. 停止点 — どこで人の判断を待つか
そして、その承認は処理を止めて待つのか、先に進めてあとから追認するのか。
「確認ダイアログを出す」は、業務を同期的に止める設計です。毎月まとまった数の申請が流れる現場でこれをやると、承認者が律速になります。金銭が動く、取り消せない、外部に送信される——このどれかに当たるなら止める価値があります。それ以外は、止めなくていい可能性が高い。
3. 受け付けられない状態のときに、人が来るケース
冒頭の事故が、まさにここです。
→ 締め処理の直後に申請が来たら
→ 上限に達した直後に、同時申請があったら
→ 承認者が異動した直後に、その人宛の承認が回ってきたら
このリストは、機能から発想すると出てきません。「無いものを数える」という発想が要ります。
⑥ あとで知った — この抜けには、名前がついていた

素朴な整理のつもりでしたが、調べると同じ分け方をする言葉が、すでにありました。
テストの世界では、カバレッジを4つに分けて呼び分けることがあります。厳密な定義があるわけではなく、緩く使われている言葉です。
→ 正常系(happy path) — 入力が正しく、例外が起きず、期待どおりの結果が出る理想の経路
→ 異常系(sad path) — 認証拒否のような、想定済みの失敗
→ 境界値(edge case) — 入力の端
→ 同時多発(corner case) — 低確率の条件が複数、同時に当たる
冒頭の事故は、4つ目でした。 「締め処理が走った瞬間」と「申請が作られた瞬間」という、それぞれ低確率の事象が同時に当たっている。だから誰も思いつかなかった。
そして、この抜けが生む結果にも名前がありました。サイレント障害(silent failure)です。定義がそのままです。
「正常系からの逸脱が、何のシグナルも出さないとき、それはサイレント障害になる」。
僕が「エラーとして飛ばないから見つからない」と書いていたものは、すでに名前が付いて、対処法まで整理されている現象でした。
その対処法が、3つ目です。突き合わせ(reconciliation)。
会計システムの文脈では、突き合わせが合わないときは記帳を止めて管理者に通知するのが期待される挙動とされています。ログを1行出して先へ進む、ではありません。
もっと分かりやすいのが、件数の突き合わせです。
送り出した件数と、返ってきた件数は一致しなければならない。一致しなければ、差分を必ず表に出す。 この規則が無いと、50件送って48件返ってきたときに、システムは「48件を処理しました」と報告します。「2件が欠けています」とは報告しません。
冒頭の事故と、まったく同じ構造です。 申請者の画面は「提出済み」と報告した。「経理に届いていない」とは報告しなかった。
新しい理屈は、ひとつも要りませんでした。
⑦ 何が変わったか

3列を書き出す作業は、すぐ終わりました。機能一覧を作るのに使った時間より、はるかに短い。
そして、書き出した「受け付けられない状態のときに人が来るケース」は、十数件になりました。 そのうち、既存の要件でカバーされていたのは数件だけです。
残りのうち、実際に起こりうると判断したものを絞り込むと、その中に、冒頭の事故と同じものがありました。
つまり、あの事故は、この表を先に書いていれば防げていたことになります。機能一覧を削って、この表を1枚足すほうが、事故は減ります。
⑧ 現場で使うなら、この3列

そのまま埋められる形にします。
表1:受け渡し
| 渡す側 | 受け取る側 | 渡すもの | 頻度 | そろっていない率(実測して埋める) |
|---|---|---|---|---|
| 申請者 | AI読み取り | 領収書の画像 | 申請ごと | 読み取れない画像の割合 |
| AI読み取り | 申請者 | 日付・金額・支払先 | 申請ごと | 人が直した項目の割合 |
| 申請者 | 上長 | 申請 | 申請ごと | 差し戻しの割合 |
| 上長 | 経理 | 承認済み申請 | 申請ごと | 経理で直した割合 |
| 経理 | 振込処理 | 確定データ | 月1回 | 振込エラーの割合 |
「そろっていない率」の列が、いちばん効きます。 ここは推測で埋めず、実際に数えてください。数字が入った瞬間、どこにAIを入れるべきかがほぼ決まります。
表2:停止点
| 工程 | 人が判断するか | 止め方 | 理由 |
|---|---|---|---|
| AI読み取り後 | する | 非同期(提案して先へ、あとで修正) | 取り消せる |
| 上長承認 | する | 非同期(一括で確認) | 1件ずつ同期で止めると律速 |
| 経理確定 | する | 同期で止める | 金銭が動く。取り消せない |
| 振込実行 | する | 同期で止める | 同上 |
同期で止めるのは、金銭が動く2工程だけ。 残りは「提案して先へ進み、あとから追認」にします。
表3:受け付けられない状態のときに、人が来るケース
| 状況 | 人が来ると | いま何が起きるか | どうする |
|---|---|---|---|
| 締め処理の直後 | 申請が作られる | 提出済みと表示され、集計に入らない | 受付を閉じ、翌月へ回す |
| 上限到達の直後 | 同時に申請 | 片方だけ通る | 両方止めて通知 |
| 承認者の異動直後 | 承認が回る | 宛先不明で滞留 | 代理へ自動転送 |
| AIが判断できない | そのまま申請 | 推測で作成される | 作らずに人へ回す |
4行目が、AIを挟む場合の必須行です。 「判断がつかないときは作らない」を、明示的に書きます。
表4:突き合わせ(無いものを数える)
| 突き合わせるもの | 頻度 | 拾う条件 | 一致しないとき |
|---|---|---|---|
| 申請者側の提出済み ⇄ 経理側の受領 | 毎日 | 片方にしか無い行 | 止めて通知 |
| 承認済み件数 ⇄ 確定件数 | 毎日 | 件数の差 | 止めて通知 |
| 確定金額 ⇄ 振込金額 | 月次 | 金額の差 | 止めて通知 |
「ログを1行出して先へ進む」にしないでください。 止めて、人に通知する。これが、サイレント障害への唯一の対処です。
⑨ この書き方が効き続ける理由

要件定義が重要になった、という言い方はやや不正確です。重要性の中身が入れ替わりました。
実装が高かった時代、要件定義の役割は手戻りを防ぐことでした。作り直しに数ヶ月かかるから、作る前に決め切る価値があった。
実装が数日になった今、手戻りのコストは劇的に下がりました。 だから「決め切る」ことの価値は、実は下がっています。
代わりに上がったのが、漏れを見つけることの価値です。作り直しは安いが、気づかない事故は安くなりません。
そして、抜けやすい場所は毎回同じです。受け渡しと、停止点と、受け付けられない状態に来る人。 この3つだけ、他の10倍の時間をかけてください。
4つ目の表は、そのうえで必ず作ってください。 上の3つは事故を減らしますが、ゼロにはできません。ゼロにできない前提で、気づける仕組みを持つ。 それが、あの分厚い一冊には書かれていなかったものです。
ちなみに、経理の月末は目に見えて短くなりました。
浮いた時間で何をしているかというと、振込が終わったあとの突き合わせです。件数と金額を並べて、片方にしか無い行を探す。
自動化した時間を、自動化が壊れていないか確認する時間に使っている。 これでいいのか、いまも少し考えています。
以上です。