AIを使ってコードレビューをやってみた
![AIを使ってコードレビューをやってみた](https://agest.co.jp/wp-content/uploads/2024/06/20240202_column_thumb_01.jpg)
はじめまして、QAエンジニアのK.Kです。
今回は流行りのAIを使ってコードレビューを実施してみましたので、その結果をお伝えします。
AIとは
まず初めにAIについてご説明したいと思います。AIとは、Artificial Intelligence(人工知能)を略した言葉で、コンピュータープログラムによって人間の知能を模倣し、機械が推論、認識、判断などの人間と同じ知的な処理機能を実行するための技術を指しています。
近頃、このAIを利用したサービスが私たちの生活において急速に増加しており、
- 音声を認識して対応するAIアシスタント(Alexa、Siriなど)
- 障害物を避けながら掃除をするお掃除ロボット
- 様々なコンテンツを生成する生成AI(ChatGPT、画像生成AI、音声生成AIなど)
など、AIは私たちの生活や社会構造に大きな変化をもたらしています。
AIテクニカルコードレビュー:蓄積されたレビュー観点とAIを組み合わせることで精度の高いレビューを効率よく実施
AIによるコードレビューの可能性と課題
なぜAIでコードレビューをするのか
コードレビューには、バグの早期発見や潜在的な問題の解決、コーディングスタイルの統一など、多岐にわたる利点がありますが、プロジェクトの進行やデッドラインに追われる中で、コードレビューに十分な時間を確保することは容易ではありません。
そこで今回は、AIによって効率的かつ品質の向上につながる有益なコードレビューが可能か見ていきたいと思います。
AIを利用したコードレビュー
コードレビューの実施環境について
今回はOpenAI社が提供している「OpenAI API」を利用して、2023年の11月に発表された最新モデルの「GPT-4Turbo」を使ったコードレビューを行っていきます。
レビュー対象のコードについて
こちらが今回コードレビューの対象となったコードで、「指定したパスに存在するフォルダ名とフォルダの作成日時の一覧を取得する」、「JSONファイルを保存する」、「JSONファイルを読み取る」といったファイル操作関連の処理を実行するためのユーティリティファイルとなっております。
このコードに対して私がレビューをする場合、「JSONファイルの保存先パスが関数内で”output”変数として定義されているが、JSONを読み取る関数でも使用しているので関数のパラメータもしくはグローバル定数とした方がよいのではないか」、「各関数に関数を説明するためのドキュメントがないため何の処理をしているのかがわかりにくい」などの指摘が考えられます。
import os
from datetime import datetime
import json
def get_directories_with_creation_date(path):
dirs_with_dates = []
for item in os.listdir(path):
item_path = os.path.join(path, item)
if os.path.isdir(item_path):
# Retrieve creation date
creation_timestamp = os.path.getctime(item_path)
creation_date = datetime.fromtimestamp(
creation_timestamp).strftime('%Y-%m-%d %H:%M:%S')
dirs_with_dates.append({"name": item, "date": creation_date})
return dirs_with_dates
def save_file(name, json_data):
if not os.path.exists("output"):
os.mkdir("output")
with open(f"output/{name}.json", "w", encoding="utf-8") as file:
json.dump(json_data, file, ensure_ascii=False, indent=2)
def load_file(name, dir="output"):
if os.path.exists(f"{dir}/{name}.json"):
with open(f"{dir}/{name}.json", "r", encoding="utf-8") as f:
return json.load(f)
else:
return None
AIによるコードレビュー
それではAIにコードレビューを依頼していきます。
AIへの指示は4つの要素として分けることができ、
- 命令:モデルに実行してほしい特定のタスクまたは命令
- 文脈:モデルをより良い対応に導く外部情報または追加コンテキスト
- 入力データ:応答を見つけたい入力または質問
- 出力指示子: 出力のタイプや形式
今回は、AIに対して以下の指示を与えました。
命令:
下記ソースコードについてレビューしてください。レビューで問題点を見つけた場合、問題点と問題点を修正するためのコードを出力してください。問題点を修正することで発生するメリット、デメリットについて出力してください。
文脈:
あなたは優秀な日本人SEです。あなたのタスクはソースコードをレビューし、問題点を全て発見することです。
入力データ:
1:import os 2:from datetime import datetime 3:import json 4: 5: 6:def get_directories_with_creation_date(path): 7: dirs_with_dates = [] 8: …省略
その結果、AIより以下の回答が返ってきました。
![](https://agest.co.jp/wp-content/uploads/2024/07/image-147.png)
このように、AIがコードの内容を読み取り、フォルダ名のパラメータ化による保守性の向上やフォルダ作成時に別のプロセスとの競合でエラーが発生しないようにmakedirs関数を使用したり、エラーハンドリングの追加などの安全性の向上につながる回答をしてくれるため、AIによる効率的なコードレビューが可能であることがわかりました。
より効果的にAIを利用するために
AIによるコードレビューは効率的かつ品質の向上につながるレビューですが、ただ漠然とAIにレビューを依頼しただけでは、思っていた通りの結果が得られないことがあります。例えば、先ほどのAIによる指摘では関数のドキュメントに関する指摘が不足しているように感じました。そこで、よりうまくAIを利用する方法を探ってみました。
ChatGPTのサービスを提供しているOpenAI社によると、AIをうまく利用するための手法の一つに、「AIに対する指示は「ふわふわした」不正確な説明を減らす」というものがあります。
そのため、AIに対して、先ほどの指示に「レビューの観点(関数のドキュメントに関する指摘のみ)」とドキュメントのコーディングスタイルを統一するための「コードのコーディングスタイル(Google style)」を付け加えた指示を与えました。
命令:
下記ソースコードについてレビューしてください。問題点とそれを修正した後のコードも出力してください。問題点を修正することで発生するメリット、デメリットについて出力してください。レビューでは関数のドキュメントに問題があるかのみ見てください。
文脈:
あなたは優秀な日本人のSEです。ソースコードをレビューし、問題点を全て指摘してください。レビュー対象のソースコードは「Google style」に従って書かれています。
入力データ:
1:import os 2:from datetime import datetime 3:import json 4: 5: 6:def get_directories_with_creation_date(path): 7: dirs_with_dates = [] …省略
その結果、AIより以下の回答が返ってきました。
![](https://agest.co.jp/wp-content/uploads/2024/07/image-148.png)
AIへの指示に「レビューの観点(関数のドキュメントに関する指摘のみ)」や「コードのコーディングスタイル(Google style)」などの見てほしい観点に関する文言を付け加えることで、先ほどの指摘とは異なり、各関数のドキュメントに対する指摘が回答として返ってきています。
そこで、観点を指定した場合と指定していない場合で5回ずつレビューをして、指摘された観点の回数を表にまとめました。
指摘 | 観点(ドキュメント)指定なし | 観点(ドキュメント)指定あり |
---|---|---|
エラーハンドリング | 1回指摘 | 指摘なし |
レースコンディション | 3回指摘 | 指摘なし |
責務の分離 | 1回指摘 | 指摘なし |
入力データの検証 | 1回指摘 | 指摘なし |
ハードコーディング | 2回指摘 | 指摘なし |
クロスプラットフォーム | 4回指摘 | 指摘なし |
ドキュメント | 1回指摘 | 5回指摘 |
このように、AIは「観点」を与えることで、観点にそったレビューをしてくれることがわかります。
AIによるコードレビューの問題点について
AIを利用するうえで問題となるのがAIの精度です。コードレビューの結果に指摘の誤りや指摘してほしい箇所に対する指摘がないことがあると、コードレビューをしたにもかかわらず品質の向上が思うようにいきません。
こちらは別のファイルに対して先ほどと同じ指示を与えた結果になります。
命令:
下記ソースコードについてレビューしてください。問題点とそれを修正した後のコードも出力してください。問題点を修正することで発生するメリット、デメリットについて出力してください。レビューでは関数のドキュメントに問題があるかのみ見てください。
文脈:
あなたは優秀な日本人のSEです。ソースコードをレビューし、問題点を全て指摘してください。レビュー対象のソースコードは「Google style」に従って書かれています。
入力データ:
1:from typing import (Any, Dict, List) 2:from langchain.retrievers.document_compressors import ( 3: LLMChainExtractor, EmbeddingsFilter) ... 80:class MyCustomCallbackHandler(BaseCallbackHandler): 81: """Custom CallbackHandler.""" 82: 83: def __init__(self, callback=None): 84: self.prompts = [] 85: self.callback = callback 86: self.pertial_token = "" 87: 88: def on_llm_new_token(self, token: str, **kwargs: Any) -> Any: 89: """Run on new LLM token. Only available when streaming is enabled.""" 90: if self.callback is not None: 91: self.pertial_token += token 92: self.callback(self.pertial_token) 93: 94: def on_llm_start(self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any) -> None: 95: kwargs["run_id"] = str(kwargs["run_id"]) 96: kwargs["parent_run_id"] = str(kwargs["parent_run_id"]) 97: 98: self.prompts.append({ 99: "prompts": prompts, 100: "serialized": serialized, 101: "kwargs": kwargs 102: }) 103: 104: 105:def create_model_from_json(model_name, json_data, callback): …省略 229:def get_file_extension(file_path): 230: return file_path.split(".")[-1] …省略 719:def run(**kwargs): ...省略 781: return result
その結果、AIより以下の回答が返ってきました。
![](https://agest.co.jp/wp-content/uploads/2024/07/image-149.png)
このように、「ドキュメント」に問題があるコードをもれなくレビューしてほしいと依頼したにもかかわらず、「on_llm_new_token」関数や「get_file_extension」関数などへの指摘が欠けていました。
これらの指摘の漏れは、AIに対する指示をさらに詳細化することで減らすことができると考えていますが、AIの結果をそのまま受け取るのではなく、必要な指摘を見極めなければいけないと感じました。
まとめ
近年、AIを利用した革新的なサービスが急速に増加しています。その中で、AIによるコードレビューは開発者たちの手助けとなり、品質の向上につながるものだと感じています。しかし、AIは発展途上の技術であり、AIの判断がすべて正しいとは限りません。AIによってできること、できないことを理解し、適切な場面でAIを利用することがよりよいコードレビューにつながるのではないかと考えます。