Pythonのコードレビューでチェックするポイントは?

公開日: 2025-10-24

「コードレビューって何を見ればいいの?」 「指摘されたとき、どう受け答えすればいいの?」

そんな疑問を持ったことはありませんか。私もエンジニアになりたての頃は、レビューコメントを見るたびに迷走していました。でも安心してください。

コードレビューは“学びの場”であり、正しいやり方を知れば怖くありません。この記事では、Pythonに特化して、初心者にも分かりやすく、丁寧にコードレビューの意味・目的・具体的な改善例・実際のコメント例・ツール導入方法まで解説します。私はエンジニア歴10年で、多数のレビューを行い、受けてきた経験から実践的なコツもお伝えします。

コードレビューってなぜ必要?

まず、少し考えてみましょう。あなたが書いたコード、1年後・2年後に自分で読み返せますか? そしてもう一つ想像してみてください。あなたが急に休んでしまい、別のメンバーがその機能を直さなければならない――そのとき、コードやPR(プルリク)の内容は他の人にとって明快でしょうか?

これらは単なる想像ではなく、実務で頻繁に起こるナレッジ継承の課題です。コードレビューはその課題に対する最も実践的で効果の高い取り組みの一つです。

以下では「なぜコードレビューが必要なのか」を、具体的な理由と実例、そしてレビューを怠ったときに起きるリスクと、その対処法まで丁寧に説明します。

1. バグを早く、安く見つけられるから

一番分かりやすいメリットはこれです。バグは発生から発見までの時間が長くなるほど修正コストが跳ね上がります。現場経験から言うと、仕様レビュー・コードレビュー・テストの順に問題を捕まえられれば、修正にかかる工数は劇的に減ります。

2. 可読性・設計の均質化が進むから

レビューを通すことで「チーム全体の書き方」が揃います。関数の命名、エラーハンドリングの方針、ログの出し方、例外の扱い方――こうした細かな差は積み重なると“保守性”に大きな差を生みます。 もしレビューがないまま各メンバーがバラバラの書き方をしていると、後からそのモジュールを触る人が毎回学習コストを払わなければならず、生産性が下がります。

レビューはこのばらつきを抑え、コードを誰でも読める状態にしてくれます。

3. 知識の共有とスキルアップの場になるから

レビューは単なるミス探しではありません。良いレビューは「なぜその設計になっているのか」「このアルゴリズムの思想は何か」を共有する場でもあります。

新人が先輩のレビューを読むことで設計思想やパターンを学び、逆にベテランも新人の斬新な実装から刺激を受けることがあります。私の経験では、ある若手が提案した小さなリファクタでパフォーマンスが改善し、チーム全員がそのテクニックを使うようになったことがありました。レビューは知識の伝播を加速します。

コードレビューでチェックすべき基本ポイント

ここでは、レビュー時に実際に見てほしい観点を、1つずつ丁寧に説明します。読みながら「これは自分のコードに当てはまるか?」と問いかけてみてください。

1.可読性(なぜ重要か・具体的に見る点)

可読性は他人が読んで理解できるかどうかです。読みやすいコードはバグが見つかりやすく、修正も楽になります。

具体的には以下を確認します。

  • 名前(変数・関数・クラス)が何を表しているか明確か
  • 1つの関数が1つの役割に絞られているか(単一責任原則)
  • コメントは「なぜそうしたのか」を説明しているか(やり方の説明ではなく意図の説明)

短い例で比べると、たとえば次のようになります。

悪い例(意図が不明瞭)

def proc(a,b):
    return a*b + 3

良い例(意図が明確)

def calculate_total_price(quantity: int, unit_price: float) -> float:
    """数量と単価から合計金額を計算(税や手数料は別処理)"""
    return quantity * unit_price + 3

コメントやドキュメンテーション文字列(docstring)で「なぜ +3 しているのか」を補足するとより親切です。

2.保守性(将来の変更に強いか)

保守性とはあとから変更しやすいかどうかを示す言葉です。

プログラムは一度作ったら終わりではなく、要件変更・機能追加・バグ修正など、常に“変化”にさらされます。 そのときに「どこを直せばいいか」がすぐ分かるコードは、チームにとって非常に価値があります。

たとえば、次のようなコードは将来つらくなります。

悪い例(ハードコード・重複が多い)

def calc_price(product_type: str, quantity: int) -> int:
    if product_type == "apple":
        return quantity * 120
    elif product_type == "banana":
        return quantity * 80
    elif product_type == "grape":
        return quantity * 200

