VSCodeにChatGPTの拡張機能を入れてコードレビューやバグを発見してもらう

ChatGPTとは?

OpenAIが開発するGPT-3という言語モデルをベースとしたチャットアプリです。

こちらの質問に対して、AIが色々な質問に答えてくれて、一般的な内容だけではなく、コードレビューやバグなども発見してくれるめっちゃ凄いやつです。

細かい内容は以下の記事がとても参考となります。

AIがコミットメッセージ自動生成!神ツール『auto-commit』『commit-autosuggestions』の紹介|逆瀬川|note

https://note.com

ChatGPT使い方総まとめ – Qiita

https://qiita.com

VSCodeと連携して、ブラウザを開かなくてもChatGPTを使用できるようにする

通常はブラウザを開いて使用するのですが、コーディング中にサクッとレビューしてもらったり、バグを見つけてもらえるような拡張機能があったので、そちらの設定方法について記述してみます。

  • 今回インストールする拡張機能

GitHub – gencay/vscode-chatgpt: A Visual Studio Code extension to supp…

https://github.com

使用までの手順

環境

  • PC: MacBook Pro (Apple M2)
  • OS: macOS Ventura 13.1
  • VSCode: v1.74.3

OpenAIの価格について

※ 2022/1/17追記(webgotoさん有難うございます)
登録時に$18分のクレジットが付与されまますが、有効期限は3ヶ月間です。

それ以降は有料となりますが、クレジットカードを登録しなくても$18分は付与されるので、勝手に課金されるなどはなさそうです。

登録後の状況は Personal > Manage account > Free trial usage から確認できます。

価格に関してはこちらに記載があります。

Screenshot 2023-01-17 at 10.17.03.png

OpenAIへ登録(無料枠あり)

  • https://chat.openai.com/auth/login へアクセス
  • 「Login」をクリック
  • 今回はGoogleアカウントを使ってログインするため「Continue with Google」をクリックしてログイン

  • アカウント登録が終わったら次の手順です

拡張機能のインストールから設定

  • VSCodeの拡張から「chatgpt」と入力
  • install をクリック

2. 拡張機能の設定

  • 歯車マークをクリック
  • 「Extension Settings」をクリック
  • Settings が開くので「Chromiunm Path」と「Api Key」を設定
  • Chromiunm Path の調べ方(Mac)
    • Finderを開く > Applications > 「Google Chrome.app」をターミナルへドラッグ&ドロップする
    • ターミナルに表示された Path を VSCode の Settings の「Chromiunm Path」へ貼り付ける
  • Api Key の設定
    • 入力欄の上にある「Documentation」をクリック
    • ブラウザが開くので、「Create new secret key」をクリック
    • API key が作成されたらコピーして、VSCode の Settings の「Api Kye」へ貼り付ける

  • Method 項目の変更
    • 「GPT3 OpenAI API Key」 に変更する
  • 日本語で回答してもらうようにする(2022/1/16追記)
    • 設定に日本語の質問文を入れると日本語で返信してくれるそうです(akiraakさん情報提供有難うございます)

VSCodeのChatGPTプラグインを日本語化する – Qiita

https://qiita.com

3. 使用できるようになっているか確認する

  • 適当にコードを範囲選択して右クリックをした際に、以下のような項目が出ていればOKです

実際に使ってみる

バグを発見してもらう

今回、意図的にバグを仕込んだFizz Buzzコードを作ってみました。

このコードを選択して右クリックから「ChatGPT: Find bugs」をクリックしてみます。

Screenshot 2023-01-15 at 13.07.47.png

すると以下のレビューが帰ってきます。

Screenshot 2023-01-15 at 13.09.48.png

翻訳文

コードはほとんど問題ないように見えますが、対処できる問題がいくつかあります。

条件分岐の順序がずれています。最初に if(i % 15 === 0) 、
次に if(i % 3 === 0)、最後に if(i % 5 === 0) とすべきだろう、
Fizz Buzz に関してはこの順番が優先されるからだ。

let i = 1 は、let i = 0 に変更すべきです。
これは、Fizz Buzz の開始番号だからです。

console.logは、1ループに1回しか実行されないので、
if文の外に置くべきです。if文の後に、console.log(output)のようにします。
outputは、値に応じてFizz、Buzz、Fizz Buzzを保持する変数です。

if-else文の最後には、3,5,15で割り切れない数を扱うための
デフォルトケースを追加することができます。

全体として、このコードは、条件文の順序を変更し、i = 0 を初期化し、
console.log 文を if-else の外に置き、
3, 5, 15 で割り切れない値に対処するためのデフォルトケースを
追加することで改善することができます。

コードを最適化(リファクタ)してもらう

続いては、「ChatGPT: Add Optimize」をクリックします。

翻訳文

このコードは以下のように最適化することができます。

for(let i = 0; i < 100; i++) {
  output = '' 
  if(i % 3 === 0)  output += 'Fizz'
  if(i % 5 === 0)  output += 'Buzz'
  console.log(output || i)
}

これで、if条件の数が4つから2つに減り、より効率的になりました。

