1. 深いネスト

iffor が何重にもネストすると、コードのインデントが右に伸びて読みにくくなる。目安はネスト 3 層まで。超えたら Early Return(ガード節)や関数分割を検討する。

❌ NG
Python — 4 層ネスト
def process_order(order):
    if order:
        if order.is_active:
            if order.items:
                for item in order.items:
                    if item.in_stock:
                        ship(item)  # 本来やりたい処理がここにある
✅ OK
Python — ガード節(Early Return)でネストを浅くする
def process_order(order):
    if not order:           return   # ガード節①
    if not order.is_active: return   # ガード節②
    if not order.items:     return   # ガード節③

    for item in order.items:
        if item.in_stock:
            ship(item)   # ネスト 2 層で済む

2. 長大な関数

関数が 100 行を超え始めると単体テストが書きにくくなり、デバッグも困難になる。関数は 1 つのことだけを行う(単一責任)という原則を守り、20〜30 行以内を目安にする。

💡 分割のヒント

関数内に「バリデーション」「データ変換」「永続化」のフェーズがあったら、それぞれを別の関数に切り出せる。コメントで「# ここから〇〇処理」と書いているなら、その区切りが関数の境界だ。

✅ OK
Python — フェーズごとに関数を分割する
def register_user(data):
    validated = _validate_user_data(data)   # 検証
    hashed    = _hash_password(validated)   # 変換
    user      = _save_user(hashed)          # 保存
    _send_welcome_email(user)               # 通知
    return user

def _validate_user_data(data): ...   # 各フェーズは単独でテスト可能
def _hash_password(data): ...
def _save_user(data): ...
def _send_welcome_email(user): ...

3. DRY 原則の違反(コードの重複)

同じロジックを複数箇所にコピー & ペーストすると、仕様変更時に「直し忘れ」が生じる。DRY (Don't Repeat Yourself) — 同じことは一箇所でのみ書く。

❌ NG
Python — メールバリデーションが 2 箇所に重複
class RegistrationService:
    def register(self, email):
        if not email or "@" not in email:   # バリデーションをコピペ
            raise ValueError("Invalid email")
        ...

class ProfileService:
    def update_email(self, email):
        if not email or "@" not in email:   # 同じロジックをコピペ
            raise ValueError("Invalid email")
        ...
✅ OK
Python — バリデーションを一箇所に集約する
def validate_email(email: str) -> None:
    """メールアドレスの基本バリデーション。不正な場合は ValueError を送出。"""
    if not email or "@" not in email:
        raise ValueError(f"Invalid email address: {email!r}")

class RegistrationService:
    def register(self, email): validate_email(email); ...

class ProfileService:
    def update_email(self, email): validate_email(email); ...

4. コメントのアンチパターン

アンチパターン問題点改善策
コードをそのまま訳すコメント
# i を 1 増やす
i += 1
何も情報を追加していない コメントを削除するか、なぜが必要なら書く
嘘のコメント(コードと乖離)
# ユーザーを削除する
→ 実際は論理削除
コードより誤ったコメントを信じてバグを生む コードを変更したらコメントも更新する
コメントアウトした死んだコード 「なぜ残しているか」が不明で混乱を招く Git の履歴に任せて削除する
メソッド名で伝わる内容をコメントに書く 冗長で保守負担が増える 名前を改善して、コメントなしでも読めるようにする

良いコメントとは

なぜこの設計にしたか」「なぜこの特殊ケースが必要か」を説明するコメント。コードを読んでも分からない 意図・背景・制約 を補足するために書く。

5. 空の catch / 例外の握り潰し

except: pass や空の catch ブロックは、エラーが起きても完全に無視してしまう。問題が起きたことを誰も知れず、その後のデータが壊れた状態で処理が続く。

❌ NG
Python — 例外を握り潰す
def save_user(user):
    try:
        db.save(user)
    except:
        pass  # エラーが起きても無視。ユーザーは「保存できた」と思っている
✅ OK
Python — 例外を適切に処理し、ログに残す
import logging
logger = logging.getLogger(__name__)

def save_user(user):
    try:
        db.save(user)
    except DatabaseError as e:
        logger.error("Failed to save user id=%s: %s", user.id, e, exc_info=True)
        raise  # または変換して上位に再送出

6. 例外の乱用(制御フロー代わりの例外)

例外は「予期しない異常事態」のために設計されている。通常の制御フロー(存在確認・条件分岐)に例外を使うと、パフォーマンスが低下し、コードの流れも分かりにくくなる。

❌ NG
Python — 例外で存在確認を行う
def get_user_email(user_id):
    try:
        user = User.objects.get(id=user_id)
        return user.email
    except User.DoesNotExist:
        return None   # 「存在しない」は例外でなく通常ケース
✅ OK
Python — 存在確認は通常の条件分岐で行う
def get_user_email(user_id):
    user = User.objects.filter(id=user_id).first()
    return user.email if user else None

7. 例外の種類を細分化しない

ExceptionRuntimeError など広すぎる型でキャッチすると、意図しない例外まで飲み込んでしまう。例外はなるべく具体的な型でキャッチし、上位に再送出するか適切なエラー型に変換する。

❌ NG
Python — 広すぎる Exception キャッチ
try:
    result = process_payment(order)
except Exception as e:
    print(f"Error: {e}")   # KeyboardInterrupt も吸収してしまう場合がある
✅ OK
Python — 具体的な型でキャッチし、想定外は再送出する
try:
    result = process_payment(order)
except PaymentDeclinedError as e:
    logger.warning("Payment declined for order %s: %s", order.id, e)
    return {"status": "declined", "reason": str(e)}
except NetworkError as e:
    logger.error("Network error during payment: %s", e, exc_info=True)
    raise PaymentServiceUnavailableError("一時的にご利用いただけません") from e
# その他の例外は意図的にキャッチしない → 呼び出し元に伝播

⚠️ カスタム例外クラスを活用する

PaymentDeclinedErrorInventoryShortageError のようにドメイン固有の例外クラスを定義すると、エラーの種類を型で表現でき、上位のハンドラが判断しやすくなる。