16
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

受け入れ駆動開発を試している: AIエージェント時代のDev/Review/QA

16
Posted at

はじめに

最近、AIエージェントを使ったWebアプリ開発の進め方をいろいろ試しています。

この記事は、製品開発の正式なプロセスを変えた話ではありません。個人的にいくつか実験してみた結果、「今のところ、この形にすると壊れにくそうだ」と感じていることをまとめたものです。

最初は、AIエージェントを使うと「コードを書くのが速くなる」という話だと思っていました。

実際、実装フェーズはかなり速くなります。短時間でかなりの量のコードがPRとして出てきます。

ただ、しばらく試してみると、問題は「AIがコードを書けるか」ではなくなってきました。

むしろ重要なのは、AIが書いたコードをどう受け入れるかです。

  • 何を実装してよいか
  • 何を実装してはいけないか
  • どのコンポーネントが何に責任を持つか
  • どういう状態になったら完了とみなすか
  • 誰がレビューし、誰が受け入れを判断するか

このあたりを決めないまま実装だけ速くすると、コードベースは速く壊れます。

この記事では、この進め方を仮に 受け入れ駆動開発 と呼びます。

TDDのように「テストからコードを書く」というよりは、AIエージェントに任せるために、受け入れケース・責務境界・仕様の検証方法を先に定義しておく開発スタイルです。

従来の開発プロセスをそのままAIに渡すと壊れる

最初は、従来の開発プロセスにAIエージェントを足せばよいと思っていました。

チケットを切り、AIに実装させ、人間がレビューし、問題があれば直す、という流れです。

しかし、いくつか試すうちに、この前提はそのままでは壊れることがわかってきました。

全コードレビューは破綻しやすい

最初は、AIが書いたコードは人間が全部読むべきだと思っていました。実際、最初は全部読んでいました。

しかし、AIエージェントがかなりの速度でPRを作るようになると、人間がすべてのコードを同じ密度で読むのは現実的ではありません。

そこで、レビューの考え方を変えました。

コードレビューそのものは、ガイドラインに基づいて独立したReviewerエージェントに任せます。人間は、時々サンプルされたコードを読みます。

そして、問題があれば個別のコードを直すというより、ガイドラインを直します。

コードを全部読む
  ↓
個別に指摘する

ではなく

サンプルを見る
  ↓
問題のパターンを見つける
  ↓
ガイドラインを直す
  ↓
次からReviewerエージェントに検出させる

これはコードレビューというより、品質管理に近いです。

全数検査ではなく、工程を監査する感覚です。

ユーザーストーリー単位のタスクは並列化に向かなかった

人間の開発では、ユーザーストーリー単位でタスクを切るのは自然です。

たとえば「ユーザーが注文できるようにする」というタスクを作るとします。

このタスクは、画面、画面向けAPI、注文サービス、在庫サービス、決済サービスなど、複数のコンポーネントにまたがります。

人間なら、全体の責務境界を意識しながら進められます。しかし、AIエージェントにこれを任せると、1つのエージェントが複数コンポーネントを触り始めます。

その結果、責務境界が崩れます。

ユーザーストーリー単位のタスク
  ↓
複数コンポーネントにまたがる
  ↓
1つのエージェントがいろいろ触る
  ↓
便利な場所に便利な処理が置かれる
  ↓
責務境界が崩れる

AIエージェントは、責務分割があまり得意ではありません。放っておくと、動く場所に処理を置きます。

そこで、タスクはコンポーネントに閉じるようにしました。

「注文機能を作る」ではなく、「Order APIコンポーネントの受け入れケースを満たす」のように切るイメージです。

記録を残しすぎると、リポジトリがエコーチェンバーになる

最初は、なるべくすべての記録をリポジトリに残そうとしていました。

作業記録、判断の履歴、handover、エージェントへの指示、過去の計画などを残しておくと、AIがそれを読んでうまく引き継げるのではないかと考えていました。

しかし、これはあまりうまくいきませんでした。

リポジトリの中でエコーチェンバーのような現象が起きます。だんだん独自の語彙が生まれ、エージェントとの会話が意味不明になっていきます。

また、過去の記録に引きずられて、悪い実装の癖が治らないこともありました。

人間なら「これは昔のメモだから今は違う」と判断できます。しかし、AIは頻出する情報を正しそうな文脈として扱ってしまいます。

そのため、リポジトリには本当に必要なものだけ残すようにしました。