バグも直した状態でリファクタされました。

テストコードを教えてもらう

さらに、「ChatGPT: Add tests」もクリックしてみます。

Screenshot 2023-01-15 at 13.14.12.png

翻訳文

テストは与えられた入力に対して期待される出力をチェックすべきです。
ですから、問題のコードでは、入力として様々な整数をチェックし、
正しい出力(Fizz, Buzz, Fizz Buzz, あるいは数字そのもの)が
記録されるかどうかを確認すべきです。

良い出発点は次のようなものでしょう。

test('Test FizzBuzz func.', () => {
  // Check values divisible by 3 and 5
  expect(fizzBuzz(15)).toBe('Fizz Buzz');
  // Check values divisible by 3
  expect(fizzBuzz(6)).toBe('Fizz');
  // Check values divisible by 5
  expect(fizzBuzz(10)).toBe('Buzz');
  // Check values not divisible by 3 and 5
  expect(fizzBuzz(7)).toBe(7);
});

コードの解説をしてもらう

続いては、提案されたリファクタコードの状態で「ChatGPT: Explain」をクリックしてみます。

Screenshot 2023-01-15 at 13.23.34.png

翻訳文

このコードは、古典的なFizzBuzzプログラムをより効率的にしたものである。
0 から 99 までをループし、それぞれの数をモジュロ演算子 % でチェックし、
必要なら "Fizz" と "Buzz" の値を一つの文字列(出力)に連結し、
3で割り切れる数(i % 3 === 0 で表示)に対しては "Fizz" 、
5で割り切れる数に対しては "Buzz" 、出力文字列に "Fizz" と "Buzz" が
両方含まれていれば "Fizz Buzz" 、3 と 5 で割り切れない数に対しては
その数値自体をプリントアウトして出力しています

その他、ChatGPTの便利な使い方

ここからは、https://chat.openai.com/chat で入力する必要がありますが、いくつか筆者が便利だなと思った内容を記述いたします。

メソッド名を提案してもらう

メソッド名の命名で英語に迷った時に、以下のように質問すると方補をあげてくれます。

「<処理内容>という処理を行うメソッド名の候補をいくつか教えてください」

例:ファイルに出力という処理を行うメソッド名の候補をいくつか教えてください

上記のように入力すると以下のように回答してくれます。

Screenshot 2023-01-15 at 13.42.26.png

それっぽい英語が返ってきます。

変数名を提案してもらう

同じ要領で変数名も提案してくれます。

「<>という役割の変数名の候補をいくつか教えてください」

例:同意済みという役割の変数名の候補をいくつか教えてください

すると以下のように返ってきます。

Screenshot 2023-01-15 at 13.45.14.png

注意書き

これらは、「承認済み」または「合意済み」の状態を伝える
変数名のいくつかの例です。
あなたが選ぶ正確な名前は、あなたのプログラムの文脈と、
あなたが従っている命名規則に依存します。

分かりやすい命名になるように、リーダブルコードを読んだり、codicを参考にしてみたり、記事を参考に(こちらの記事等)させてもらっていましたが、迷った時の候補としてこちらも良さそうに思いました。
(命名はとても大事ですね)

理解の曖昧な用語を要約してもらう

「<用語>を要約して」と入力することで、曖昧な用語を要約してくれることもできます。

例:TDDを要約して

Screenshot 2023-01-15 at 14.10.09.png

例:DDDを要約して

Screenshot 2023-01-15 at 14.10.51.png

おもしろいので、もう1つ。

例:Clean Architectureを要約して

Screenshot 2023-01-15 at 14.12.27.png

うーん、、

書籍を読む前にざっくりとした概念だけ頭に置いておくにはいいかもしれませんが、
このあたりはしっかりと書籍やブラウザ検索などで知見を深めて、
ちゃんと理解する必要がありそうです。

用語を詳しく解説してもらう

試しに、「Clean Architectureを詳しく解説して」もしくは「Clean Architectureについて詳しく教えて」と入力。

Screenshot 2023-01-15 at 14.16.02.png

…途中で止まってしまいました。

※2022/1/16追記
途中で止まってしまった場合は、「続き」と入力すると途切れた箇所から続けてくれました(kazuphさん情報提供有難うございます)

サンプルコードを教えてもらう

「<>の実例コードを教えて」もしくは「<>のサンプルコードを教えて」と入力すると教えてくれます

途中で止まってしまうので、「続き」と入力します。

例:「typescriptでのdddの実例コード」を教えて

typescriptでのdddの実例コードを教えての回答内容

DDD (Domain-Driven Design)はアプリケーションの設計方法論であり、特定のドメインに特化した設計を行うことを目的としています。
TypeScriptでDDDを実装するためのコード例を示すことは難しいため、一般的なDDDのアプローチを説明します。

DDDには、以下のような3つのレイヤーがあります。