ここでは商品が増えるたびにif文を追加する必要があります。 将来的に「桃」や「梨」を追加するたびにコードを修正するのは明らかに非効率です。

改善例

PRODUCT_PRICES = {
    "apple": 120,
    "banana": 80,
    "grape": 200,
}

def calc_price(product_type: str, quantity: int) -> int:
    """商品ごとの単価を外部定義にし、変更に強くする"""
    unit_price = PRODUCT_PRICES.get(product_type)
    if unit_price is None:
        raise ValueError(f"未対応の商品: {product_type}")
    return quantity * unit_price

このように辞書や設定ファイルに単価を外出しすれば、コードの修正範囲を最小限にできます。もしデータがDBやAPIで管理されるようになっても、関数内の実装を変えるだけで済みます。

変更点が一箇所に集約されていることが保守性の高い設計の特徴です。

3.正確性と例外処理(エラーに強い設計)

「動く」だけではなく、異常時にどう動くかも考えなければなりません。 エラーを無視したコードは、一見問題なく動いているように見えて、実は裏でデータ破損や不正な状態を生み出していることがあります。

悪い例(例外を無視している)

def divide(a, b):
    return a / b

result = divide(10, 0)
print(result)  # ZeroDivisionErrorでクラッシュ

改善例(例外をキャッチして安全に処理)

def safe_divide(a: float, b: float) -> float:
    """ゼロ除算を防ぎ、安全に結果を返す"""
    try:
        return a / b
    except ZeroDivisionError:
        print("警告: 0で割り算が行われました。0を返します。")
        return 0.0

result = safe_divide(10, 0)
print(result)  # -> 0.0

このように例外をキャッチして、エラーメッセージを出したり、ログを残したりするだけで信頼性がぐっと上がります。 レビューでは「このコード、もしAPIが失敗したら?」「ファイルが存在しなかったら?」という観点で見るとよいです。

さらに実務ではloggingモジュールを活用することで、異常時の原因を追いやすくなります。

import logging

logger = logging.getLogger(__name__)

def load_data(file_path: str) -> list[str]:
    try:
        with open(file_path, encoding="utf-8") as f:
            return f.readlines()
    except FileNotFoundError:
        logger.error(f"ファイルが見つかりません: {file_path}")
        return []

このように例外を握りつぶさない設計が、保守性と信頼性を高めます。

4.パフォーマンス(過剰最適化に注意しつつ)

速度やメモリが重要な部分ではアルゴリズムの計算量を意識します。

ただし、読みやすさを著しく損なう過剰な最適化は避けるべきです。まずは正しく、次に必要であれば最適化しましょう。レビューでは「ここは頻出処理か?負荷は高いか?」を判断材料にします。

5.セキュリティ(意外と見落としがち)

入力値の検証、SQLインジェクション対策、シークレット情報の取り扱い(ハードコード禁止)、適切な権限チェックなど、セキュリティ観点は必須です。

簡単なミスが大きな事故につながるので、レビューでしっかり指摘します。

実例で学ぶ:悪いコード → 改善(詳細に解説)

次に、少し長めのサンプルを使って、悪い書き方から改善までを段階的に見ていきます。実務でよくあるパターンです。

ケース:CSVを読み込んで集計する処理(悪い例)

まずは悪い(読みにくい/危険な)コードを示します。

import csv

def load_and_sum(path):
    f = open(path)
    reader = csv.reader(f)
    total = 0
    for row in reader:
        total += int(row[2])
    return total

このコードの問題点を丁寧に整理します。

  1. ファイルを開いた後に必ず閉じていない(リソースリークの可能性)
  2. CSVの列の意味が不明(row[2]が何を示すか不明)
  3. 型変換時の例外処理(空行や数値でない値がある場合)をしていない
  4. エンコーディングやヘッダーの有無を考慮していない

改善案(丁寧な実装と説明)

改善は段階的に行うと学びやすいです。 まずは安全にファイルを扱い、意味のある名前を付け、エラー処理を加えます。

import csv
from typing import Optional