SSoT、Single Source of Truthを意識させることが重要です。重複した説明、古い方針、なんとなく残したメモは、AIにとってノイズになります。

Dev / Review / QA の3エージェント構成にする

受け入れ駆動開発では、AIエージェントの役割を分けます。

今試している流れは、ざっくり以下のようなものです。

要件定義
  ↓
基本設計
  ↓
インターフェース設計
  ↓
スキーマベースの机上デバッグ
  ↓
コンポーネント単位の受け入れケース作成
  ↓
Devエージェントによる実装
  ↓
Reviewerエージェントによるレビュー
  ↓
QAエージェントによる受け入れレビュー
  ↓
E2Eテスト

ポイントは、Dev / Reviewer / QA を同じエージェントにやらせないことです。

役割 主な責務
Dev 実装と受け入れテストの作成
Reviewer ガイドラインに基づくコードレビュー
QA 受け入れケースと受け入れテストの妥当性レビュー

Devエージェントは、実装と自動テストを作ります。

Reviewerエージェントは、PRが作成されたらガイドラインに基づいてレビューします。レビューコメントを受けてDevエージェントが修正し、フィードバックループが回ります。

QAエージェントは、受け入れレビューをします。ここで見るのは、「テストが通るか」だけではありません。

  • 受け入れケースは妥当か
  • 受け入れテストはケースをちゃんと表現しているか
  • テストケースが不足していないか
  • 受け入れと関係ない余計なコードがないか
  • Devが基準をhackしていないか

といったことを見ます。

最初は、QAエージェントに手動テストをさせていました。しかし、これは品質が安定しませんでした。時間もかかります。

そこで、受け入れテスト自体はDevが自動テストとして作成し、QAはその妥当性をレビューする方式に変えました。

Dev:
  実装する
  受け入れテストを書く

Reviewer:
  ガイドラインに基づいてコードを見る

QA:
  その受け入れテストで本当に受け入れケースを確認できるかを見る

この分担にすると、Devが自分で作った基準を自分で満たして終わる、という状態を避けやすくなります。

AIエージェントは、与えられた基準を満たすことには強いです。一方で、その基準自体が妥当かどうかを自分で判断させると、都合のよいテストや都合のよい実装になりがちです。

そのため、Dev / Reviewer / QA を分けて、独立した視点を持たせることが重要でした。

コンポーネント単位で独立して開発できるようにする

AIエージェントに並列で開発させるには、タスクをコンポーネント単位に閉じる必要があります。

人間向けには、ユーザーストーリー単位のタスクは自然です。しかし、AIエージェントにとっては範囲が広すぎます。

複数コンポーネントをまたぐタスクを渡すと、AIは便利な場所に処理を置きます。画面向けAPIがドメインロジックを持ったり、中継コンポーネントがスキーマ変換責務を持ったりします。

そのため、実装タスクは「業務機能」ではなく「コンポーネントの受け入れケース」として切るようにしました。

acceptance-scope.md は受け入れケースの優先順位付きリスト

各コンポーネントには acceptance-scope.md を置きます。

これは詳細な仕様書ではありません。

acceptance-scope.md は、コンポーネント単位の受け入れテストケースを、優先度順に並べたシンプルなリストです。

1ケースは基本的に1行で書きます。

詳細な手順、request / response、期待されるDB状態、外部サービスへのrequestなどは、受け入れテストコードやJSON Schema、机上デバッグ用のpayloadに置きます。acceptance-scope.md に全部を書こうとはしません。

このファイルで管理するのは主に以下です。

  • どの受け入れケースがあるか
  • 優先度は何か
  • どこまで実装済みか
  • どのPRで実装されたか

例えば、注文APIなら次のようになります。

# Order API acceptance scope

## P0: 注文作成の最小成立

| ID | 受け入れケース | Status | PR |
|---|---|---|---|
| ORD-AC-001 | 注文作成リクエストを受け付け、注文を保存できる | done | #123 |
| ORD-AC-002 | 在庫サービスに在庫確保リクエストを送る | done | #123 |
| ORD-AC-003 | 決済サービスにオーソリリクエストを送る | done | #124 |
| ORD-AC-004 | レスポンスがJSON Schemaに準拠する | done | #124 |

## P1: 代表的な失敗系

