AI時代の動く仕様書
AIが実装の多くを担う時代において、人間の役割は「どう書くか」から「何を仕様として成立させるか」へと移っています。
動く仕様書 (executable specification)は、その判断基準を人間にもAIにも理解可能な形で固定するための手段です。
分析モデルから詳細化し、実装からのフィードバックを受け、再びモデルへと戻る。
このアップダウンの往復を支える中核として、動く仕様書はAI時代の開発における最も実践的な仕様表現となります。
Executable Specificationとは
Executable Specificationは、日本語では「実行可能仕様」と訳すのが一般的ですが、SimpleModelingでは動く仕様書と呼びます。
動く仕様書は、BDD (Behavior-Driven Development)(Behavior-Driven Development)に由来するテストケースの考え方です。
要点は単純で、テストケースを、そのまま仕様書として読める形にしようという発想です。
BDDでは主に「振る舞い(behavior)」をシナリオとして記述します。
一方 SimpleModeling では、振る舞いに限定せず、
-
APIの使い方
-
制約や境界条件
-
性質(property)
-
要求仕様のシナリオ
など、仕様として意味を持つあらゆるテストを動く仕様書として位置づけます。
動く仕様書は、単なるテストではなく、「このプログラムは何を仕様として満たすのか」を実行可能な形で明示する別表現の仕様です。
プログラミング環境
プログラミング言語には Scala 3 を使用し、cats と組み合わせたオブジェクト関数型プログラミングを行います。
Scala 3 が提供する強力な静的型システムと関数型プログラミングにより、多くのバグ (bug)は実行前、すなわちコンパイル段階で排除されます。
カーリー・ハワード対応の観点では、静的型付き関数型言語におけるコンパイルは、直観主義命題論理における証明と同等と見なすことができます。
テスティングフレームワークには ScalaTest を採用し、Property Based Testing(PBT)も積極的に活用します。
AI時代のプログラミング言語として、Scalaは有力な選択肢だと考えています。
なぜなら、コーディングの多くをAIが担う前提では、人間にとっての文法の易しさよりも、
-
AIが誤りにくいこと
-
意図や制約が型として表現できること
-
仕様と実装の距離が近いこと
強い静的型付けと関数型プログラミングの組み合わせは、これらの要件を自然に満たしており、AI駆動開発と非常に相性のよい基盤を提供します。
テストの考え方
テスト・ケースは多ければよい、というものではありません。テスト・ケースの量を意図的に絞り込み、作りすぎないという観点が重要になります。
Scalaと関数型プログラミングを採用する意図の一つは、必要となるテストケースの数を大幅に減らせる点にあります。
Scalaの強力な静的型システムと関数型プログラミングの組み合わせにより、多くのバグは実行時ではなくコンパイル時に排除されます。
その結果、コンパイル済みのプログラムは、すでにかなり「クリーンな状態」になっています。
この前提があるため、すべてをテストで網羅しようとするのではなく、意味のある箇所だけを、最小限のテストで押さえるという方針が成立します。
テストの種類
SimpleModelingでは、この前提のもとで品質を確保するために、以下のテストを 動く仕様書 として整備します。
まず、ボトムアップの観点では、コンポーネント (Component)やクラスの仕様から出発し、実装がその仕様を満たしているかを確認する 仕様確認 (verification) のための動く仕様書を作成します。
-
コンポーネント・テストAPIの使い方、前提条件、戻り値、エラー条件などを明示的に仕様として固定し、実装が設計仕様どおりであることを仕様確認する
一方、トップダウンの観点では、要求仕様から出発し、システム全体として意味的に正しい振る舞いをしているかを確認する 妥当性確認 (validation) のための動く仕様書を作成します。
-
シナリオ・テスト要求仕様をシナリオとして記述し、ユースケースの前提・操作・結果をそのまま実行可能な形で表現することで、要求を満たしているかを妥当性確認する
これらは単なる品質保証のためのテストではありません。
という二つの方向から、仕様を実行可能な形で保持するための基本構成として位置づけられます。
この二層構造により、実装・設計・要求仕様の間にあるズレを、動く仕様書という形で早期に発見し、人間とAIの双方が同じ判断基準を共有できるようになります。
動く仕様書
動く仕様書を作成する目的は、次の3点に集約されます。
-
仕様を明確に記述する
-
実装が仕様通りに動くことを確認する
-
機能の正しい使い方を示すサンプルになる
動く仕様書は、単なるテストコードではありません。
-
人間が仕様として読める
-
実行によって正否が確定する
-
実行結果そのものが仕様の説明になる
さらに重要なのは、動く仕様書がAIにとっても理解・参照しやすい仕様表現になっている点です。
自然言語による仕様書は、人間には柔軟に解釈できますが、AIにとっては解釈の余地が広く、どこまでが必須仕様なのかを正確に把握することが困難です。
一方、動く仕様書では、
-
入力と出力が明示され
-
成否条件がコードとして固定され
-
実行結果によって判断が下される
ため、AIは「何が正しい振る舞いか」「どこが仕様の境界か」を明確に把握できます。
動く仕様書は、
-
人間にとっては理解と確認のための仕様書
-
AIにとっては判断基準として利用できる仕様表現
という二重の役割を果たします。
このように、動く仕様書は人間とAIが同じ仕様を共有するための、実践的で信頼性の高い表現形式です。
動く仕様書の例
動く仕様書の具体例を説明します。
プログラム例
APIの入力、出力、前提条件、そして満たすべき仕様を、実行可能なテストとしてそのまま表現します。
この形式により、仕様は文章として読むことができると同時に、実行によって正しさを確認できます。
以降のコード例では、テストコードそのものが「APIは何を満たすべきか」を明示する仕様として機能します。
class PingSpec extends AnyWordSpec with Matchers with GivenWhenThen {
"Ping API" should {
"return pong" in {
Given("a ping API")
val api = PingApi()
When("ping is called")
val result = api.ping()
Then("pong is returned")
result shouldBe "pong"
}
}
}
テスト・プログラムも ScalaTest の独自文法(Scala の DSL (Domain Specific Language) 機能を活用)を使用しており、仕様記述の部分とテスト処理の部分が分かりやすく記述されています。
これだけでも、何をテストしているのかが容易に読み取れます。
実行例
動く仕様書を実行すると、出力は次のようになります。
PingSpec:
Ping API
- should return pong
Given a ping API
When ping is called
Then pong is returned
Run completed in 120 milliseconds.
Total number of tests run: 1
Tests: succeeded 1, failed 0
この実行結果は、単に「テストが通った」ことを示すだけでなく、
-
テスト名がAPIの振る舞いの要約になっている
-
Given, When, Thenというルール化された内容に即した仕様の文章が出力される
という、一種の仕様書の文章が出力されます。
英語の場合には、そのまま英文としても読めるような文章になっているところがミソになっています。
まさに動く仕様書ですね。
出力結果は、人間にとってもAIにとっても「何が仕様なのか」を把握しやすい表現になります。
動く仕様書のポイント
ここでは、動く仕様書をどのように書くかという観点から、ScalaTestの使いどころを整理します。
重要なのは、これらが単なるテスト記法ではなく、仕様を人間とAIの双方に伝えるための表現技法になっている点です。
AnyWordSpec
AnyWordSpecを用いることで、自然言語に近いDSL形式で仕様を記述できます。
仕様として読む部分(what)と、検証を行うプログラム部分(how)が明確に分かれるため、仕様の輪郭を素早く把握できます。
この構造は、人間にとって読みやすいだけでなく、AIが「どこが仕様で、どこが実装的処理か」を判断しやすいという利点も持ちます。
PBT
PBT(Property Based Testing)を使用すると、入力値の具体例ではなく、成り立つべき性質そのものを仕様として固定できます。
property("reverse twice returns original") =
forAll { (xs: List[Int]) =>
xs.reverse.reverse == xs
}
具体例の列挙ではなく、性質を直接記述することで、仕様の本質が明確になり、AIにとっても「守るべき不変条件」を把握しやすくなります。
GivenWhenThen
GivenWhenThenトレイトを用いることで、前提・操作・結果の因果関係が明確な仕様を記述できます。
SimpleModelingでは、
-
コンポーネント・テスト
-
シナリオ・テスト
の両方でGiven / When / Thenを使用します。
Given("a registered user")
When("the user submits a valid request")
Then("the system returns a success response")
この形式は、要求仕様として記述されたシナリオを、そのまま動く仕様書として実装に接地するための基本形になります。
Matchers
Matchersを活用することで、検証内容をドメインの言葉で表現できます。
result should beAccepted
result should haveStatus(Success)
このような表現は、単なる真偽判定ではなく、「何が仕様として期待されているか」を明示的に示します。
コンポーネント開発者自身がMatchersを提供することで、動く仕様書はより仕様書らしい表現になり、人間とAIの双方にとって意図を読み取りやすいものになります。
三位一体
AIの支援を前提とした開発では、
-
文章による仕様記述
-
実装コード
の3つを同時に育てていく開発スタイルが有効です。
ここで重要なのは、これらを工程として直列に並べるのではなく、常に相互参照しながら進化させる点にあります。
文章による仕様は、意図・背景・文脈を人間とAIで共有するための入口になります。
実装コードは、その意図を具体的な振る舞いとして具現化します。
動く仕様書は、その振る舞いが「何を仕様として満たしているのか」を実行可能な形で固定します。
この三者は独立ではなく、
-
動く仕様書の失敗が、実装の誤りを示すこともあれば
-
設計の曖昧さや
-
分析モデルそのものの不備を示すこともあります
そのため開発は、
という アップダウンの往復運動 を伴います。
この循環によって、分析モデルは現実の振る舞いに接地され、実装は意味的な裏付けを持つようになります。
なお、この開発スタイルは TDD (Test-Driven Development)(Test Driven Development)とは異なります。
動く仕様書の目的は、実装を逐次駆動することではなく、仕様の意味と境界を固定し続けることにあります。
三者が相互に矛盾しない状態を維持すること。
そして、そのズレを早期に発見し、分析モデル・設計・実装へとフィードバックできること。
これこそが、AI時代における実践的な品質保証であり、三位一体の開発スタイルが持つ本質的な価値です。
参照
用語集
- 動く仕様書 (executable specification)
-
Executable Specification(動く仕様書)とは、実行可能な形で記述され、実行することで仕様の正否や意味が確定する仕様表現である。
- BDD (Behavior-Driven Development)
-
Behavior Driven Development(BDD, 振る舞い駆動開発)とは、システムの振る舞いをシナリオとして記述し、関係者間で共有可能な形で仕様を明確化する開発アプローチである。
- バグ (bug)
-
俗語的にソフトウェアの不具合を指す用語。厳密な技術的定義はなく、Defect や Fault、Failure を広く含む日常的な表現。
- コンポーネント (Component)
-
責務・契約・依存関係を明示的に定義し、再利用可能で交換可能な単位としてカプセル化されたソフトウェア構成要素。論理モデルでは抽象構造単位として、物理モデルでは実装・デプロイメント単位として扱われる。
- 仕様確認 (verification)
-
Verification(仕様確認)とは、規定された設計仕様や要求仕様に対して、実装が一致しているかを確認する行為である。
- エラー (error)
-
汎用的な表現として用いられる用語。ソフトウェア工学では多義的に使われ、バグや障害全般を指す。SimpleModeling では広義のラベルとして使用し、個別には Mistake・Defect・Fault・Failure・Deviation に整理する。
- 妥当性確認 (validation)
-
Validation(妥当性確認)とは、システムや機能が利用目的や要求仕様に対して妥当であるかを確認する行為である。
- DSL (Domain Specific Language)
-
DSL(ドメイン固有言語)は、特定の領域(ドメイン)に特化して設計された言語であり、その分野の概念や構造を直接的かつ簡潔に表現することを目的とします。 一般的な汎用プログラミング言語(GPL)に比べ、DSLは特定ドメインの問題解決や自動生成に適した高い抽象度を持ちます。
- TDD (Test-Driven Development)
-
Test Driven Development(TDD, テスト駆動開発)とは、実装に先立ってテストを記述し、そのテストを通すことを起点として実装とリファクタリングを繰り返す開発手法である。