はじめに
皆さんは Github Copilot Chat というツールをご存知でしょうか?
Github Copilot Chat は、プログラミングと関係する質問に対して、対話ベースで回答をしてくれるツールです。
workspaceのコードを読み取り回答してくれるため、精度が高く実務でも非常に役立ちます。
今回は、この Github Copilot Chat を活用することで、開発スピードが 1.5 倍速になった活用術をご紹介いたします。
活用方法
さて、活用方法について実際に投げているプロンプトや活用術をご紹介いたします。
今回は、Vscodeで拡張機能のGithub Copilot chatをインストールして使用しています。
基本的な使い方
ここでは、GithubやVscodeのドキュメントにもある基本的な使い方をご紹介いたします。
プロンプトに「@」と入力して、エージェント機能が使用することができます。
@workspace
ワークスペース内のコードに関するコンテキストがあります。 Copilot でプロジェクトの構造、コードのさまざまな部分の相互作用、またはプロジェクト内の設計パターンを考慮する場合に @workspace を使用します。
github Copilot のドキュメントより
具体例でいくと「@workspace
データベース接続はどこでどのように構成されていますか?」
といったプロンプトを投げることで、データベース接続のコードを教えてくれます。
実際に、適当なRailsプロジェクトで試すとこの通り。
う〜ん、すごく便利、新しくプロジェクトにアサインされた時や
初めて使うフレームワークで活躍してくれそうですね。
@vscode
Visual Studio Code コマンドと機能に関するコンテキストがあります。 Visual Studio Code に関するヘルプが必要な場合に @vscode を使用します。
github Copilot のドキュメントより
Vscodeエディタ自体のコマンドや機能を教ええてくれるコンテキストになります。
@terminal
Visual Studio Codeターミナル シェルとその内容に関するコンテキストを持ちます。
ターミナル コマンドの作成またはデバッグに関するヘルプが必要な場合に @terminal を使用します。
github Copilot のドキュメントより
「@terminal
直前のコミットを取り消したい」といったプロンプトを投げることで、
コミット名を変更するコマンドを教えてくれます。
以下は、@kim_t0814 がよく使用するプロンプトです。
そのライブラリを全く知らない時
今回はすでに知っていますが、flag_shih_tzu
という1 つの Gem について知らなかったとしましょう。
以下のようなプロンプトを投げると、概要と一次ソースを貰うことができます
@workspace
XXXのことが分かりません。
XXXの概要とそのリファレンスを教えてください。
リファレンス(特に英語のもの)を見ても何を言っているのかさっぱりな時ってありますよね。
そういう場合、概要を知った上でリファレンスを見ることで、無知の状態でも頭にスッと入って理解することができます。
テストコードを書いて欲しい!
さて、Copilot Chat はテストコードも workspace のコードを読み取って作成してくれます。
今回は、去年作成した GraphQL API を叩いて電気料金を取得するアプリケーションの
メインロジックに対して、テストコードを作成してもらいます!(現在は、メインロジックはサービス層に移植済)
実際のコードは以下の通りになります。
class OctopusEnergyBill
GetBillDayQUERY = OctopusClient::Client.parse <<~'GRAPHQL'
query(
$accountNumber: String!
$fromDatetime: DateTime
$toDatetime: DateTime
) {
account(accountNumber: $accountNumber) {
properties {
electricitySupplyPoints {
agreements {
validFrom
}
halfHourlyReadings(
fromDatetime: $fromDatetime
toDatetime: $toDatetime
) {
startAt
endAt
value
costEstimate
consumptionStep
consumptionRateBand
}
}
}
}
}
GRAPHQL
GetBillMonthQUERY = OctopusClient::Client.parse <<~'GRAPHQL'
query ($accountNumber: String!) {
account(accountNumber: $accountNumber) {
properties {
electricitySupplyPoints {
agreements {
validFrom
}
intervalReadings {
endAt
startAt
value
costEstimate
}
}
}
}
}
GRAPHQL
def notify_yesterday
result = OctopusClient::Client.query(GetBillDayQUERY, variables: {
accountNumber: ENV['OCTOPUS_ACCOUNT_NUMBER'],
fromDatetime: Date.yesterday.beginning_of_day.iso8601,
toDatetime: Date.yesterday.end_of_day.iso8601
})
electricity_supply_points = get_electricity_supply_points(result)
half_hourly_readings = electricity_supply_points.first["halfHourlyReadings"]
@kwh = half_hourly_readings.pluck("value").map(&:to_f).sum.round(2)
@cost = half_hourly_readings.pluck("costEstimate").map(&:to_f).sum.round(1)
text = "#{Date.yesterday.strftime('%Y年%m月%d日')}は#{@kwh}kWh消費して#{@cost}円かかったよ"
send_message(text)
end
def notify_last_month
result = OctopusClient::Client.query(GetBillMonthQUERY, variables: {
accountNumber: ENV['OCTOPUS_ACCOUNT_NUMBER'],
})
electricity_supply_points = get_electricity_supply_points(result)
monthly_readings = electricity_supply_points.first["intervalReadings"]
@kwh = monthly_readings.last["value"].to_f.round(1)
@cost = monthly_readings.last["costEstimate"].to_f.round(0)
text = "#{Date.today.last_month.strftime('%Y年%m月')}は#{@kwh}kWh消費して#{@cost}円かかったよ"
send_message(text)
end
private
def get_electricity_supply_points(result_data)
properties = result_data.original_hash.dig("data", "account", "properties")
properties.first["electricitySupplyPoints"]
end
def send_message(text)
message = {
type: 'text',
text: text
}
LineBotClient.new.client.broadcast(message)
end
end
さて、上記のファイル内のコード上で右クリックして Copilot>ファイルをチャット内
に追加をクリックした上で、
以下のようなプロンプトを投げると、単体テストコードを作成してくれます。
@workspace /tests
実際に生成されたコードはこちら
describe OctopusEnergyBill do
describe '#notify_yesterday' do
let(:mock_electricity_supply_points) { [{"halfHourlyReadings" => [{"value" => "1.0", "costEstimate" => "2.0"}]}] }
before do
allow(OctopusClient::Client).to receive(:query).and_return(mock_result)
allow(octopus_energy_bill).to receive(:get_electricity_supply_points).and_return(mock_electricity_supply_points)
allow(octopus_energy_bill).to receive(:send_message)
end
it 'queries the OctopusClient with correct parameters' do
octopus_energy_bill.notify_yesterday
expect(OctopusClient::Client).to have_received(:query).with(
OctopusEnergyBill::GetBillDayQUERY,
variables: {
accountNumber: ENV['OCTOPUS_ACCOUNT_NUMBER'],
fromDatetime: Date.yesterday.beginning_of_day.iso8601,
toDatetime: Date.yesterday.end_of_day.iso8601
}
)
end
it 'sends a message with the correct text' do
octopus_energy_bill.notify_yesterday
expect(octopus_energy_bill).to have_received(:send_message).with("#{Date.yesterday.strftime('%Y年%m月%d日')}は1.0kWh消費して2.0円かかったよ")
end
end
describe '#notify_last_month' do
let(:mock_electricity_supply_points) { [{"intervalReadings" => [{"value" => "139.0", "costEstimate" => "3500.8"}]}] }
before do
allow(OctopusClient::Client).to receive(:query).and_return(mock_result)
allow(octopus_energy_bill).to receive(:get_electricity_supply_points).and_return(mock_electricity_supply_points)
allow(octopus_energy_bill).to receive(:send_message)
end
it 'queries the OctopusClient with correct parameters' do
octopus_energy_bill.notify_last_month
expect(OctopusClient::Client).to have_received(:query).with(
OctopusEnergyBill::GetBillMonthQUERY,
variables: { accountNumber: ENV['OCTOPUS_ACCOUNT_NUMBER'] }
)
end
it 'sends a message with the correct text' do
octopus_energy_bill.notify_last_month
expect(octopus_energy_bill).to have_received(:send_message).with("#{Date.today.last_month.strftime('%Y年%m月')}は139.0kWh消費して3501円かかったよ")
end
end
end
mock を含めて、だいぶそれっぽいテストコードを生成してくれました。
後は、実際にコードが正常に動くか検証したり、自然言語でテストケースを追加して壁打ちをしていくことで
より良いテストコードを作成することができます。
レビュー依頼を出す前に
これは、実際に実務中に自分はセルフレビューをするのですが、そのセルフレビューの一環として
Copilot Chat を使用しています。
流れとしては、
実装
↓
セルフレビュー
↓
Copilot Chatにレビューさせる
という順で行うことによって、自分の目で見逃していた部分を補完することができます。
セルフレビューと Copilot Chat のダブルチェックによって、よりよい状態で
コードをレビュー依頼することができます。
これで、レビューの打ち返しを減らし、approve をもらうまでの時間を短縮することもできますし
レビュワーの負担も減らすことができます(まあ、Coplot Chat を使用せず完璧なコードを書ければそれがベストですが)。
さて、今回は分かりやすいように、分かりやすさを重視し、あえて悪いコードをレビューしてもらいます。
以下はミノ駆動本の 14 章のコードの悪いパターンを参照に ruby に書き換えたものです。
class PurchasePointPayment
attr_reader :customer_id, :comic_id, :purchase_point, :local_date_time
def initialize(customer,comic)
if customer.enabled?
@customer_id = customer.id
if comic.enabled?
@comic_id = comic.id
if comic.current_purchase_point.amount <= customer_possession_point.amount
@consumption_point = comic.current_purchase_point
@payment_date_time = Time.current
else
fail '所持ポイントが不足しています。'
end
else
fail ArgumentError, '現在取り扱いのできないコミックです。'
end
else
fail ArgumentError, '有効な購入者ではありません。'
end
end
end
筆者は以下のようなプロンプトを投げます。
@workspace
あなたは、レビュワーです。
下記のコードレビューをする際に修正するとしたらどこを修正しますか?
また問題ない場合はLGTMを出してください。
以下、先ほどの悪いコードを貼り付ける or 選択して追加しておく
実際に生成されたコードとコメントはこちら
class PurchasePointPayment
attr_reader :customer_id, :comic_id, :purchase_point, :payment_date_time
ERROR_MESSAGES = {
invalid_customer: '有効な購入者ではありません。',
invalid_comic: '現在取り扱いのできないコミックです。',
insufficient_points: '所持ポイントが不足しています。'
}.freeze
def initialize(customer, comic)
validate_customer(customer)
validate_comic(comic)
validate_points(customer, comic)
@customer_id = customer.id
@comic_id = comic.id
@purchase_point = comic.current_purchase_point.amount
@payment_date_time = Time.current
end
private
def validate_customer(customer)
fail ArgumentError, ERROR_MESSAGES[:invalid_customer] unless customer.enabled?
end
def validate_comic(comic)
fail ArgumentError, ERROR_MESSAGES[:invalid_comic] unless comic.enabled?
end
def validate_points(customer, comic)
fail ERROR_MESSAGES[:insufficient_points] if comic.current_purchase_point.amount > customer.possession_point.amount
end
end
素晴らしい...😇
そのプロジェクトごとにコーディング規約はあるので、それに合わせる必要はありますが
理由も端的に説明してくれるので、コードを理解した上で修正することができます。
ただ、Copilot Chat が生成したコードが全て正しいとは限らないので、
そこはオーナーシップを持ってレビュー依頼をしてください(突っ込まれそうなところは回答できるように)。
資格の勉強時に
そのライブラリを全く知らない時と、似たような使用方法になりますが
Ruby Goldの勉強をしていた際にリファレンスだけ見た際に理解できなかった部分を
Copilot Chatと壁打ちをすることで理解を深めることができました。
ただし、プログラミングと離れている内容の場合は
Copilotの範囲外になります。
ただし、以下のようなプロンプトのようにゴリ押しをしてプログラミングや
ソフトウェア開発と結びつけることで回答してくれはします...
(かなりネタ的な使い方になりますが)
Copilotさん!?
このプロンプト、ドメインを考える時にもしかしたら役に立つことがあるかも知れませんね...!
終わりに
昨今、生成AIがどんどん流行りつつありますが、それを有効活用することが
手を動かすエンジニアにおいても重要なのかもしれませんね。
プロンプトを真似して投げてみることは、すぐ出来ると思うので
是非お試しください。
あ、実務で使用する場合はGithub Copilotにコードを学習させないことをお忘れなく!
参照