2 ポイント 投稿者 GN⁺ 2025-03-12 | 1件のコメント | WhatsAppで共有
  • 最近コードベースをレビューしていて、コードの品質自体は高いにもかかわらず精神的に疲れるという体験をした
    • これはコードの複雑性というより、可読性に関係していた
  • コードの可読性を高めるための8つのパターンを導き出した

コード可読性メトリクスと代替的な複雑性メトリクス

  • コード可読性を測定する普遍的で広く使われているメトリクスは存在しない
  • 実際には使われていない学術論文や個人的な意見があるだけ
  • 新しいメトリクスを作るより、誰でも簡単に議論できる視覚的パターンに注目
  • 複雑性メトリクスで重要な条件:
    • ソースコードのスニペットや個別の関数で機能すること
    • アルゴリズム複雑性ではなくコードの書き方に焦点を当てること
    • スタイル要素(変数名、空白、インデントなど)には焦点を当てないこと

Halstead複雑性メトリクス

  • 1970年代にMaurice Halsteadが開発したコード複雑性メトリクス
  • 言語やプラットフォームに関係なくコードの書き方を数値化できる
  • 演算子と被演算子の数に基づいて、プログラムの長さ、ボリューム、難易度を計算
  • 主な測定値:
    • 固有演算子数 (n1)
    • 固有被演算子数 (n2)
    • 総演算子数 (N1)
    • 総被演算子数 (N2)
  • より多くの演算子と被演算子が使われるほどコードの複雑性は増す
  • すべての言語で演算子および被演算子の定義が明確とは限らないため、一貫したツールの使用が重要

Halstead複雑性から得られるインサイト

  • 短く、変数の少ない関数は可読性が高い
  • 言語固有の演算子やシンタックスシュガーの使用は最小限にする
  • 関数型プログラミングのチェーン利用(map/reduce/filter など)は、長くなりすぎると可読性が落ちる

認知的複雑性(Cognitive Complexity)

  • SonarSourceが開発した複雑性メトリクス
  • コードの読みづらさをより正確に測定しようとする試み
  • 3つの主要原則:
    1. 短縮構文(shorthand constructs)は読みやすさを高める
    2. 非線形なフローの分断は難易度を高める
    3. ネストした制御フローは難易度を高める

認知的複雑性から得られるインサイト

  • 短縮構文は簡潔だが、潜在的なバグのリスクがある
  • 条件文および論理演算子は使いすぎると可読性が落ちる
  • 例外処理はコード複雑性を高める主な原因
  • gotoは一般には避けるべきだが、特定の状況では有用なことがある
  • ネストした制御構造は可能な限り減らすのがよい

関数の形、パターン、変数

  • 関数の視覚的な「形」はコード可読性に重要な役割を果たす
  • 可読性を高める3つの原則:
    1. 明確で具体的な変数名を使う
    • 変数のシャドーイングを避ける
    • 視覚的に区別しやすい名前を使う(ijのような似た名前は避ける)
    1. 変数の生存期間(liveness)を短くする
    • 変数のスコープは短いほどよい
    • 関数の境界を越えて長く維持される変数は複雑性を高める
    1. 見慣れたコードパターンを再利用する
    • 一貫したコードパターンを保つと可読性が向上する
    • 新しいアプローチより、既存で見慣れたパターンを優先して使う

コード可読性を向上させる8つのパターン

  1. 行数/演算子/被演算子の数を減らす – 小さな関数と少ない変数の使用が可読性を高める
  2. 新しいアプローチを避ける – コードベース内で見慣れたパターンを維持する
  3. グループ化 – 長い関数チェーンやイテレータなどは補助関数に分離する
  4. 条件文を単純化する – 条件文は短く保ち、論理演算子の混在を最小限にする
  5. gotoを最小限にする – 必要ならエラー処理でのみ限定的に使う
  6. ネストを最小化する – ネストしたロジックは減らし、必要なら関数に分離する
  7. 明確な変数名を使う – 具体的で重複しない変数名を使う
  8. 変数の生存期間を短くする – 関数内で短く保ち、関数の境界を越えないようにする

結論

  • コード可読性はコード品質の重要な要素
  • HalsteadおよびCognitive Complexityは、可読性の問題を数値化し改善の方向性を示せる
  • 簡潔で明確なコードを書くことで、コード保守が容易になりバグ発生の可能性が減る
  • 最適なコード作成とは、単純さ、一貫性、明確さを優先すること

1件のコメント

 
GN⁺ 2025-03-12
Hacker Newsの意見
  • mapreducefilter のような関数型プログラミングの構造をつなげるのは簡潔だが、長いチェーンは可読性を損なう傾向がある

    • これは記事が示唆していることではない
    • 慣れていないから悪いと考える、よくある不満のように感じる
    • 少し慣れれば、ほかの方法より読み書きしやすい
    • 関数型プログラミングの基本を学ぶことが重要だ
    • Monad を説明する必要はないが、少なくとも mapfilter をやみくもに批判しない程度には慣れるべきだ
  • 良いコードの重要な側面は質的であり、文学的でもある

    • 数学的な考え方をするプログラマーや学者にとっては居心地が悪いかもしれない
    • ドストエフスキーとウッドハウスは好きだが、彼らの文章は大きく異なる
    • コードベースのスタイルを理解するには時間がかかる
  • コードを読むときに最も疲れる問題は可変性だ

    • 変数を一度だけ「固定」できる能力は大きな贈り物だ
    • メソッドを理解する過程は、0% から 100% へ単調に増加していくべきだ
    • GOTO が有害な理由は、可変変数の状態を把握しにくいからだ
  • 小さな関数と少ない変数は、一般に読みやすい

    • 「可読性」への焦点がミクロな可読性に偏っている
    • そのせいでコードが過度に分断される
    • APL 系の言語はその反対の極端にある
  • TypeScript はコードを読みにくくする

    • データモデルが「原子的」に保たれていれば問題ない
    • 型推論に依存すると、フィールドを元の位置までたどるのが難しい
  • getOddness4 関数は非対称性を与える

    • getOddness2 関数は対称的な選択肢を提供する
  • 記事は興味深いが、満足できるものではない

    • 言語固有の演算子や糖衣構文の使用を避けるべきだという意見には同意しない
    • mapreducefilter のような構造は、うまく使えばほかの演算子を置き換え、「量」を減らせる
  • 可読性を定義しようとする試みは称賛に値する

    • 多くの人を対象にテストすれば、可読性の実際の次元を見つけられるはずだ
  • コードの複雑さは構文木の大きさで表せる

    • 局所的な複雑さの低減は、全体の複雑さに大きな影響を与えない
  • 長い関数チェーンやコールバックは、小さなグループに分けて、適切に命名された変数を使うのがよい

    • どちらのバージョンも効率の面では同じだ
    • 違いはコンパイラにある