| ID | 受け入れケース | Status | PR |
|---|---|---|---|
| ORD-AC-101 | 在庫不足なら注文を保存せず409を返す | in_progress | #128 |
| ORD-AC-102 | 決済失敗なら注文を確定しない | not_started | |
| ORD-AC-103 | 不正なrequest bodyなら400を返す | not_started | |

P0 / P1 / P2 は、技術的な難易度ではなく、業務的に意味のある単位でまとめます。

例えば注文APIなら、P0は「注文作成が最小限成立する」、P1は「代表的な失敗系を扱える」、P2は「ログや監査などの運用要件を満たす」というように切ります。

ここで大事なのは、P0の完了で業務的に意味のある最小単位が成立することです。

acceptance-scope.md は、PR分割にも使います。

PRを作るときは、このファイルのどの受け入れケースをcloseするかを先に決めます。

例えば、Order APIのP0を一気に全部実装すると大きすぎる場合は、次のように分けます。

#123:
  - ORD-AC-001
  - ORD-AC-002

#124:
  - ORD-AC-003
  - ORD-AC-004

このとき、1つのPRが大きくなりすぎないように、だいたい3000行以下に収まるように実装スコープを調整しています。

3000行という数字は厳密なルールではありません。人間とエージェントがレビュー可能なサイズに保つための目安です。

受け入れテストはコンポーネント単位の小さなE2Eとして書く

受け入れテストは、単体テストとは別に作ります。

単体テストは、関数やクラスのような小さな単位を検証します。もちろんこれは必要です。

しかし、AIエージェント開発では、単体テストだけでは足りません。個々の関数は正しくても、コンポーネント全体として責務がズレていることがあるからです。

そこで、コンポーネント単位の受け入れテストを作ります。

これは「小さなE2E」のようなものです。

通常のE2E:
  ブラウザやユーザー操作からシステム全体を見る

コンポーネント単位の受け入れテスト:
  対象コンポーネントだけを起動し、
  依存先をモックして、
  外からAPIを叩いて確認する

注文アプリのOrder APIを例にします。

Order APIは、注文作成の入口となるコンポーネントです。注文リクエストを受け取り、在庫サービスや決済サービスを呼び出し、注文を保存します。

受け入れテストでは、Order APIだけを本物として起動します。

  • Order APIは実際に起動する
  • DBはIn Memory DBを使う
  • 在庫サービスはモックサーバにする
  • 決済サービスもモックサーバにする
  • HTTP API経由でOrder APIを叩く
  • レスポンスを確認する
  • DB状態を確認する
  • モックサーバに届いたrequestを確認する
  • request / response がJSON Schemaに準拠しているか確認する

イメージとしては以下です。

test
  ↓ HTTP request
Order API  ----->  Inventory Mock Server
   |
   +--------->  Payment Mock Server
   |
   +--------->  In Memory DB

たとえば、正常系の受け入れテストでは以下を確認します。

Given:
  - Inventory Mock Server は在庫確保成功を返す
  - Payment Mock Server は決済オーソリ成功を返す
  - In Memory DB は空

When:
  - POST /orders に注文作成requestを送る

Then:
  - response.status は 201
  - response.body は order-create-response.schema.yaml に準拠する
  - In Memory DB に注文が保存されている
  - Inventory Mock Server に送られたrequestが inventory-reserve-request.schema.yaml に準拠する
  - Payment Mock Server に送られたrequestが payment-authorize-request.schema.yaml に準拠する

ここで大事なのは、外部サービスに送ったrequestも検証することです。

Order APIのレスポンスだけを見ると、表面上はうまく動いているように見えるかもしれません。しかし、在庫サービスや決済サービスに送るrequestが仕様からズレていれば、コンポーネント間の契約は壊れています。

例えば、仕様では決済サービスに customerId を渡すことになっているのに、AIが勝手に userId に変換していたとします。

{
  "userId": "cus_001",
  "amount": 2000
}

これを payment-authorize-request.schema.yaml で検証すれば、仕様とのズレを検出できます。

実装:
  Payment Service に userId を送る

仕様:
  Payment Service には customerId を送る

受け入れテスト:
  mock server が受け取ったrequestをJSON Schemaで検証
  ↓
  schema validation で失敗

これで、実装が仕様からズレたことを検知できます。

受け入れテストは「動くか」ではなく「責務通りか」を見る

E2Eテストは、最終的にユーザー操作として成立しているかを見るには向いています。