def sum_column_from_csv(file_path: str, column_name: str) -> int:
    """
    CSVファイルから指定した列の合計値を返す。
    - file_path: CSVファイルのパス
    - column_name: ヘッダー名(例: 'quantity')
    """
    total = 0
    with open(file_path, newline='', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        if column_name not in reader.fieldnames:
            raise ValueError(f"指定された列が見つかりません: {column_name}")
        for row in reader:
            try:
                val = int(row[column_name])
            except (ValueError, TypeError):
                # 数値でない行はスキップする。ここは要件次第で例外にすることも。
                continue
            total += val
    return total

ポイントを整理します。with を使うことでファイルが自動的に閉じられます。csv.DictReader を使うと列名で参照でき、row[2]のようなインデックス参照より明確です。エラーはスローするかログに残すかは要件に依存しますが、何もしないよりははるかに安全です。

実務でよく見る「微妙なコード」パターンと改善例

ここでは「一見動くけどやや微妙、将来バグを生む可能性が高い」パターンを挙げ、改善方法を示します。レビューでよく取り上げる箇所です。

以下は、複雑なネストです。悪い例のサンプルコードです。

if cond_a:
    if cond_b:
        if cond_c:
            do_something()

改善するためには、ガード節で早期リターンします。

if not cond_a:
    return
if not cond_b:
    return
if not cond_c:
    return
do_something()

このようにするとロジックが平坦化し、読みやすくなります。

型ヒントと静的解析の効果的な使い方

Pythonは動的型付けですが、型ヒント(type hints)を使うことで読み手に意図を伝え、静的解析ツール(mypyなど)による検査も可能になります。大規模プロジェクトでは型ヒントがあると保守性が格段に上がります。

from typing import List

def average(values: List[float]) -> float:
    if not values:
        raise ValueError("values must not be empty")
    return sum(values) / len(values)

型ヒントの利点は次のとおりです。IDEが補完を効かせやすくなる、APIのインターフェースが明文化される、静的解析でバグの予防ができる、などです。

単体テストとレビューの関係 — テストがあるとレビューが速くなる

コードを出すときにテストが付いていると、レビュワーは安心して変更を追えます。 テストは「この関数はこういう入出力になるはず」という仕様の明文化でもあります。簡単なpytestの例を示します。

# sample_module.py
def add(a: int, b: int) -> int:
    return a + b

# test_sample_module.py
def test_add():
    assert add(1, 2) == 3

PRにテストが含まれていれば、レビュワーはそのテストを読んで意図を理解しやすく、必要であれば追加のテストを提案できます。

実際のPRコメント例

レビューコメントは丁寧な言葉遣いと、改善案の提示が大切です。ここでは、実際に私がレビューする際の文章をご紹介します

  • 「この関数名だと何をするか直感的に伝わりにくいので、calculate_total_price のように変更してはどうでしょうか?」
  • 「ここは例外が発生する可能性があるので、try/exceptでハンドリングしたいです。設計上はどう扱う想定ですか?」
  • 「コメントがあると助かります。特に +3 の理由を書いてもらえると後から見返すときにありがたいです。」

相互に建設的にやりとりすることで、コードの質が上がり、チームの文化も良くなります。

レビューを速く・質を上げるためのワークフロー(実務ノウハウ)

私の経験上、以下のルールをチームで共有するとレビューがスムーズになります。導入前に「なぜそのルールが必要か」を議論して合意しておくと定着しやすいです。

まず、PRは小さく(変更点は可能な限り小さくまとめる)出す。大きなPRはレビュワーの負担が大きく、見落としが発生しやすいからです。次に、自動チェック(CI)で最低限の静的解析やテストを通す仕組みを作る。自動で弾ける不具合はレビュー対象から外せます。最後に、レビュー基準をドキュメント化しておく(命名規則、テストカバレッジ基準、パフォーマンスの目安など)と効果的です。

まとめ

コードレビューは、単なるミス探しではなく「より良いコードを一緒に育てる時間」です。誰かの目が入ることで、コードの質だけでなくチーム全体の理解と成長も深まります。
完璧を求めるより、まずは“読まれることを意識したコード”を書くことが第一歩です。

レビューを重ねるうちに、自然と設計力・表現力・協働力が身につきます。小さな改善の積み重ねが、プロダクトとチームを確実に強くします!

ここまでお読みいただきありがとうございました!

Pythonの基礎から応用まで学べる
Python WebAcademy

Python WebAcademyでは、Pythonの基礎からアーキテクチャなどの応用的な内容まで幅広く学べます。
また、ブラウザ上で直接Pythonコードを試すことができ、実践的なスキルを身につけることが可能です。

Pythonの学習を始める