投稿日
GitHub Copilotの利用レポート―パターン化とコメントの活用
もくじ
はじめに
こんにちは。デザイン&エンジニアリング部所属の下田です。最近の休日は、Mechwarriorに乗って過ごしています。
本記事は、GitHub Copilotの利用レポートです。私達が実際の現場で利用してみた工夫や所感をまとめています。
GitHub Copilotとは、コード補完の形で提案を行うAIを用いたペアプログラマで、良き副操縦士(Copilot)となるようにと、名付けられています。
概要やIDEへの導入方法は下記の公式ページを参照いただくとして、さっそくレポートしていきたいと思います。
操作方法
IDEはIntelliJ IDEA(以下、IntelliJ)とVisual Studio Code(以下、VS Code)で利用しました。キーマップは以下のようになります。
IntelliJ
操作 | キー |
提案を受け入れる | Tabキー |
次の提案を表示
前の提案を表示 |
Alt + ] (macOSはOption + ])
Alt + [ (macOSはOption + [) |
複数の提案を表示する |
|
プロンプトで提案させる | コメントに処理の概要を書いて改行 |
VS Code
操作 | キー |
提案を受け入れる | Tabキー |
次の提案を表示
前の提案を表示 |
ポップアップされるメニューの「>」をマウスクリック
ポップアップされるメニューの「<」をマウスクリック |
複数の提案を表示する | Ctrl + Enter (別のタブが表示されます) |
プロンプトで提案させる | コメントに処理の概要を書いて改行 |
提案(サジェスション)の受け入れはTabキーで行います。提案はおよそ1秒ほど待つかキーストロークがあると灰色のテキストで浮かびます。待つ場合は少しテンポが遅いと感じる人もいるかもしれません。
VS Codeの場合は、Ctrl+Enterで複数の提案を一度に提示させ受け入れることもできます。IntelliJの場合は上記表に書いてあるとおりですが、キーバインドによってか、他の動作が優先されました。
活用事例
実際の案件での利用として、フロントエンドは、TypeScriptでAPIのバリデーション定義と画面の設定値との突合検査で利用し、バックエンドは、Javaでテストデータビルダーやオブジェクトマザーと呼ばれるパターンの実装で利用しました。
最初にTypeScriptの例を紹介したあとで、Javaの例を紹介していきます。(なお、アニメーション画像は見やすさのため1.5~2倍程度にして再生しています。実際の提案のテンポは少し遅くなります。)
アーリーリターンの提案(クラス名の英訳と類似コンテキストの学習)
下のように、ある値がXHasOptionItem(選択オプションのある画面項目)というクラスのインスタンスであれば、処理を行わないようにアーリーリターンを実装していました。
普通のコードアシストと違い、クラスの英語の訳をコメントとして提案してくれ、「最大値はない」と提案を修正すると、次に「最小値」を書く場合は、そのまま適用できるコメントを提案してくれました。
構造の流用(2x3パターンのコードの残りを作成させる)
APIリクエストのバリデーション定義と、実際の画面項目の設定値とを突合させるテストコードで、全体の構成の実装とマッピング部分の実装の補完を行わせました。
Createのリクエスト、Updateのリクエストの場合に対して、Company、Group、Employeeのモデルがあり、合計6パターン実装する必要がありました。うち最初の2パターンは人間が実装し、それを「真似る」形で残り4パターンを実装させています。
下の図では、CompanyモデルのCreateとUpdateのリクエストで実装済みで、Groupに関して提案を元に作成しています。Ctrl+Enterで一気に提案させることもできます。
makeCompanyViewやmakeGroupViewなど、Create/Updateリクエストで共通関数がありますが、同じ処理を共通化せずに書いていると、本来は関数の中身になるべき内容が2回、そのままが展開されて書かれます。
したがって、既存のコーディングと同じように、DRY(Don’t Repeat Yourself)の原則や共通化は重要になります。
コメントの活用その1
冒頭で記載した公式のページに、「GitHub Copilot は、編集中のファイルや関連ファイルのコンテキストを分析し、テキスト エディター内から候補の提示を行います」とあるように、GitHub Copilotは近くに書かれたコードやコメントの流用が得意なことがわかりました。
以下にコメントの活用事例を紹介します。
コメントの活用その1とその2で紹介する例いずれも、指示を与えるのではなく、コメントを書き、最初の1つを書いてあとは「雰囲気」を察してパターンを提案してくれます。もちろんコメントは不要になったら消して構いません。
§コメントからの類推① 既存コードの部分抽出
既存のコードからパターンを抜き出して別のコードを生成している例です。
下の図では、APIリクエストのバリデーション定義と、実際の画面項目の設定値とを突合させる場面で、既に実装済みである「画面項目の値をリクエストに詰める処理部分」をコピーしてコメントとし、類似の突合のマッピングを作っています。
コードからの活用
この例からはJavaでの事例になります。
GitHub Copilotを使って作ったテストデータビルダーとはどんなものかを簡単に見せると、以下の図のように、組織>グループ>ユーザーであったり、イテレーション、ボード>タスクであったりする階層構造を持つデータを流れるようなAPIで実装するビルダーパターンの一種です。
テストで使うある決まったデータを返すメソッドやクラスをオブジェクトマザーとも呼びます。
なお、上の図ではTODOボードを書くと、DOINGボード、DONEボードを自動提案で出してくれました。これもコンテキストを雰囲気で理解して出しているようです。
コメントの活用その2
§コメントからの類推② SQL文からデータ抽出
SQLのインサート文から部分抽出してデータ作成APIに渡す例です。
同じくJavaで、テストデータの入ったテーブルからSQLを生成してコメントとして貼り付け、そのデータを作成用のビルダーパターンのAPIに渡しています。実際の現場では、マスターデータ作成SQLからテストデータを作成するところで役立ちました。
§コメントからの類推③ メンバの定義
メンバの定義のコメントからメンバを作っているところです。
Javaで、ビルダー対象をクラスコメントに記述して対象が何であるかのヒントとしています。IDEのGetter生成機能と合わせればタイプ量を減らして実装できます。
構造の流用その2(ビルダーメソッドの実装)
ビルダーのメソッドは、大別して子構造を持たない場合と子構造を持つ場合の2種類があります。子構造を持たないパターンはreturn this; をするメソッドにして、メソッドチェインをドットで繋げて流れるようなAPIとして書けるようにします。
下の図では、ビルダーメソッドのiterationと内部メソッドcreateIterationを手で実装し、それ以降のビルダーメソッドboardと内部メソッドcreateBoardは提案に任せています。3個目以降も同様になります。なお、モデルのセッターを埋める実装は、自動生成では難しいので共通のNameとIdだけに絞って全体の雛形を作ることを優先しています。GitHub Copilotが得意な領域を先にさせることで人間がやるタスクは後で集中してできます。画像は2倍速ではありますが、モデルが多数ある場合には効率は上がるようになります。現場での利用では5モデルから10モデル程度あったので大変助かりました。
子構造を持つ場合は、たとえばboard(String name, String value, Task… tasks)のように最後の引数や可変引数で子供を足します。
以下の図では、boardメソッドにtaskを足す処理を追加しています。
複数種類のビルダーを実装していましたが、2クラス目以降ではforループ等を補完してくれるようになったり最初から子構造を持つコードを提案してきたりするようになりました。子構造を持たないのであれば、一旦提案を受け入れて削る作業をすれば良かっただけでゼロから書く作業は省略できました。
プロンプトを使う
コメントに次に行いたい処理を書いて置くと、関数の形で提案してくれます。
以下の図が、メンバをSystem.out.printlnに出力する関数のプロンプトの例です。プロンプトは日本語でも構いません。
なお、forループをメソッド参照のforEachに書き換えています。これも1つ目以降は自動で提案してくれました。
以上が、レポートの内容になります。
まとめ
- 良かった点
- パターン化に強い
- 逆に言うと、パターン化された状況を作ってやると良いです。
- 変数名、関数名、コメントなどをパターン化して繰り返すと覚え提案してくれます。
- 近くにあるコメントがとても有効
- 同じファイルのコードや近くのコメントの抽出や流用が得意なようです。(編集中のファイルや関連ファイルを分析するため)
- 提案の元になるコードの品質や簡潔さは、特に重要。容易に冗長なコードも量産できるため、共通化しコンパクトに保ち、質の良いコードにしてから、量産することが大事です。
- パターン化に強い
- 良くなかった点
- 初回には弱い
- 参考にするファイルを開いていて、クラスは違うのに全く同じものを提案してくることがありました。
- かっこの整合性が弱いときがある
- TypeScriptでよく起こっていましたが、([{id:1},{id:2}])のような例で、閉じかっこが「}])」あるべきところが「})」となったりしました。
- 提案の再現性は高くはない
- 同じ状況で同じ提案をするとは限らない。コメントなども異なる。
- 完全に同じ提案をしてくることが少なく、実行するたびに変わることがありました。
- 初回には弱い
さいごに
定量的な観点から、Javaの9種類のビルダークラスで、50以上のモデルのオブジェクトのツリー構造の作成する実装を行いましたが、少なくとも6-7割程度はGitHub Copilotによる自動生成をそのまま利用できました。
特にパターン化とコメントの活用は有効で、最後の方のビルダークラスの実装では、10分程度で150~300行を生成することができました。残りの3-4割は、人が判断しながら行わなれければならない難しい作業で、それに集中できました。
GitHub Copilotは、現時点でも生産性向上が十分あると考えていますが、今後もより一層欠かせないものとなるとでしょう。