GitHub Copilotを調教してAndroid開発してみた

今回はイヤイヤ期に入ってしまって意地でもコードを書きたくなくなった僕がGitHub Copilotを調教して代わりにAndroid開発をさせてみた結果を共有したいと思います。

## GitHub Copilotって何？
AIがコードの補完をしてくれる機能です。
ZOZOでは[GitHub Copilotが全社導入](https://techblog.zozo.com/entry/introducing_github_copilot)されているので僕も試しに使ってみる事にしました。Android StudioにてPluginsを入れてGitHubにログインする事ですぐに使えるようになりました。

![スクリーンショット 2023-12-22 20.31.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/5037d2c4-ec7d-b259-0c8c-6fe7f2f109a8.png)

![スクリーンショット 2023-12-23 18.46.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/f86824bd-b564-7f28-f420-eefcfbafa7f3.png)

## 何も考えずに使ってみた感想
導入しただけで特に意識せずいつも通り作業するのみだと「ちょっとすごいサジェスト機能」くらいの感覚です。頭の中読まれてるな頭にアルミホイルもっと巻かないと、と思えるくらい先読みしてサジェストしてくれる時もたまにありますが全く意味不明なサジェストも多かったりで劇的に効率が上がるような体験は得られませんでした。

## プロンプトエンジニアリングって何？
この記事を書くにあたってとにかく最初に色々試す事をやって行き詰まり、それからドキュメントを読みました。その中で自分がやろうとしている事がプロンプトエンジニアリングだという事にあとから気付きました。

こちらのブログで大きく3つのポイントが解説されています。

https://github.blog/2023-06-20-how-to-write-better-prompts-for-github-copilot/

1. 高いレベルの目標を設定してステージを設定する
2. 質問はシンプルかつ具体的にし、短い出力を受信することを目指す
3. 1つか2つ例を挙げる

ですが、個人的にはあまりしっくりこなかったです。むしろ下記の内容が分かりやすく感じました(言いたい事は一緒なのかもしれませんが)

https://dev.to/github/a-beginners-guide-to-prompt-engineering-with-github-copilot-3ibp/#1424065

> - コンピューターは指示されたことしか実行できません
> - 指示を非常に具体的にする必要がある
> - 彼らは一度に一歩ずつ命令を受けるのが得意です
> - アルゴリズムは単なる一連の命令です
> - たとえば、「ロボット」にサンドイッチを作るように指示する場合、次のように指示する必要があります。
>
> 1. パンの袋を開ける
> 2. 袋から最初の2枚の食パンを取り出します
> 3. 食パンをカウンターに並べて置く
> 4. バターナイフでパンの一枚にピーナッツバターを塗ります
> 5. などなど

自分で色々試してみた結論と通じる部分があり、先に読んでおけば良かったなあと感じました。

## コメントからコードを生成する
手始めにユーザーのデータを持つdata classを以下のようなコメントでコードを生成してみました。これだったら素直にコード書いた方が早いじゃんと思われるかもしれませんがイヤイヤ期なのでできません。

![スクリーンショット 2023-12-22 21.23.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/12543538-3e67-b889-f6c2-2428957024ba.png)

`@param`を活用したコメントで書いた方が人間にも分かりやすいですね。
![スクリーンショット 2023-12-22 21.24.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/a1665412-42b4-5cbb-543d-56f292ec7c2c.png)

## 理解できない概念があると途端にポンコツ
1つ困ったのが性別をenumで定義しようとしたのですがどういうわけか意地でもサジェストしてくれませんでした。後述しますがgenderという概念が出てくると途端にGitHub Copilotがだんまりしてしまうのでここだけは仕方なく手動で書きました。この辺りの話題には触れたくないのでしょうか？

![スクリーンショット 2023-12-22 21.27.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/6b5595f2-91fc-f586-9bb7-9a59bd680e7f.png)
![スクリーンショット 2023-12-22 21.26.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/190be77e-c0af-805f-d473-f54dcedfa3e8.png)

GitHub Copilotは別タブのコードも読んでサジェストしてくるので関連するコードは開いておいた方がより正確になるらしいのですがこの件に関しては逆効果になりました。また、別プロジェクトの別タブも読んでいるのでそこは注意です。基本的には質の高いコードを食わせた方が精度が高くなる認識で大丈夫だと思います。チーム開発などでコーディングガイドラインを食わせて統一された表記にできるような使い方ができたら良さそうですね。

## 汎用的なものは得意
逆にハッキリと答えが分かるものはすごく得意なようで曜日や月などはもちろん都道府県などもサジェストしてくれます。
ただ、EHIMEのあとで止まってしまい、KOUまで入力しないとKOUCHIが出てきませんでした(23/12/24時点)。高知県は四国の中ではメジャーな方だと思ってたのですが不思議です。

![スクリーンショット 2023-12-22 21.30.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/1f465a5f-f13e-1823-571c-386198dbee55.png)

おまけですが歴代ダービー馬を列挙してもらったところ怒涛のキタサン連発されてしまいました。

![スクリーンショット 2023-12-22 21.32.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/4ec33380-3f1a-af31-0f8f-ee54d42fb271.png)
![スクリーンショット 2023-12-22 21.34.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/12a724e0-2ef4-4b7d-8761-f282ab1bfaae.png)

最後の方のゼンショー(全勝？)へのこだわりはなんなんでしょう。たまにゼンシューも混じりますね。そもそもダービー勝ってる馬の方が少なくないですか？

## 定型文に強い
バージョンカタログの記述などは定型文なので大得意です。
![スクリーンショット 2023-12-22 21.40.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/18187761-0f80-5e0c-a4c6-918edf7847c6.png)
![スクリーンショット 2023-12-22 21.42.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/1727e1f6-96cf-b515-0abc-d42470741992.png)

ViewModelの記述も`// ViewModel`と書くだけでサジェストしてくれます。
![スクリーンショット 2023-12-22 21.44.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/f1567ea4-cd36-332f-091c-0227467fee39.png)

## ViewModelの中身を作ってみる
`ViewModelを定義する`これだけだと意味が分からないみたいでサジェストしてくれません。
![スクリーンショット 2023-12-22 21.46.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/e14f7867-6be2-0a83-67dc-26d505e1a4e8.png)

ですが、`ViewModelを〜`とコメントから`implementsする`とコメントをサジェストしてくれました。更におまけでユーザーデータの追加や削除処理まで付けてくれました。このようにコメントもサジェストしてそれをベースにコードもサジェストしてくれるのが便利です。

![スクリーンショット 2023-12-22 21.53.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/d4e2b8e5-2f74-98c3-511f-762deed7e822.png)

ユーザーデータはUiStateとしてView側に公開したいのでUiStateを定義します。最初はMutableStateFlowを公開したので
![スクリーンショット 2023-12-22 21.55.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/c4229491-f6b1-9d3d-5bd0-2bc2ca64b62e.png)
`privateな〜`や`publicな〜`とコメント追記するとすぐに意図を理解してくれました(最初に`private`と記述したらあとのコメントはサジェストしてくれました)

![スクリーンショット 2023-12-22 21.57.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/485208c6-c513-0755-18c0-1ea3eb75d51c.png)

`asStateFlow()はstateFlowを返す`という一文はサジェストされた文言をそのまま採用したらコードも期待するサジェストを出してくれました。ですが、人間があとから読むと意味不明になるのでこういうところは人力で修正した方がよさそうですね。

## ComposeViewを作ってみる
ここが一番苦戦しました。コメント1つでサジェストの内容ががらりと変わるので試行錯誤が非常に多かったです。また、先程も伝えました通りgenderが入ると精度がやたら下がるのでタブを閉じたり一時的にコード削除したりしました。
色々試行錯誤の結果、以下のようにすると比較的精度の高いサジェストが来るように感じました。

1. やりたいことを伝える
2. やりたいことはできるだけ短文で伝える
3. やりたいことを達成する為には何を使うか/使わないか伝える
4. 命令は冒頭にまとめる
5. 上手くいかなければ1行の命令の追加と削除や命令文の言葉のニュアンスを変えてみる

冒頭にお伝えしたプロンプトエンジニアリングの基礎の内容に近しい印象ですがいかがでしょうか。
> 1. パンの袋を開ける
> 2. 袋から最初の2枚の食パンを取り出します
> 3. 食パンをカウンターに並べて置く
> 4. バターナイフでパンの一枚にピーナッツバターを塗ります
> 5. などなど

もし、1のやりたいことを伝えるだけで理想のコードがサジェストがされたらもうそれで万事OKです。
実際になかなかそう上手くいかない事が多いので2のやりたい事を1つ1つ短文で区切ります。
それでも上手くいかない場合に3の具体的に何を使って(使わないで)どうするのかを指定します。

例えば`ユーザーアイコンと名前を横並びで表示する`だけのコメントだと全く検討外れのサジェストしかされませんでした。そこで`Rowを使って〜`と加える事により正しくサジェストされるようになりました。更に`中央に配置する`だけでなく`CenterVerticallyを使って〜`と記述する事でレイアウトもサジェストされるようになりました。なので1、2を試して上手く行かない場合はある程度やりたい事に対する知識を必要とするなと感じました。

4は最初に命令をまとめておいた方がコードを類推してやりたい事を先読み(たまに余計な事)してくれるので処理毎に細かくコメントを書くよりも効率が良かったです。下記では頼んでないけどIconとTextの間に余白を作ってくれています。

![スクリーンショット 2023-12-23 19.56.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/ff827b6d-09b6-4c50-ed2a-0b95cb0315c4.png)

5は命令の組み合わせや言葉を少し変えるだけで結果が全く異なってくるのでより良い精度を求めて消したり増やしたり色々試してみましょうの意味です。内容によっては理解力が著しく低下する場合があるのでその場合はその一文を消したり別の言い方にしたりです。たまにおかしくなって壊れちゃったりします。
![スクリーンショット 2023-12-23 17.31.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/7318bb54-2b08-861d-fd24-5ba293d7931e.png)

ダミーのユーザーデータを生成させてみた時に`nameは日本人とする`と記述したら
**name = "日本人"**
がずらっと並んでしまいました。`nameは日本人の名前とする`と修正する事で上手く行きました。空気を読んでめちゃくちゃ気を利かせてくれる時もありますが、機械のように振る舞う時もあるので基本的には細かく指示をした方が無難です。壊れた時は一旦コメント削除してAndroidStudioを再起動しましょう。また、全く同じ内容のコメントでも昨日と今日で全然違う事もあります。サジェストされたコードは一期一会です。


## 成果物
コードを一切書かずにこのような画面が完成しました。

![Screenshot_20231221_175737.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617906/df086e6a-bfd8-d36a-bd83-a7c9b7d449c2.png)

Composeが分かるならどう考えても素直にコード書いた方が早いのは間違いないですが初学者の方には良いペアプロ相手になるかもしれません。今回はコードを一切書かないというコンセプトだったので何回もコメントを調整して理想のサジェストが出るまで耐えましたがざっくりサジェストされたコードをベースに微調整した方が効率がいいです。というか本来はそういう使い方だと思います。

## まとめ
- コメント(プロンプト)の書き方が調教のカギ
- 定型文は大得意
- 別タブで開いているコードを読むので質の良いコードを食わせた方が精度が上がる。
- GitHub Copilotが理解できない概念があると途端に精度が落ちたりサジェストしてくれなくなる。その場合はそこだけは人力か該当のコードが書いてあるタブを閉じた方がいい。別プロジェクトのタブも読んでいるので注意
- サジェストは一期一会。昨日素晴らしいサジェストを返してくれたコメントも今日めちゃくちゃばかになる事がある
- コメントをたくさん書くとコメントもサジェストしてくれるようになるがあまり任せっきりにしてはいけない。適度な命令が必要
- たまに壊れちゃう