しかし、その実現方法が妥当かどうかまでは見てくれません。

受け入れテストでは、「動くか」だけではなく、「このコンポーネントが責務通りに振る舞っているか」を見ます。

そのために、受け入れケースには「やること」だけでなく「やらないこと」も入れます。

Order API がやること:
  - 注文リクエストを受け取る
  - 在庫サービスに在庫確保を依頼する
  - 決済サービスにオーソリを依頼する
  - 注文を保存する

Order API がやらないこと:
  - 在庫数を自分で計算しない
  - 決済可否を自分で判断しない
  - customerId を userId に変換しない
  - 外部サービスのレスポンス形式を都合よく補正しない

AIは「やるべきこと」は比較的うまく実装します。

一方で、「やってはいけないこと」を守らせるのは難しいです。だからこそ、受け入れケースと受け入れテストに、out-of-scopeを明示することが重要になります。

Markdown + OpenAPI / JSON Schema で仕様バグや実装とのズレを防ぐ

AIエージェントを使うようになると、仕様の書き方も重要になります。

Markdownで書かれたSpecは読みやすいです。意味論や意図も書けます。

しかし、Markdownだけでは実装との整合性が担保されません。

たとえば、次のようなSpecを書いたとします。

注文作成APIは customerId を受け取る。
画面向けAPIは customerId を変更せず、注文サービスに渡す。

人間には読みやすいです。

しかし、AIが実装すると、内部的には userId の方が自然だと判断して、勝手に変換処理を入れることがあります。

Markdownだけだと、こうしたズレを自動検出できません。

一方で、JSON Schemaだけでも不十分です。

JSON Schemaは、JSONの形を検証できます。しかし、「そのフィールドが業務上どういう意味を持つか」までは表現しきれません。

つまり、どちらか一方では足りません。

Markdown:
  読みやすい
  意味論を書ける
  しかし実装とのdriftを検出しづらい

JSON Schema:
  機械検証できる
  CIに組み込める
  しかし意味論は落ちる

だから:
  Markdownで意味を書く
  JSON Schemaで境界を固定する
  replayと受け入れテストで検証する

この組み合わせが重要でした。

AIが出す仕様にはバリがある

AIは仕様もかなりそれっぽく書いてくれます。

ただし、実際に何度か試してみると、AIが出す仕様には必ずボロがあります。

例えば、以下のようなものです。

  • なんとなく必要そう、という理由で付いたフィールド
  • 同じデータが親と子の両方に存在する
  • 同じ概念なのに別名で出てくる
  • nullable / optional の理由が曖昧
  • 画面向けAPIとリソースサーバでスキーマが微妙に違う
  • 中継するだけのはずのコンポーネントが、いつの間にか変換責務を持っている

ここでいう「画面向けAPI」は、いわゆるBFF、Backend for Frontendのようなコンポーネントです。画面から呼びやすいAPIを提供し、必要に応じて裏側のサービスを呼び出す中継役です。

この中継役のスキーマと、裏側の注文サービスのスキーマが少しだけ違うとします。

人間なら「そもそもこのスキーマが変では?」と気づくかもしれません。

しかし、AIエージェントはそのズレを「満たすべき仕様」として受け取ります。

その結果、画面向けAPI側で型変換を始めたり、フィールド名の変換をしたり、不要なfallback処理を入れたりします。

微妙にズレた仕様
  ↓
AIが頑張って整合させる
  ↓
中継コンポーネントに変換・補完・rename処理が生える
  ↓
責務が歪む

これは3Dプリンタのバリ取りに近い作業だと思っています。

AIは形を出してくれます。しかし、そのまま使うには接合面が荒い。

そこで、OpenAPIやJSON Schemaを使って、実装前に机上デバッグをします。

机上デバッグはAPI仕様を紙の上で実行する

机上デバッグでは、API仕様を実装前に「紙の上で実行」します。

流れはだいたい以下です。

1. API仕様をOpenAPI形式で書く
2. OpenAPIだけでは表現しづらい意味論をMarkdownで補足する
3. スキーマを schema/ 以下に独立したJSON Schema形式のYAMLとして置く
4. 代表的な業務シナリオをYAMLで定義する
5. シナリオごとに満たすべき機能要件・非機能要件を書く
6. そのシーケンスをシミュレーションする
7. 各ステップのrequest / responseをJSONとして記録する
8. 結果記録用のYAMLで、シナリオのステップとrequest / responseを紐づける
9. 最後にJSON Schemaに即してrequest / responseをバリデーションする
10. シミュレーション結果をCIに組み込み、schema変更時の回帰テストにする

