1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

エンジニア2年目になり、コードレビューを受ける機会が増えてきました。レビューで指摘される内容も1年目の頃とは変わり、単純な文法エラーよりも「可読性」や「保守性」に関するコメントが多くなりました。そんな中で「リーダブルコード」を読み、普段Python・Djangoでの開発にTDDを取り入れている私にとって、多くの学びを得ることができました。

重要なところの要約

「理解するまでの時間」を最優先に

本書の核心は「コードは他の人が最短時間で理解できるように書かなければならない」という点です。つまり、短いコードよりも、理解しやすいコードの方が価値が高いということです。

表面上の改善(第1部)

命名の重要性が特に印象に残りました。tmpdataといった汎用的な名前を避け、「何を表しているか」を明確にする。

# 悪い例:汎用的すぎる名前
def get_data(request):
    data = User.objects.filter(status=1)
    tmp = []
    for d in data:
        tmp.append(d.name)
    return tmp

# 良い例:明確で具体的な名前
def get_active_user_names(request):
    active_users = User.objects.filter(is_active=True)
    user_names = []
    for user in active_users:
        user_names.append(user.name)
    return user_names

限界値にはmax_/min_、範囲にはfirst_/last_を使うなど、具体的な指針も実践的でした。

# 悪い例:曖昧な限界値
MAX_ITEMS = 10  # 10を含む?含まない?

# 良い例:明確な限界値
MAX_ITEMS_COUNT = 10  # 10個まで(10を含む)

コメントの使い分けについては、「コードから推測できることは書かない」「なぜそうしたかの理由を書く」という原則が参考になりました。

# 悪い例:コードを説明するだけ
# ユーザー一覧を取得
users = User.objects.all()

# 良い例:理由や背景を説明
# パフォーマンス向上のため、非アクティブユーザーは除外
# (過去のA/Bテストで応答時間が30%改善することを確認済み)
users = User.objects.filter(is_active=True)

ロジックの単純化(第2部)

早期リターンによるネスト削減は、すぐに実践できる技法でした。

# 悪い例:深いネスト
def create_post(request):
    if request.method == 'POST':
        if request.user.is_authenticated:
            if request.POST.get('title'):
                # メインの処理
                return create_post_logic(request)
            else:
                return JsonResponse({'error': 'タイトルが必要'})
        else:
            return JsonResponse({'error': 'ログインが必要'})
    else:
        return JsonResponse({'error': 'POSTのみ許可'})

# 良い例:早期リターンでネストを削減
def create_post(request):
    if request.method != 'POST':
        return JsonResponse({'error': 'POSTのみ許可'})

    if not request.user.is_authenticated:
        return JsonResponse({'error': 'ログインが必要'})

    if not request.POST.get('title'):
        return JsonResponse({'error': 'タイトルが必要'})

    # メインの処理(ネストなし)
    return create_post_logic(request)

巨大な式の分割で紹介された「説明変数」「要約変数」の概念も有用でした。

# 悪い例:複雑な条件式
if request.user.is_staff and request.user.profile.department == 'IT' and request.user.profile.years_of_service > 3:
    # 処理

# 良い例:説明変数で分割
is_staff_member = request.user.is_staff
is_it_department = request.user.profile.department == 'IT'
has_sufficient_experience = request.user.profile.years_of_service > 3
can_access_admin_feature = is_staff_member and is_it_department and has_sufficient_experience

if can_access_admin_feature:
    # 処理

コードの再編成(第3部)

  • **「一度に1つのことを」**という原則は、関数設計の指針として非常に有用でした。
# 悪い例:複数のことを一度に行う
def process_user_data(user_id):
    # ユーザー取得
    user = User.objects.get(id=user_id)

    # データ変換
    user_data = {
        'name': user.first_name + ' ' + user.last_name,
        'age': calculate_age(user.birth_date)
    }

    # ログ出力
    logger.info(f"User {user_id} processed")

    # メール送信
    send_notification_email(user.email, user_data)

    return user_data