ドメイン層:ドメインモデルとドメインロジックが含まれます。これは、アプリケーションの主要なビジネスロジックが含まれています。
アプリケーション層:アプリケーションサービスやインタラクションオブジェクトが含まれます。これらは、ドメイン層のオブジェクトを使用して、アプリケーションの使用者に対してサービスを提供します。
インフラストラクチャ層:データベースアクセスや通信などの低レベルのタスクが含まれます。
TypeScriptでDDDを実装する場合、ドメイン層はクラスやインターフェイスを使用してモデル化し、アプリケーション層はサービスクラスを使用し、インフラストラクチャ層は repository パターンを使用します。

以下に例を示します

class Order {
    // ドメインモデルのプロパティ
    private items: OrderItem[] = [];
    private customer: Customer;

    // ドメインロジック
    addItem(item: OrderItem) {
        this.items.push(item);
    }
    calculateTotal(): number {
        return this.items.reduce((total, item) => total + item.price, 0);
    }
}

class OrderService {
    // アプリケーションサービス
    private orderRepository: OrderRepository;
    constructor(orderRepository: OrderRepository) {
        this.orderRepository = orderRepository;
    }
    createOrder(customerId: string, items: OrderItem[]) {
        const customer = this.customerRepository.getById(customerId);
        const order = new Order(customer);
        items.forEach(item => order.addItem(item));
        this.orderRepository.save(order);
    }
}

class OrderRepository {
    // インフラストラクチャ層
    private orders: Order[] = [];
    save(order: Order) {
    this.orders.push(order);
}

    getById(id: string): Order {
        // アプリケーションの中で使用したいロジックを実装
        return this.orders.find(order => order.id === id);
    }
}

このように、TypeScriptでDDDを実装するためには、
それぞれのレイヤーについて、クラスやインターフェイスを使用し、
適切なロジックを実装していくことが重要です。

これは一例であり、実際のアプリケーションでは、
もう少し複雑で複雑なロジックが必要になることがあります。

「typescriptでのdddのサンプルコードを教えて」の回答

DDD (Domain-Driven Design)をTypeScriptで実装する際の
サンプルコードを示します。

以下に、簡単なe-commerceアプリケーションの例を示します。

ドメイン層では、ProductとOrderのクラスを定義します。

class Product {
    constructor(
        private id: string,
        private name: string,
        private price: number
    ) {}
    getId() { return this.id }
    getName() { return this.name }
    getPrice() { return this.price }
}

class Order {
    private id: string;
    private products: Product[];
    private createdAt: Date;
    constructor(id: string, products: Product[]) {
        this.id = id;
        this.products = products;
        this.createdAt = new Date();
    }
    getId() { return this.id }
    getProducts() { return this.products }
    getCreatedAt() { return this.createdAt }
    getTotalPrice(): number {
        return this.products.reduce((total, product) => total + product.getPrice(), 0);
    }
}
アプリケーション層では、OrderServiceクラスを定義します。このクラスは、Orderを作成し、OrderRepositoryに保存するためのロジックを持っています。

class OrderService {
    private orderRepository: OrderRepository;
    constructor(orderRepository: OrderRepository) {
        this.orderRepository = orderRepository;
    }
    createOrder(products: Product[]): Order {
        const id = generateId();
        const order = new Order(id, products);
        this.orderRepository.save(order);
        return order;
    }
}
インフラストラクチャ層では、OrderRepositoryクラスを定義します。
このクラスは、Orderを保存し、取得するためのロジックを持っています。

class OrderRepository {
    private orders: Order[] = [];
    save(order: Order) {
        this.orders.push(order);
    }
    getById(id: string): Order {
        return this.orders.find(order => order.getId() === id);
    }
}
これは、TypeScriptでDDDを実装する一例です。実際のアプリケーションでは、もっと複雑なロジックや構造が必要になることがあります。

ここで示したコードは単純な例であり、実際のアプリケーションで使用するには
アプリケーションを構築するために必要なテクニックを適用することが重要です。

実際に使用する際には、アプリケーションに必要なロジックを適切に実装し、
柔軟な構造にすることが大切です。
また、DDDは複雑なアプリケーションを設計するためのアプローチであり、
実際には複数のパターンやテクニックを組み合わせて使用することが必要になります。

質問内容のチョイスが良くなかったですが、
やはりちゃんと書籍等で学んだ方が良さそうです。

データベース設計を提案してもらう

なんと、「<app機能>のデータベース設計して」と入力すれば提案があります。

例:チャット機能のデータベース設計して

Screenshot 2023-01-15 at 13.57.56.png

実務には到底耐えられないですが、回答があること自体が凄いですね。

用途の違いを教えてもらう

それぞれの用途の違いについて教えてもらいます。

<A> と <B> の違いを教えて」

例:vue.jsのwatchとwatchEffectの違いを教えて

Screenshot 2023-01-15 at 21.31.33.png

英単語の違いを教えてもらう

命名時などに迷ってしまうような、使い分けが明確でない英単語を教えてもらいます。

例:hopeとwishの違いを教えて

Screenshot 2023-01-15 at 21.37.41.png

感想

所々微妙な箇所もあり、全て鵜呑みにしてしまうのはかなり危険なので、
やはり自分で詳細を把握して正しく判断する力は必要ですが、使い方は無限に広がりそうでめちゃくちゃ凄いなと思いました。