はじめに
AI Shift バックエンドエンジニアの木村です。
本記事はAIShift Advent Calendar 2023の 17日目の記事となります。
今回はAIを利用した開発支援ツールであるGitHub Copilot ChatとCursorを使ってみた感想を紹介したいと思います。
GitHub Copilotの主な機能はコードの入力補完ですが、Copilot Chatはコードの質問、改善、生成などが自然言語での対話で開発を支援してくれるものになります。
CursorはAI-first Code Editorを謳った製品でありVisual Studio CodeをForkして作られています。
セットアップや基本機能の説明は省略して具体的な使用例を紹介していきたいと思います。
ベースとするサンプルコード
JavaのSpringプロジェクトから拝借したDBアクセスサンプルコードを例にして今回紹介するツールに開発のお手伝いをしてもらいたいと思います。
gs-relational-data-access
@SpringBootApplication
public class RelationalDataAccessApplication implements CommandLineRunner {
private static final Logger log = LoggerFactory.getLogger(RelationalDataAccessApplication.class);
public static void main(String args[]) {
SpringApplication.run(RelationalDataAccessApplication.class, args);
}
@Autowired
JdbcTemplate jdbcTemplate;
@Override
public void run(String... strings) throws Exception {
log.info("Creating tables");
jdbcTemplate.execute("DROP TABLE customers IF EXISTS");
jdbcTemplate.execute("CREATE TABLE customers(" +
"id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))");
// Split up the array of whole names into an array of first/last names
List<Object[]> splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long").stream()
.map(name -> name.split(" "))
.collect(Collectors.toList());
// Use a Java 8 stream to print out each tuple of the list
splitUpNames.forEach(name -> log.info(String.format("Inserting customer record for %s %s", name[0], name[1])));
// Uses JdbcTemplate's batchUpdate operation to bulk load data
jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames);
log.info("Querying for customer records where first_name = 'Josh':");
jdbcTemplate.query(
"SELECT id, first_name, last_name FROM customers WHERE first_name = ?",
(rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name")), "Josh")
.forEach(customer -> log.info(customer.toString()));
}
}
AIにコードを説明してもらう
GitHub Copilot Chat
過不足なくいい説明だと思います。
/explainというコマンドで現在開いているコードの説明もできますが英語で返ってくるので今回は日本語で指示しています。
部分的に色を変えていたりレイアウトが整っていて読みやすいです。
Cursor
こちらの説明もよさそうです。
Cursorではさらに『with codebase』というボタンがあり、こちらはコード全体に対して質問や指示ができます。
例えばcodebaseに対してコードの説明を求めると全体の概要と主要なファイルの説明をしてくれました。
(長いので一部のみ)
これとは別に実業務のレポジトリでコードの説明をしてもったところ、main関数やswaggerのドキュメントを見つけて概要を説明してくれました。さすがに現状深い説明は難しそうですが、長いトークンを扱えるLLMも登場してきているので「あそこの仕様どうだったっけ?」という質問や「XXについての状態遷移を図にまとめて」というよう指示に応えることも1,2年でできるようになるかもしれません。
AIにリファクタリングしてもらう
GitHub Copilot Chat
@Override
public void run(String... strings) throws Exception {
log.info("Creating tables");
createTable();
List<Object[]> splitUpNames = splitNames(Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long"));
logNames(splitUpNames);
insertNames(splitUpNames);
queryNames("Josh");
}
テーブル作成、ログ出力、データ保存、データ参照とメソッドが分割されていてよさそうです。長いので呼び出しもとのみ抜粋。
Cursor
@Override
public void run(String... strings) throws Exception {
createTable();
insertData();
queryData();
}
こちらはデータの生成もメソッド内に隠蔽されるパターンでリファクタリングの提案がされました。
AIにClean Architectureで書き直してもらう
GitHub Copilot Chat
正直期待していませんでしたが、ちゃんとinterfaceと実装を分けたコードを提示してくれました。Usecase -> repositoryの依存性注入はSpringDIは使わずにコンストラクタインジェクションしていました。
public interface CustomerUseCase {
void createCustomers(List names);
List getCustomersByName(String name);
}
public class CustomerUseCaseImpl implements CustomerUseCase {
private CustomerRepository customerRepository;
public CustomerUseCaseImpl(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
@Override
public void createCustomers(List names) {
List customers = names.stream()
.map(name -> name.split(" "))
.map(nameParts -> new Customer(null, nameParts[0], nameParts[1]))
.collect(Collectors.toList());
customerRepository.saveAll(customers);
}
@Override
public List getCustomersByName(String name) {
return customerRepository.findByName(name);
}
}
public interface CustomerRepository {
void saveAll(List customers);
List findByName(String name);
}
長いので詳細は割愛します。
Cursor
public interface CustomerRepository {
void createTable();
void insert(List<Customer> customers);
List<Customer> findByFirstName(String firstName);
}
こちらではRepositoryがinterfaceと実装を分けられていてController的なコードから直接呼び出すようなコードでした。DBアクセスしかしていないと見切っているのかはわかりませんが、これくらいの長さのコードならちょうどいいかもしれません。
感想
Clean Architectureで書くとどうしてもコード量が増えるのでAIにリファクタリングしてもらうのはありだと思いました。むしろ単純なデータのCRUDはAIに任せてビジネスロジックのみ人+AIで見るというようになるかもしれません。
さらに進んでAIによるコード生成や修正が安定してきたらそれを前提にしたアーキテクチャが流行るかもしれません。
AIにビルドエラーを修正してもらう
変数をタイポしてみました。
GitHub Copilot Chat
例が簡単ですが正しい回答ですね。連鎖的にエラーになってる部分についても説明されています。また、/fixコマンドでもエラーの解決方法を教えてくれますが、英語で返ってくるので日本語で直接聞きました。
Cursor
エラー箇所にカーソルを当てるとボタンが出現してCHAT欄に自動でプロンプトを書いてくれるので使い勝手がいいです。回答内容も正しいです。
また、アプリケーションの実行時などCursor内のターミナルでエラーが出た場合にも調査用のボタンが出現して押すとエラーメッセージと実装から原因調査をしてくれるのでかなり便利そうです。試してみたところ調査の精度もけっこう高い気がします。
おまけ : 要件から新規プロジェクトの作成
Cursorのでは『何を作りたいか』を伝えると1からコードを生成してくれる機能があります。
日本語メニューではわかりにくいですが『ファイルを開く...』から始めます。
何を作ってほしいか自然言語で入力します。日本語で大丈夫でした。
(日本語がおかしかったですが汲み取ってくれました...)
タスクが分割されてコードが順次生成されていきます。
以下の数のファイルが生成されました。
go: 6, html: 1, js: 1, css: 1
残念ながらそのままでは動かなかったのですがHttpのハンドラ、DBアクセス(MySQL前提で書かれていました)などに別れていて少し直せば動きそうでした。
今後...
まだ試せていませんが、先日JetBrainsのAI Assistantが正式リリースされました。
またGitHubは11月にGit Hub Copilotの将来像となるCopilot Workspaceを発表していて(2024リリース)、こちらは自然言語のIssueからAIが仕様、コードを書くというものです
数年のうちにアイデアや課題感があれば誰でもアプリケーションが作れる時代が確実にくるでしょう。
『エンジニアの仕事がなくなる』と捉えることもできますし、『生産効率が上がる』とも『アイデア次第でできることが広がる』と捉えることもできます。
できる限り、変化にあらがうのではなく変化を活用して仕事に活かしたいものです。
おわりに
AI Shiftではエンジニアの採用に力を入れています!
少しでも興味を持っていただけましたら、カジュアル面談でお話しませんか?
(オンライン・19時以降の面談も可能です!)
【面談フォームはこちら】
https://hrmos.co/pages/cyberagent-group/jobs/1826557091831955459
明日は開発チームのsZma5aよりSIPについてご紹介いたします。