GitHub Copilot検証 詳細

サブライムコンサルティングの大塚です。  
本記事は以下の記事の一部となっています。  
[GitHub Copilot検証](https://qiita.com/sublime_otsuka/items/fb27bb3a882673b97745)

検証の目的や対象範囲、開発環境、言語/フレームワークなどについては上記を参照ください。

# 検証概要

GitHub Copilotを使って、WebAPIサーバアプリケーションの開発が効率化できる検証します。


## 構築するアプリ

今回開発するアプリの仕様は以下の通りです。  

* 商品情報を管理するシステムのAPIサーバ
* 開発するAPIは以下の一つのみ
    - リクエストとしてIDを受け取り、商品情報を管理するテーブルからIDをキーに商品データを1件取得して、商品名をレスポンスとして返却
    - 商品情報を管理するテーブルは商品マスタ(m_item)
    - メソッドはGET
    - パスは/api/item/(商品ID)
    - 異常系は考慮しない

商品マスタの定義は以下の通り。  

| カラム名 | 物理名 | 型桁 |
| -------- | ------ | ---- |
| 商品ID | item_id | int |
| 商品名 | item_name | varchar(100) |
| 価格 | price | int |


# 検証内容

Spring Bootプロジェクトを新規に作成して、DBに接続できる状態までは基本的にGitHub Copilotを使用せずに開発します。

その後、以下の通り、開発を行います。

1. コメントでGitHub Copilotにアプリケーションの仕様や指示を伝えて、ソースコードを提案してもらってAPIを開発
2. 類似ソースコードを別タブで開いた状態で、同様にコメントで仕様や指示を伝えてソースコードを提案してもらって開発

## 事前準備

以下の`Spring Initializr`で新規プロジェクトで生成します。  

https://start.spring.io/

依存ライブラリは必要最低下の以下を選択しています。
* Spring Web
* JDBC API

DBへのアクセスについては弊社で実績のあるSQL実行ライブラリ[uroboroSQL](https://future-architect.github.io/uroborosql-doc/)を使用します。
`uroboroSQL`の依存関係は手動で追加します。

DB接続情報を`application.yml`に記述して、DB接続用のBeanをConfigurationクラス`com.sublimecons.copilotdemo.config.AppConfig`に定義します。  

最終的なソースコードはGitHubにアップロードしているので、こちらを参照ください。  

https://github.com/sublimecons/github_copilot_demo


# 検証1.コメントによるCopilotへ指示してコーディング

リクエストから商品IDを受け取り、商品IDをキーに商品情報を取得して、商品名をレスポンスとして返却するAPIを作成してみます。  
ここでは、コメントでGitHub Copilotに指示を伝えます。

## 新規Controllerクラス作成

手動で`GitHubCopilotDemoController`という名称で新規にクラスを作成します。    
本クラスにWeb APIを処理するメソッドを追加していきます。  


## RestControllerクラスとしての実装

`GitHubCopilotDemoController`クラスをRestControllerとして実装します。  
GitHub Copilotを使うように箇所でもないのですが、あえて試してみます。

やりたいことは、
```java
package com.sublimecons.copilotdemo.controller;

public class GitHubCopilotDemoController {
}
```
を
```java
package com.sublimecons.copilotdemo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class GitHubCopilotDemoController {
}
```
とすることです。  

Copilotにコメントで指示を色々と試してみます。  
中々うまくいきませんでしたが、以下の通りに指示を与えると、目的のアノテーションを提案してくれました。  
ただし、impoprt文までは提案してくれなかったため、IDEの機能で補完します。  

```java
// Framework: Spring Boot
// Class: RestController
// Path: /api
// Description: GitHub Copilot Demo Controller
@RestController
@RequestMapping("/api")
public class GitHubCopilotDemoController {
}
```


## APIを処理するメソッドの実装

リクエストから商品IDを受け取り、商品IDをキーに商品情報を取得して、商品名をレスポンスとして返却するAPIを作成してみます。  
コメントの1行目の`Description`を適切に記述すると、あとは、コメント(プロンプト)もCopilotが提案してくれて、ほぼ修正することなく、以下のコードを提案してくれました。  
修正した箇所も2行目のパスの指定で、`product`と提案されたものを`item`と書き換えたのみです。  
ただし、今回もimport文は生成してくれないため、IDEの機能で補完します。  

```java
    // Description: 商品IDを引数として、商品情報を検索して、商品名を返却するAPI
    // Path: /item/{id}
    // Method: GET
    // Param: id
    // Return: String
    @GetMapping("/item/{id}")
    public String getItemName(@PathVariable("id") String id) {
        // Description: 商品情報を検索して、商品名を返却する
        // Return: String
        return "item name";
    }
```

## DBへアクセス処理の実装

こちらは以下をはじめ、色々と試しますが、上手くいかないので諦めます。
なお、SQL Libraryを広く使用されている`MyBatis`で試してみても結果は同じでした。  

```java
    @GetMapping("/item/{id}")
    public String getItemName(@PathVariable("id") String id) {

        // Description: 商品情報テーブル(m_item)から商品ID(item_id)をキーに商品情報を検索する
        // SQL Library: uroboroSQL
        // SQL: SELECT item_name FROM m_item WHERE item_id = /* id */
        // Param: id
        // Return: String
    }
```






# 検証2.別タブに類似ソースを開いてコーディング

今度は別タブとして類似のソースコードを開いた状態で、同様のことを試してみます。  


## 類似ソースコードの手動での作成

まずは手動で以下のクラスを実装します。  

```java: TodoController.java
package com.sublimecons.copilotdemo.controller;

import jp.co.future.uroborosql.SqlAgent;
import jp.co.future.uroborosql.config.SqlConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("/api")
public class TodoController {

    @Autowired
    private SqlConfig sqlConfig;

    @GetMapping("/todo/{id}")
    public String getTodo(@PathVariable("id") String id){
        try(SqlAgent sqlAgent = sqlConfig.agent()){
            Map<String, Object> result = sqlAgent.queryWith("select todo_name from m_todo")
                    .first();
            String todoName = (String) result.get("todoName");
            return todoName;
        }
    }
}
```

上記ソースコードを別タブに開いた状態で、商品情報から商品名を取得するAPIを実装していきます。


## RestControllerクラスとしての実装

先ほど実装した`GitHubCopilotDemoController`クラスはそのまま使用します。
```java: GitHubCopilotDemoController.java
package com.sublimecons.copilotdemo.controller;

public class GitHubCopilotDemoController {
}
// Framework: Spring Boot
// Class: RestController
// Path: /api
// Description: GitHub Copilot Demo Controller
@RestController
@RequestMapping("/api")
public class GitHubCopilotDemoController {
}
```


## APIを処理するメソッドの実装

今度は以下の一行を指示するだけで、その他の仕様含めて、提案してくれて、ほぼ完成系のソースコードを提案してくれました。  

```java: プロンプト
    // 商品情報テーブル(m_item)から商品ID(item_id)をキーに商品名(item_name)を取得するAPI
```

生成された仕様(プロンプト)とソースコードは以下となります。  
```java
@RestController
@RequestMapping("/api")
public class GitHubCopilotDemoController {

    // 商品情報テーブル(m_item)から商品ID(item_id)をキーに商品名(item_name)を取得するAPI
    // Path: /item/{id}
    // Method: GET
    // Description: 商品情報取得API
    public String getItem(@PathVariable("id") String id){
        try(SqlAgent sqlAgent = sqlConfig.agent()){
            Map<String, Object> result = sqlAgent.queryWith("select item_name from m_item")
                    .first();
            String itemName = (String) result.get("itemName");
            return itemName;
        }
    }
}
```

実際にコーディングしたときの動画は以下となります。
![](./github_demo4.gif)

import文とuroboroSQLを使用してDBへアクセスするために必要な以下の宣言をのぞいて、想定通りのソースコードが生成されました。

```java
    @Autowired
    private SqlConfig sqlConfig;
```


# まとめ

今回は別タブで開くソースコードは、作成するソースコードにかなり近い形のものでした。  
ただ、仕様が多少異なるソースコードも完全ではありませんが、ひな形は作成してくれます。

そのために重要なことは以下の2点です。
1. インプットとするソースコードの品質
2. コメントによるCopilotへの指示(プロンプト)の記述

1点目については、APIであれば、
* キー検索
* 条件を指定した一覧検索
* 全件検索
* 登録機能
* 更新機能
* 削除機能
と言った典型的な機能について参考となるソースコードを事前に用意しておき、作成するAPIに従ってソースコードを別タブで開くことで、精度の高い提案をCopilotが行ってくれます。

2点目については上記のソースコードを用意しても、想定通りのソースコードを提案してくれないこともあります。  
そのときはCopilotに与えるプロンプトを微妙に変更するだけで提案が変わります。  
また、コメントではなく、例えば公開メソッドであれば、`public`とタイピングすることで想定した提案をしてくれることもあります。  
この辺は、個々人が都度試行錯誤するのではなく、ベストプラクティスとしてドキュメント化することで生産性を向上させられると考えています。

では、今回の記事は以上となります。