虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

GitHub Copilot で効率よく開発するためのTips

こんにちは、Mです。
虎の穴ラボにて GitHub Copilot が導入され、使っていくうちに特徴や使い方がわかってきたので、簡単にTipsとして共有したいと思います。 toranoana-lab.hatenablog.com

私はIntelliJ IDEAを使用していますが、他のエディタでは挙動が異なる可能性があります。

特徴

タブで開いているファイルを参照する

例えば、AnimalというEnumファイルがあるとします。

public enum Animal {
    CHICKEN("にわとり"),
    PIG("豚"),
    SHEEP("羊");

    private String japaneseName;

    Animal(String japaneseName) {
        this.japaneseName = japaneseName;
    }

    public String getJapaneseName() {
        return japaneseName;
    }
}

TestClassから呼び出し、switch文を作りたいと思います。

public class TestClass {
    public void test() {
        Animal animal = Animal.CHICKEN;
        switch (animal) {
            // ここをGitHub Copilotで生成してもらう
        }
    }
}

case文を生成してほしいですが、GitHub Copilotで予測できるのは以下の情報です。

  • AnimalというEnumを使って switch文を作ろうとしている
  • Animalに何が含まれているのかはわからないが、 CHICKEN が存在する

という情報しかないため、以下のようなコードを生成します。

Animal animal = Animal.CHICKEN;
switch (animal) {
    case DOG:
        System.out.println("犬です");
        break;
    case CAT:
        System.out.println("猫です");
        break;
    case CHICKEN:
        System.out.println("鶏です");
        break;
    default:
        System.out.println("その他です");
        break;
}

推測で猫などがでてきましたね。これでは意味がないため、Animalを参照してほしいです。
参照させるためには、タブを同時に開いておく必要があります。

参照したいAnimalもタブで同時に開いておき、再度実行してみると、

Animal animal = Animal.CHICKEN;
switch (animal) {
    case CHICKEN:
        break;
    case PIG:
        break;
    case SHEEP:
        break;
    default:
        break;
}

参照して適切なcaseを作ってくれました。

関連するファイルだけをタブで開くことで、GitHub Copilotの予測精度が高まります。
反対に、関係ないファイルをタブで開いていると間違った予測をしてしまう可能性も上がるため適切なファイルのみに絞る事が大切です。

この仕様は、SQLの生成などに非常に便利です。CREATE文などを含んだSQLファイルをタブで開いておくことで、結合条件なども満たしたSQLを作成してくれます。

-- USERテーブルの作成
CREATE TABLE USER (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    age INT,
    email VARCHAR(50)
);

-- JOBテーブルの作成
CREATE TABLE JOB (
    id INT PRIMARY KEY,
    user_id INT,
    title VARCHAR(50),
    company VARCHAR(100),
    start_date DATE,
    end_date DATE,
    FOREIGN KEY (user_id) REFERENCES USER(id)
);

-- USER と JOB を結合して取得するSQL( GitHub Copilotで生成 )
SELECT
    u.id,
    u.name,
    u.age,
    u.email,
    j.title,
    j.company,
    j.start_date,
    j.end_date
FROM
    USER u
INNER JOIN JOB j ON
    u.id = j.user_id

既に書いた書き方を覚えてくれる

public class TestClass {
    public void test() {
        List<String> stringList = getStringList();

        // nullチェック
    }

    public List<String> getStringList() {
        return null;
    }
}

getStringList() メソッドでリストを取得し、その後に null チェックを GitHub Copilot に生成してもらいます。 そのまま生成すると以下のようなコードになります。

// nullチェック
if (stringList != null && !stringList.isEmpty()) {
    System.out.println("リストが空ではありません");
}

内容を変更してみました。

// nullチェック
if (stringList != null) {
    for (String string : stringList) {
        System.out.println(string);
    }
}

この状態でもう一つのメソッドを追加し、同様に null チェックのコードを生成してみると、修正した for 文を使ったコードが生成されます。

public class TestClass {
    public void test() {
        List<String> stringList = getStringList();

        // nullチェック
        if (stringList != null) {
            for (String string : stringList) {
                System.out.println(string);
            }
        }
        
        List<Long> longList = getLongList();
        
        // nullチェック
        if (longList != null) {
            for (Long longValue : longList) {
                System.out.println(longValue);
            }
        }
    }

    public List<String> getStringList() {
        return null;
    }

    public List<Long> getLongList() {
        return null;
    }
}

GitHub Copilotが考えたコードを無理に提案せず、柔軟にこちらの書き方に従ってくれます。
ただし、良くない書き方をしていたとしてもそれを学習し提案してしまいます。
自分の知らない書き方を提案してきた場合は、一度調べてみてその書き方が適切か検討する必要があります。

テストコードはGitHub Copilotの判断が優先される?

Lambdaのデフォルトコードのhello worldを返却する関数のテストコードを生成してみました

export const lambdaHandler = async (event, context) => {
    try {
        return {
            'statusCode': 200,
            'body': JSON.stringify({
                message: 'hello world',
            })
        }
    } catch (err) {
        console.log(err);
        return err;
    }
};
// lambdaHandler のテスト
describe('lambdaHandler', function () {
  it('should return "Hello World!"', async () => {
    const result = await lambdaHandler(event, context);
    expect(result).to.be.an('object');
    expect(result.statusCode).to.equal(200);
    expect(result.body).to.equal('Hello World!');
  });
});

生成されたテストコードですが、ステータスコードのチェックなどはあっていますが hello worldHello World! になっており通らないテストコードになっています。

推測ですが、lambdaHandler を読み込みハローワールドを返却するというところまでは理解し、
ハローワールドのテストコードであれば Hello World! が一般的なのでこちらが返却されるべきという判断をしてこのテストコードを生成したのだと考えます。

とりあえず通るテストコードを生成するのではなく、そのコードの本質的な部分を考えてこうあるべきという提案をしてくれるので面白いですが注意が必要です。

最後に

GitHub Copilotは使い始めたばかりですが、とりあえず有効にしておくだけで優秀な提案をしてくれるので非常に助かっています。

まだまだ有効活用できるような書き方ができるように引き続き研究を続けていきたいです。

P.S.

採用

虎の穴では一緒に働く仲間を募集中です!
この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧下さい。
yumenosora.co.jp