# 良い例:責任を分割
def get_user_by_id(user_id):
    return User.objects.get(id=user_id)

def format_user_data(user):
    return {
        'name': f"{user.first_name} {user.last_name}",
        'age': calculate_age(user.birth_date)
    }

def log_user_processing(user_id):
    logger.info(f"User {user_id} processed")

def notify_user_processing(user_email, user_data):
    send_notification_email(user_email, user_data)

def process_user_data(user_id):
    user = get_user_by_id(user_id)
    user_data = format_user_data(user)
    log_user_processing(user_id)
    notify_user_processing(user.email, user_data)
    return user_data

本書で得た気付き

1. コードレビューでの指摘が理解できるように

これまでレビューで「もう少し読みやすくできませんか?」と言われても具体的にどうすべきかわからないことがありました。しかし、本書の原則を知ることで、レビュアーの意図が理解できるようになりました。

例えば、「この条件分岐は複雑すぎる」という指摘は、8章の「巨大な式を分割する」の話だったのだと今なら理解できます。

2. TDDとの相性の良さ

TDDで開発していると、テストコードも含めて読みやすさが重要だと実感しています。テストメソッド名も「何をテストしているか」が一目でわかるよう命名することで、テストの保守性が向上しました。

# 改善前
def test_user_validation(self):
    pass

# 改善後
def test_should_reject_user_registration_with_invalid_email(self):
    pass

4. 「6ヶ月後の自分」も他人という視点

特に響いたのは**「6ヶ月後の自分も他人」**という考え方です。実際に過去のコードを見返すと、当時は理解していたつもりでも、今見ると意図がわからないコードが多数ありました。この経験により、未来の自分のためにもコードを書くという意識が芽生えました。

今後のコーディングスタイル

1. 命名規則の徹底

  • 変数名・関数名は処理内容を具体的に表現する
  • ブール値にはis_has_can_を積極的に使用
  • 単位がある値には単位を付加(timeout_secmax_retry_countなど)
# 改善予定のスタイル
def fetch_active_users_with_recent_login(days_threshold=30):
    cutoff_date = timezone.now() - timedelta(days=days_threshold)
    return User.objects.filter(
        is_active=True,
        last_login__gte=cutoff_date
    )

2. 関数設計の原則

  • 1つの関数は1つのことだけを行う
  • 引数は3つ以下を目指す
  • 早期リターンを活用してネストを浅くする
  • 副作用のない純粋関数を心がける

3. コメント戦略

  • WHATではなくWHYを書く
  • TODOコメントには期限や担当者を明記
  • 複雑なビジネスロジックには要約コメントを付ける
# 改善予定のコメントスタイル
def calculate_discount_rate(user, product):
    # ゴールド会員は特別価格適用のため、
    # 通常の割引計算とは異なるロジックを使用
    if user.membership_level == 'GOLD':
        return calculate_gold_member_discount(user, product)

    # TODO(2024-07-01): プレミアム会員制度導入時に追加予定
    return calculate_standard_discount(product)

4. コードレビューでの活用

  • 自分のコードをcommitする前にセルフレビューする際の基準として使用
  • レビューコメントを理解するための共通言語として活用
  • リファクタリングの指針として参照

5. 継続的な改善

  • 月1回、過去に書いたコードをリーダブルコードの観点で見直す
  • 新しい技術や設計パターンを学ぶ際も、可読性を重視した選択をする

まとめ

「リーダブルコード」は、技術的なスキルだけでなく、「他者への思いやり」の重要性を教えてくれました。コードは自分だけのものではなく、チームメンバーや未来の自分のためのコミュニケーションツールです。

2年目という成長段階で本書と出会えたことで、悪い癖が固まる前に正しい方向性を学ぶことができました。まだまだ完璧に実践できているわけではありませんが、意識することから始めて、日々のコーディングで少しずつ改善していきたいと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?