新卒でフロントエンド開発者をしています、イソダです。
約半年前、業務経験3か月目のとき、ついに私もGitHub Copilotを使用できるようになりました。これで開発速度爆上がりだと意気込んでいたのですが、思ったより速度が上がらない。。。
GitHub Copilotが提案してくれるコードが間違っていたり、あまりきれいでなかったりするので、結局は自分で書き直したり、間違いがないか入念にチェックしたりという作業が起きました。
理想とはほど遠いなと思いながら日々を過ごしていると、ある時気づいたことがありました。それは、間違いばかり提案してくるのは、自分のコードが汚いからでは?ということです。
GitHub Copilotは既存のコードを読み取って、文脈に合わせたコードを提案してくれます。公式ドキュメントには次の記載があります。
GitHub Copilot は、編集中のファイルや関連ファイルのコンテキストを分析し、テキスト エディター内から候補の提示を行います。
GitHub Docs, “GitHub Copilot Individuals について”,2024/06/03
よって、既存のコードが汚いから、文脈を正しく理解できず、間違った提案をするのだと考えました。
そのため、GitHub Copilotにバリバリコードを書いてもらうということは諦めて、もっと違う形で有効的に使うために工夫していこうと方向転換しました。その結果、自身の成長を加速させるような使い方ができてきたと感じています。
開発初心者向けにはなると思いますが、今回はその使い方を紹介します。
1. 間違いを提案されたら既存のコードをきれいにする
GitHub Copilotの提案に間違いが多く含まれているときは、だいたい既存のコードが汚いため文脈が把握しづらく、次のコードの流れが推測しづらいときです。そのため理解しやすいように既存のコードをきれいにしてあげます。ルンバが掃除しやすいように部屋を片付けるのと同じです。
この方法のメリットは次が考えられます。
- GitHub Copilotの提案が正確になり、以降の開発速度が上がる。
- きれいなコードを書く意識が身についたり、きっかけになる。
- 自分がコードを読み返したり、コードレビュアーがコードを読む負担を減らせる。
コードをきれいにする方法はいろいろあります。私はよく次に挙げるものを行っています。
- ロジックを短く簡潔に書き換える。
- 使わないコードは削除する(コメントアウトで残さない)。
- ロジックを説明変数に代入したり関数に切り出したりで名前を付ける。
- 変数や関数の順番を入れ替えて、コードの流れをきれいにする。
基本リーダブルコードという書籍を参考にしています。
最初の提案
では、実際のコードをきれいにする前後でGitHub Copilotの提案が変わる例を示したいと思います。ユーザーのロールを設定する機能を実装していた時のことです。この時ロールが変更されているかを判定する変数isDirty
を追加しようとしました。実際にGitHub Copilotが提案してくれたコードが次のスクリーンショットになります。
この提案は間違っています。ロールが変更されているかを確認するには、変更後のロールを持つ変数projectUserRoleValue
の値と、変更前のDBから取得したロール情報projectUsers[0].roles.find((role) ⇒ role.type === 'general').role_id
とが異なるかを確認する必要があります。そして、これらの変数はisDirty
の宣言前には既に揃っていました。しかしGitHub Copilotの提案では、変更後のロールを持つ変数projectUserRoleValue
が何かしらの値を持っていれば、ロールが変更されているという判断を下しています。
このように間違いを提案されるときは、既存のコードが汚くなってきているという目印です。実際前述した変更前のDBから取得したロール情報projectUsers[0].roles.find((role) ⇒ role.type === 'general')
は、ぱっと見では何を表現しているのかわかりません。
改善後の提案
それでは、コードをきれいにするとどうなるでしょうか。先ほどの変更前のロール情報を変数initGeneralUserRole
に代入して、何を表現しているかをわかりやすくします。
const initGeneralUserRole = projectUsers[0].roles.find((role) ⇒ role.type === 'general');
他にもコードを短く簡潔にしたり、わかりにくいロジックを分かりやすい名前の変数に代入したりとしていきます。その後、再び変数isDirty
を追加しようとすると、GitHub Copilotの提案は次のようになります。
この提案は合っています。変更前のロールinitGeneralUserRole
と変更後のロールgeneralUserRoleIdInProject
とが異なっているか比較できてします。
このように既存のコードをきれいにするだけで、GitHub Copilotの提案精度は向上してくれます。
2. 想定と違う提案をされたら英単語の意味を詳細に調べなおす
あたりまえではありますが、実装したいロジックとそれに付ける名前とが一致していないときは、GitHub Copilotの提案も実装したいロジックとは異なるものになります。
筆者の英語力では、こういったことがちょくちょくおきます。英単語の正確な意味を誤解してたり、ニュアンスまで知らなかったりして、名前付けを間違えます。そのため想定していたロジックと異なる提案をされたら、名前付けを見直します。特に英単語の意味をきちんと調べるようにしています。
この方法のメリットは次があげられます。
- GitHub Copilotの提案が正確になり、以降の開発速度が上がる
- 名前付けのスキルが向上する
- 英語の勉強になる
最初の提案
例を挙げます。プロジェクト情報の配列のある要素を移動する関数を実装しているときのことです。このとき移動後の配列要素のインデックスnewIndex
の1つ前方の要素のプロジェクトを参照する必要がありました。このプロジェクトを持つ変数をformerProjectOfNewIndex
として定義しようとすると、GitHub Copilotは次のスクリーンショットのように提案してくれました。
この提案は実装したいものと異なります。しかし、これはGitHub Copilotが間違っているのではく、変数の名前が少し不適切です。
辞書で英単語の意味を調べると次のような記述が出てきます。
「former」が形容詞として使われる場合、過去の地位や状態、あるいは以前の時点での事柄を指す。
weblio 実用英語辞典, 2024/05/28
つまり、formerProjectOfNewIndex
は配列要素を移動させる以前のnewIndex
の位置にあるプロジェクトという意味になります。formerの意味を軽く検索しただけだと、「前の」という意味が引っかかるので、英単語の正確な意味を誤解していました。
改善後の提案
変数名を見直して、変更した後のGitHub Copilotの提案が次のスクリーンショットになります。
こちらは想定通りnewIndex
のひとつ前の位置のプロジェクトを変数に代入してくれています。
3. 知らないコードを提案されたら理解して糧とする
コードを書いていると、筆者がまだ知らない文法や組み込み関数を使った実装方法をGitHub Copilotが提案してくれる時があります。
こういったとき、時間がない、調べるのが面倒などの理由で提案を無視して、自分の知っている方法で実装したくなります。しかし、できるだけ提案された方法を調べて使ってみるようにしています。
これのメリットは次が挙げられます。
- 言語への知識が増える
- その言語の慣習に沿った実装方法が身につく
- (提案されたものが良ければ)ロジックをより簡潔に記述できる
このメリットの中で1番大きいのが「その言語の慣習に沿った実装方法が身につく」だと思います。それぞれの言語にはコードの品質を良くするための慣習があります。
JavaScriptだと配列をfor文で回すのでなく、map関数やreduce関数を使って副作用が起きない純粋な関数として実装するなどです。
GitHub Copilotの提案はこういった慣習に沿った提案してくれることが多いです。そのため、提案してくれたコードを、きちんと理解して使えるようになると自然とコードの品質も良くなっていきます。理解するためには言語リファレンスを調べるなどの労力がかかりますが、1度理解してしまえば自分の血肉となり、以降ずっと使える資産になります。
まとめ
今回紹介したような使い方でGitHub Copilotを使用すると、既存のコードを見直したり、新しい文法や組み込み関数を知るきっかけになります。この習慣を継続して学習を進めることで、言語の習熟が促進されたり、開発スキルの成長が加速していると、筆者は感じています。