例えば、注文作成APIを考えます。

ディレクトリ構成は以下のようなイメージです。

docs/
  order-api/
    interfaces/
      openapi.yaml
      semantics.md
    acceptance-scope.md

  verification/
    scenarios/
      create-order.yaml
    results/
      create-order-result.yaml
    payloads/
      create-order/
        01-create-order-request.json
        02-create-order-response.json
        03-inventory-request.json
        04-payment-request.json

schema/
  order-create-request.yaml
  order-create-response.yaml
  inventory-reserve-request.yaml
  payment-authorize-request.yaml

OpenAPIには、エンドポイントとrequest / responseの形を書きます。

paths:
  /orders:
    post:
      operationId: createOrder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "../../../schema/order-create-request.yaml"
      responses:
        "201":
          description: Order created
          content:
            application/json:
              schema:
                $ref: "../../../schema/order-create-response.yaml"

スキーマは schema/ 以下に独立したJSON Schema形式のYAMLとして置きます。

$id: "order-create-request"
type: object
additionalProperties: false
required:
  - customerId
  - items
properties:
  customerId:
    type: string
  items:
    type: array
    minItems: 1
    items:
      type: object
      additionalProperties: false
      required:
        - productId
        - quantity
      properties:
        productId:
          type: string
        quantity:
          type: integer
          minimum: 1

ただし、これだけでは customerId が何を意味するのかは十分に表現できません。

そこで、意味論をMarkdownで補足します。

# Order API Semantics

## customerId

customerId は注文主体となる顧客IDを表す。

- 認証済みユーザーIDとは限らない
- Order APIは customerId を userId に変換してはならない
- 在庫サービスや決済サービスに渡す場合も、意味を変えてはならない

## Order APIの責務

Order APIは、注文作成の入口となるAPIを提供する。

Order APIは以下を行う。

- 注文リクエストを受け取る
- 在庫サービスに在庫確保を依頼する
- 決済サービスにオーソリを依頼する
- 注文を保存する

Order APIは以下を行わない。

- 在庫数を自分で計算する
- 決済可否を自分で判断する
- customerId を別の意味のIDに変換する

次に、代表的な業務シナリオをYAMLで書きます。

id: create-order-happy-path
title: 注文を作成する

requirements:
  functional:
    - ユーザーが商品と数量を指定して注文を作成できる
    - Order APIは在庫サービスに在庫確保を依頼する
    - Order APIは決済サービスにオーソリを依頼する
    - レスポンスにorderIdを含める
  nonFunctional:
    - 各APIのrequest/responseはJSON Schemaに準拠する
    - Order APIはcustomerIdの意味を変更しない

steps:
  - id: create-order-request
    component: order-api
    operation: POST /orders
    request: ../payloads/create-order/01-create-order-request.json
    response: ../payloads/create-order/02-create-order-response.json

  - id: reserve-inventory
    component: inventory-service
    operation: POST /internal/inventory/reservations
    request: ../payloads/create-order/03-inventory-request.json

  - id: authorize-payment
    component: payment-service
    operation: POST /internal/payments/authorizations
    request: ../payloads/create-order/04-payment-request.json

最後に、各ステップのrequest / responseをJSONで記録し、結果記録用のYAMLでschemaと紐づけます。

scenario: create-order-happy-path

results:
  - step: create-order-request
    request:
      file: ../payloads/create-order/01-create-order-request.json
      schema: ../../../schema/order-create-request.yaml
      valid: true
    response:
      file: ../payloads/create-order/02-create-order-response.json
      schema: ../../../schema/order-create-response.yaml
      valid: true

  - step: reserve-inventory
    request:
      file: ../payloads/create-order/03-inventory-request.json
      schema: ../../../schema/inventory-reserve-request.yaml
      valid: true

  - step: authorize-payment
    request:
      file: ../payloads/create-order/04-payment-request.json
      schema: ../../../schema/payment-authorize-request.yaml
      valid: true

CIでは、これらのpayloadをJSON Schemaに対して検証します。

schema変更
  ↓
代表シナリオのrequest / responseを再検証
  ↓
過去のシミュレーション結果が壊れていないか確認

これは、実装コードに対するテストではなく、仕様レベルの回帰テストです。

