この記事はなに?
AIに実装を任せる際、「要件定義は細かく」「設計はしっかり」と言われることが多いですが、その最適な粒度を掴むのは意外と難しいものです。
簡易的なIssueであっても、指示がふわっとしていたせいで、AIが既存コンポーネントを活用せずに新規作成してしまい、「これなら自分で書いたほうが早かった...」という手戻りを経験したことはないでしょうか?
私はこういった経験から、「AI向けのIssue」と「人間向けのIssue」は別物であると実感しました。
今回は、Visual Studio Codeでチャットしながら要件を詰めて、最後にGitHub上でCopilotに実装を任せる(Assignする)ところまでをゴールとし、ペアプロしました。
ペアプロを実践した環境・前提
環境
- パソコン:Mac
- エディタ:Visual Studio Code
- 画面共有:Slackのハドル
- Issue:GitHub
- AI:Visual Studio Code上のCopilot Chatでやり取り
- その他ツール:GitHub MCP
- Visual Studio Codeから直接Issueを読み書きするために使用
前提
- 役割
- ドライバー:私
- ナビゲーター:チームメンバー
- Issueの内容
- とあるページのアクセシビリティ改善(10項目ほど)
実践
では、実際にどのようなフローで進めたのか、ステップごとに紹介します。
Step1 人間がIssueを理解する
今回のIssueは、Issue作成者が対象ファイルややることを割と細かく指示していてくれていました。
詳細はぼかしますが、概ね下記のように書いてありました。
## 対象URL
./hoge/fuga
## 対象ファイル
- a.html.slim
- b/foo 配下2ファイル
## やること
- `hoge/fuga` を `hoge・fuga` にして、 `foo・bar` の表記を合わせる
- フォームの入力の補助テキストを `label` と `input` の間に移動し、読み上げで制限がわかるようにする
- 各 `input` のエラーメッセージを `label` と `input` の間に移動し、読み上げでエラー内容がわかってから `input` に進めるようにする
- 400%拡大時にNGってほどではないが、input が非常に狭い箇所があるので、縦並びにする
- リンクに下線を追加する
- `h2` の 見出し を `h1` にする
- `main` タグで実装する
- `label` と `input` を紐づける
- 「更新する」ボタンの操作結果を Snackbar コンポーネントで表示する
- title を `foo` にする
普段は、対象ファイルや対象URLは作業者が見つけてくるような書き方が多いので、このIssueのディスクリプションは親切な方です。
変更すべき点も詳しく書いてあるので、そのままでも行けそうですが、それでもコードの内部を理解している人間向けのハイコンテキストな状態だと思います。
特に「400%拡大時にNGってほどではないが、input が非常に狭い箇所があるので、縦並びにする」という項目は、NGなのかNGじゃないのか結局は人間が目視しないとわかりません。
このままAIにわたすと「400%に拡大すればいいんだな」と解釈し、4倍に指定してくる可能性もありそうだねと話し合いました。
Step2 AI向けの「翻訳」作業(ペアプロの目的)
まだ完成に至っていませんが、GitHub MCPを活用して、AI向けにIssueの内容を翻訳してくれるプロンプトを作成しました。
実際に使ったプロンプト
---
mode: agent
---
あなたは、ユーザーがGitHubのIssueをAIが実装するための要件に変換するのを支援するAIアシスタントです。
# 目的
ユーザーが指定したGitHubのIssueの内容をもとに、実装に必要な要件を洗い出し、GitHub MCP のupdate_issue ツールを使用して、そのIssueに対して要件を追加することです。
下記のタスクの流れにしたがって、Issueの要件を作成・更新をしてください。
# タスクの流れ
## 1. Issueの特定
Github MCP の search_issues ツールを使用して、与えられたURLからIssueを検索し見つけます。
- リポジトリ名
- Issue_number
## 2. Issueの内容確認
Github MCP の issue_write ツールを使用して、特定したIssueの内容を取得します。
Issueのタイトル、説明、コメントなどを確認し、実装に必要な要件を洗い出すための情報を収集します。
## 3. 要件の洗い出し
収集した情報をもとに、実装に必要な要件を洗い出します。
要件は具体的で明確なものにし、実装の指針となるようにします。
洗い出した要件は、下記の形式で、 `.github/tmp` フォルダ内に、`requirements_{{ repo }}_{{ issue_number }}.md` という名前のMarkdownファイルに保存してください。
---
````markdown
# What
## Prompt (要件定義)
- 達成すべき目標と作業のステップを明確に定義します
- ウェルスコープ - 明確に範囲が定める
- 要求定義の構造化 - 実装ステップを明記する
## Context(背景情報)
エージェントが作業を行うために必要な既存のコードベースの知識と構造を提供します
## Constraints(制約/非機能要件)
セキュリティ、パフォーマンス、アーキテクチャの適合性など、守るべき品質の境界線を設定します
## DoD (完了の定義)
作業が成功し、PRがマージ可能と見なされるための具体的な検証基準を定めます
/````
---
## 4. 洗い出した要件の確認
依頼者に一度、作成した `requirements_{{ repo }}_{{ issue_number }}.md` を確認してもらってください。
確認が終わり、書き込みの依頼があるまで、Github の Issue の上書きやコメントはしないでください。
ポイント
- GitHub MCP のツール名も指定する
- Visual Studio Codeのチャット上で、ツールの指定をし直したことがあったので、使ってほしいツールが有るなら、明記したほうが良さそうでした
- 要件の洗い出した後すぐに Issue にコメントさせない
- 割と良さそうと思えるものを出力してくれることがありますが、修正する箇所も少なからずあります
- Visual Studio Code上で準備を進めたいので、コメントは許可があるまでしないようにして、tmpファイルを作ってもらうことにしました
- 洗い出した要件の出力形式を指定するときに、バッククオートを4つ並べます
- 通常の3つ(```)だと、生成されたMarkdown内のコードブロックと干渉して出力が崩れるため、外側を4つ(````)で囲む指示を入れています
ペアプロで出力結果を一緒に見る
途中経過を保存し忘れてしまいましたが、概ね期待する内容で翻訳されました。
ですが、最初に懸念した通り「400%拡大時にNGってほどではないが、input が非常に狭い箇所があるので、縦並びにする」の部分は「400%に拡大し、input を並びにする」というニュアンスで出力されました。
このままだと、ブラウザの400%拡大ではなく、input 要素を4倍に拡大しそうだということから、指示を下記に変更しました。
- ベタ書きのURLのテキストとinputを400%拡大時に縦並びにするレスポンシブデザインを実装
-
a.html.slimの.classにflex-wrap: wrapを追加 -
.class2にmin-width: 272px;とflex: 1;を追加
-
このように、指示項目を一つずつペアプロで一緒に確認して行きました。
翻訳結果(Copilotへの指示書)
最初の人間向けのIssueと同様、詳細はぼかしますが、AI向けに翻訳した要件は下記のようになりました。
## Copilot向け実装要件
このIssueをGitHub Copilotに実装してもらうための要件定義です。
### What(達成すべき目標)
fugaページ (`./hoge/fuga`) のアクセシビリティを改善し、WCAG 2.1 Level AA基準に準拠させる。
### Prompt(実装ステップ)
#### ステップ1: テキスト・ラベルの改善
1. `hoge/fuga` を `hoge・fuga` にして、 `foo・bar` の表記と統一する
- i18n対応で、日本語と英語も統一する
2. `h2` の `見出し` を `h1` に変更する
3. `title` を `foo` に変更する
- Rubyコード: `title = t('.title')` と `content_for :head` ブロックで実装
- 日本語・英語のlocaleファイルに対応する
#### ステップ2: フォーム構造の改善
1. `label` と `input` を `for` 属性と `id` 属性で紐づける
2. 文字数制限 (`200文字以内`) を `label` と `input` の間に移動し、スクリーンリーダーで読み上げられるようにする
3. 各 `input` のエラーメッセージを `label` と `input` の間に移動し、`aria-describedby` で関連付ける
4. ベタ書きのURLのテキストとinputを400%拡大時に縦並びにするレスポンシブデザインを実装
- `a.html.slim` の `.class` に `flex-wrap: wrap` を追加
- `.class2` に `min-width: 272px;` と `flex: 1;` を追加
#### ステップ3: セマンティックHTML・アクセシビリティ要素の追加
1. ページ全体を `main` タグで囲む
- `a.html.slim` の `.class3` を `main.class3` に変更する
2. リンクに下線 (`text-decoration: underline`) を追加する
- hoverしたときに、 `color: blueTextDim;` を追加する
#### ステップ4: フィードバック機能の実装
1. 「更新する」ボタンの操作結果をSnackbarで表示する機能を実装
- 成功時とエラー時の両方に対応
- `c.html.slim` を参考に実装を行う
### Context(背景情報)
**対象ファイル:**
- `a.html.slim`
- `config/locales/public/settings/profiles` 配下の2ファイル(日本語・英語)
**既存の実装参考:**
- Snackbarコンポーネント実装:`c.html.slim`
- デザインシステム変数:`styled/variables.tsx`
**関連Issue:** #1587
### Constraints(制約)
- WCAG 2.1 Level AA準拠
- スクリーンリーダーでの適切な読み上げ対応
- キーボードのみでの操作可能性
- 400%拡大時のレイアウト崩れなし
- デザインシステムの変数を使用
- CSSプロパティはアルファベット順に並べる
- 日本語・英語の両ロケール対応
**コーディング規約:**
- `bundle exec rubocop` でRubyコードのリント
- `yarn lint` でJavaScript/TypeScriptコードのリント
- 関連するRSpec/Jestテストの実行
### DoD(完了の定義)
**機能要件:**
- [ ] `hoge/fuga` → `hoge・fuga` に表記変更
- [ ] 文字数制限が `label` と `input` 間に配置
- [ ] エラーメッセージが `label` と `input` 間に配置で `aria-describedby` で関連付け
- [ ] 400%拡大で URL テキストと input が縦並び
- [ ] リンクに下線表示
- [ ] `h1` タグで `見出し` を表示
- [ ] ページ全体が `main` タグで囲まれている
- [ ] `label` と `input` が紐づいている
- [ ] 「更新する」ボタン結果が Snackbar で表示
- [ ] ページタイトルが `foo`
- [ ] 日本語・英語ロケール正常動作
**アクセシビリティ要件:**
- [ ] axe DevTools/Lighthouse で重大エラーゼロ
- [ ] スクリーンリーダーでの読み上げ正常
- [ ] キーボードのみで全操作可能
- [ ] フォーカスインジケーター明確表示
**コード品質:**
- [ ] `bundle exec rubocop` エラーなし
- [ ] `yarn lint` エラーなし
- [ ] RSpec/Jest テスト全て通過
- [ ] CSSプロパティがアルファベット順
**その他:**
- [ ] ツイート準備完了
- [ ] リリースノート作成
- [ ] 複数ブラウザ・デバイステスト完了
Step3 AIへの依頼
- 完成した要件定義をIssueにコメントする
- Issueのディスクリプション(本文)は、人間が読むための仕様として残します
- ディスクリプションと、AI向けに作った要件定義をためていって、今後の知見にしたいと思っています
- Visual Studio Code上から、GitHub MCP経由で「作成した要件」をIssueにコメントするよう指示しました。
- GitHub の Issue で Assignee に GitHub Copilotを追加する
Step3 で今回の目的はゴールに到達しましたが、実際にはもう2ステップ工程があります。
軽く触れておきたいと思います。
Step4 Copilotが作成したプルリクエストの内容を確認する
Copilotに修正指示を出しても良いですし、Copilotが作った branch を pull して、自分で修正しても良いです。
Copilot が作成したコードの責任を持つのは、結局依頼した自分自身なので、自分が「これでレビュアーに出せる」という状態までブラッシュアップします。
今回の記事では、ここは主題ではないのでさっと行きますが、コードが納得できる状態まで、修正対応します。
Step5 コードレビューの依頼をする
ここからのフローは、従来の開発と同様です
Step4 でレビュアーに出せる状態までブラッシュアップできたら、コードレビューを依頼します。
レビュアーからコメントをもらったら、修正対応をし、再度レビューを依頼するというサイクルを繰り返します。
レビューコメントの対応もまた、Copilot に依頼してもいいですし、自分で修正しても良いです。
Copilot に依頼する場合は、コメントの内容が抽象的だったり曖昧な場合は、Step2 の要件定義のように、AI向けに翻訳して修正指示を出す必要があります。
ペアプロして得られたこと
今回の「Issue作成ペアプロ」を通して、「AIにコードを書かせる」だけでなく、その前段階の 「AIに指示する言葉を定義する」 部分こそ、人間がペアプロで知恵を出し合う価値がある領域だと感じました。
一人でAIと対峙していると、期待通りのコードが返ってこない時にイライラしたり、「自分でやったほうが早い…」と感じてしまうことも多いです
しかし、ペアプロ形式であれば「今の指示だと伝わらないか」「次はこう言ってみよう」という試行錯誤自体を楽しむことができました。
また、自分よりもスキルの高い人にナビゲーターを担当してもらえれば、知らなかった機能や言い回しについての学びも得られます。
AIへの指示が明確になると、手戻りのコストが圧倒的に減っていくと感じました。
GitHub MCPのようなツールも組み合わせつつ、今後もこのフローをブラッシュアップしていきたいと思います。
この記事が誰かのお役に立てたら幸いです。
