もし仕様がなかったら:TDDと暗黙の前提
同じソートをテスト駆動開発で作ると何が起きるか — 例示の認知的メリットと、文化の共有という前提、そしてマルチエージェント時代
同じソートを、仕様なしで作る
これまでの 2 ページでは、陰仕様を起点に設計・実装・テストへ降りてきました。今度は逆の世界を考えます。仕様を書かず、テスト駆動開発(TDD)で同じソートを作るとどうなるでしょうか。TDD では「失敗するテストを書く → 通す最小の実装を書く → 整理する」(Red-Green-Refactor)のサイクルを回します。仕様書の代わりに、テスト — つまり例 — が先に立ちます。
// TDD: 仕様の代わりに、例からはじめる test('空列はそのまま', () => { expect(sort([])).toEqual([]); }); test('1 要素はそのまま', () => { expect(sort([5])).toEqual([5]); }); test('整列済みは変わらない', () => { expect(sort([1, 2, 3])).toEqual([1, 2, 3]); }); test('逆順を並べ替える', () => { expect(sort([3, 2, 1])).toEqual([1, 2, 3]); }); test('重複を保つ', () => { expect(sort([3, 1, 3])).toEqual([1, 3, 3]); }); test('負数も扱える', () => { expect(sort([-1, 5, -10])).toEqual([-10, -1, 5]); });
テストを増やすほど、実装は「正しいソート」へ誘導されていくように見えます。しかし、このテスト群が固定しているのは有限個の点だけです。陰仕様の事後条件が「すべての入力」について語っていたのとは対照的です。
実装者は「真の仕様」を推察している
次の実装を見てください。上の 6 つのテストをすべて通します。
// この実装は、上の 6 つのテストをすべて通す function sort(l) { const r = [...l].sort((a, b) => a - b); return r.length <= 3 ? r : r.slice(0, 3); // 4 要素以上は 3 つに切り詰める(!) }
馬鹿げた実装に見えます。しかし「テストがすべて通る」という基準だけでは、これを排除できません。テストをどんなに分厚くしても状況は本質的に変わらず、テスト群を満たす実装は無数に存在します。実際の実装者 — 人間でも AI でも — がこう書かないのは、テストという射影から「作者はソートを意図しているのだろう」と推察し、真の仕様を頭の中で復元して、それに向かって書いているからです。
この復元が成立するのは、テストの書き手と実装者が同じ文化と常識を共有しているからです。sort という関数名から意図を汲む。典型例の選び方から境界の扱いを察する。重複のテストから要素を保存する意図を読む。TDD は — そして例示による意図の伝達一般は — この暗黙の共有を前提にしているのです。
それでも例示には合理的なメリットがある
TDD が広く普及したのは偶然ではありません。例示は人間にとって圧倒的に理解しやすいのです。post IsPermutation(r, l) and IsOrdered(r) を読むには全称記号や順列・集合の素養が要りますが、sort([3, 1, 3]) が [1, 3, 3] になることは誰にでも読めます。
文化と常識を共有できる集団の中では、直感的ではない集合論や全称記号のような抽象論よりも、例示のほうが大多数の人間の認知負荷をはるかに低く抑えられます。人間がコードを書き、人間がレビューする環境で TDD が磨かれてきたのは、まさにこの点でした。
| 観点 | 例示(テスト) | 述語(形式仕様) |
|---|---|---|
| 読み手に必要な素養 | ほぼ不要 — 文化・常識で読める | 論理記号・集合論の基礎 |
| 固定できる範囲 | 有限個の点 | すべての入力 |
| 意図の伝達 | 文化・常識の共有に依存 | 記号の定義のみに依存 |
| 大多数の人間の認知負荷 | 低い | 高い |
| 機械検証との接続 | 点ごとの一致確認 | 全称的な検証・証明の基盤 |
マルチエージェント開発で、この前提は崩れる
AI エージェントにとって、形式的な述語は「読みにくい抽象」ではありません。認知負荷の議論は人間の認知特性に固有のものであり、機械同士の協調にはそのまま当てはまりません。例示が持っていた最大のメリットは、書き手と読み手が AI になる環境では効かなくなります。
一方で、TDD が立っていた前提 — 文化と常識の共有 — は、異なるモデル・異なる事業者・異なるバージョンのエージェントが協調する環境では保証されません。推察に頼る協調は、推察がズレた瞬間に静かに壊れます。しかもテストは有限個の点しか見ていないため、ズレはテストの隙間をすり抜けます。
形式的な契約であれば、契約の意味は記号の定義だけで一意に定まります。意図を正しく仕様へ写し取れているか — 第1部の「順列条件を忘れると」で見た問い — は残りますが、契約そのものは、これまでの 2 ページで見たように、実行時検証・証明責務・仕様由来のテストへ機械的に接続できます。
手法は前提の上に立つ
にもかかわらず、業界は人間がコーディングしていた時代の開発手法に強く引っ張られています。ツールも組織もベストプラクティスも、「人間の認知負荷を最小化する」という目的に向けて磨かれてきました。書き手が AI へ移るとき、この目的関数そのものが変わります。
TDD が間違っていたのではありません。TDD がその上で最適だった前提が変わるのです。前提が変われば、最適な手法も変わる — 例示による意図の推察から、契約による意図の固定へ。これが、マルチエージェント開発が形式的な契約を必要とする理由です。