JSON Schemaだけでは意味論の破壊を防げない

この仕組みが必要なのは、JSON Schemaのvalidationだけでは、意味論の破壊を防げないからです。

例えば、AIが次のような「改善」を提案したとします。

required:
  - userId
  - products
properties:
  userId:
    type: string
  products:
    type: array
    items:
      type: object
      required:
        - id
        - amount
      properties:
        id:
          type: string
        amount:
          type: integer

一見すると、これもスキーマとしては成立しています。

customerIduserId に、itemsproducts に、quantityamount に変えただけです。AIからすると、自然な命名に寄せたつもりなのかもしれません。

しかし、業務シナリオ上は壊れています。

  • customerIduserId は同じ意味ではない
  • quantityamount は同じ意味とは限らない
  • 中継コンポーネントが変換責務を持ち始める
  • 同じ概念の名前がコンポーネント間でズレる

問題は変換処理がないことではなく、そもそもスキーマがズレていることです。

人間なら「このスキーマおかしくない?」と上流に戻せるかもしれません。しかし、AIは与えられた仕様を満たすために、局所的な補正コードを書いてしまいます。

だから、代表シナリオを使って、実際にデータを流してみる必要があります。

人間はコードを書くより、違和感をフィードバックする役割に寄っていく

AIエージェントに任せられることは増えています。

明確な受け入れケースがあり、仕様が機械可読で、自動テストできるものは、かなり任せやすくなりました。

例えば、以下のようなものです。

  • コンポーネント単位の実装
  • 受け入れテストの作成
  • 既存ガイドラインに基づくレビュー
  • PRコメントへの対応
  • JSON Schemaに基づくrequest / response検証

一方で、人間がまだ見た方がよいものもあります。

  • 責務分割
  • 抽象化の止めどころ
  • UIや設計の「なんか微妙」
  • 過去に同じ失敗をしていないか
  • ガイドライン自体が間違っていないか
  • Elephant in the room

AIレビューで怖いのは、細かい指摘が間違っていることより、Elephant in the roomを素通りすることです。

例えば、以下のようなものです。

  • 受け入れテストしかなく、単体テストがない
  • UI実装なのにCSSが一切書かれていない
  • 中継コンポーネントがいつの間にか変換責務を持っている
  • 見た瞬間に「なんか微妙」な設計になっている

レビューコメントの一つ一つはもっともらしいです。しかし、そもそも大きな問題を見落としていることがあります。

AIは短距離の整合性には強いです。

この関数は型に合っているか。
このテストは通るか。
このPRコメントに対応できるか。
このJSONはschemaに合っているか。

一方で、長距離の整合性にはまだ弱いと感じています。

前回も同じ責務分割で失敗していないか。
この抽象は3ヶ月後に保守できるか。
この命名は別コンポーネントと意味がズレていないか。

この長距離の違和感を人間が補う必要があります。

人間のレビューは、コードの正誤判定から工程改善へ移っていきます。

問題のあるコードを1つ直すのではなく、同じ問題を次から検出できるようにガイドラインを直す。個別修正者ではなく、品質管理のフィードバック源になる。

これが、今のところ一番しっくりきている役割分担です。

まとめ

AIエージェントを使うと、実装速度はかなり上がります。

しかし、実装が速くなると、ボトルネックは「書くこと」ではなく「受け入れること」に移ります。

そのためには、AIに曖昧なタスクを渡すのではなく、次の3つを先に整える必要がありました。

  • Dev / Reviewer / QA を分ける
  • コンポーネント単位で独立して開発できるようにする
  • Markdownだけでなく、OpenAPIやJSON Schemaで仕様を機械可読にする

特に重要だったのは、仕様を「読めるもの」として書くだけでなく、「検証できるもの」にすることです。

Markdownは意味論を書くには向いています。
一方で、実装とのズレを検出するには、JSON Schemaやシナリオreplay、コンポーネント単位の受け入れテストが必要でした。

また、人間の役割も変わります。

AIが書いたコードをすべて読むのではなく、サンプルを見て、違和感を拾い、ガイドラインや受け入れケースにフィードバックする。コードを直接直すより、同じ問題が次から検出されるように工程を直す。

受け入れ駆動開発は、AIエージェントに実装を任せるための開発プロセスというより、AIが高速に生成するコードを安全に受け入れるための品質管理の仕組みだと感じています。

16
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?